From 7780923f504618c9111a6b7906aad98a8afb13d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Mon, 11 Dec 2023 08:39:42 +0100 Subject: [PATCH] Filter duplicate resolver messages Currently a warning of the resolver might be printed multiple times to the log because we call the resolver for each registered environment. This adds a duplication filtering on the log message that each message is only printed once per project. --- .../tycho/core/shared/MavenLogger.java | 26 +++-- .../configuration/DefaultMavenContext.java | 17 ++- .../configuration/FilteringMavenLogger.java | 109 ++++++++++++++++++ .../ProjectorResolutionStrategy.java | 4 +- 4 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/FilteringMavenLogger.java diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/shared/MavenLogger.java b/tycho-core/src/main/java/org/eclipse/tycho/core/shared/MavenLogger.java index a5846de663..77f935e5ff 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/shared/MavenLogger.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/shared/MavenLogger.java @@ -19,23 +19,31 @@ */ public interface MavenLogger { - public void error(String message); + default void error(String message) { + error(message, null); + } - public void error(String message, Throwable cause); + default void warn(String message) { + warn(message, null); + } - public void warn(String message); + default void debug(String message) { + debug(message, null); + } - public void warn(String message, Throwable cause); + void error(String message, Throwable cause); - public void info(String message); + void warn(String message, Throwable cause); - public void debug(String message); + void info(String message); - public void debug(String message, Throwable cause); + void debug(String message, Throwable cause); - public boolean isDebugEnabled(); + boolean isDebugEnabled(); - public boolean isExtendedDebugEnabled(); + default boolean isExtendedDebugEnabled() { + return false; + } T adapt(Class adapt); diff --git a/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/DefaultMavenContext.java b/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/DefaultMavenContext.java index 3a4153e7ba..33b0462ca0 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/DefaultMavenContext.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/DefaultMavenContext.java @@ -34,13 +34,11 @@ import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; -import org.codehaus.plexus.logging.Logger; import org.eclipse.tycho.MavenRepositoryLocation; import org.eclipse.tycho.ReactorProject; import org.eclipse.tycho.core.osgitools.DefaultReactorProject; import org.eclipse.tycho.core.shared.MavenContext; import org.eclipse.tycho.core.shared.MavenLogger; -import org.eclipse.tycho.osgi.adapters.MavenLoggerAdapter; import org.eclipse.tycho.p2maven.repository.P2ArtifactRepositoryLayout; @Component(role = MavenContext.class) @@ -50,8 +48,9 @@ public class DefaultMavenContext implements MavenContext { ArtifactHandlerManager artifactHandlerManager; @Requirement LegacySupport legacySupport; - @Requirement - Logger logger; + + @Requirement(hint = FilteringMavenLogger.HINT) + MavenLogger mavenLogger; private Properties globalProps; private List repositoryLocations; @@ -121,8 +120,8 @@ public File getLocalRepositoryRoot() { } @Override - public MavenLogger getLogger() { - return new MavenLoggerAdapter(logger, false); + public synchronized MavenLogger getLogger() { + return mavenLogger; } @Override @@ -153,15 +152,15 @@ public Collection getProjects() { private Optional getSession() { if (legacySupport == null) { - logger.warn("Legacy support not available"); + mavenLogger.warn("Legacy support not available"); return Optional.empty(); } MavenSession session = legacySupport.getSession(); if (session == null) { - if (logger.isDebugEnabled()) { + if (mavenLogger.isDebugEnabled()) { Thread.dumpStack(); } - logger.warn("Not called from a maven thread"); + mavenLogger.warn("Not called from a maven thread"); return Optional.empty(); } return Optional.of(session); diff --git a/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/FilteringMavenLogger.java b/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/FilteringMavenLogger.java new file mode 100644 index 0000000000..3865e48f47 --- /dev/null +++ b/tycho-core/src/main/java/org/eclipse/tycho/osgi/configuration/FilteringMavenLogger.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.osgi.configuration; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; +import org.eclipse.tycho.core.shared.MavenLogger; + +/** + * A logger that filters duplicate messages from the output, each message is only printed once + */ +@Component(role = MavenLogger.class, hint = FilteringMavenLogger.HINT) +public class FilteringMavenLogger implements MavenLogger { + + static final String HINT = "filtering"; + + @Requirement + private LegacySupport legacySupport; + + @Requirement + private Logger logger; + + private Map> messageLogMap = new ConcurrentHashMap<>(); + + @Override + public void error(String message, Throwable cause) { + if (logger.isErrorEnabled() && mustLog(message, cause, 1)) { + logger.error(message, cause); + } + } + + private boolean mustLog(String message, Throwable cause, int type) { + if (legacySupport == null) { + return true; + } + MavenSession session = legacySupport.getSession(); + if (session == null) { + return true; + } + MavenProject project = session.getCurrentProject(); + if (project == null) { + return true; + } + LogKey logKey; + if (cause == null) { + logKey = new LogKey(message, "", type); + } else { + logKey = new LogKey(message, cause.toString(), type); + } + return messageLogMap.computeIfAbsent(project, p -> ConcurrentHashMap.newKeySet()).add(logKey); + } + + @Override + public void warn(String message, Throwable cause) { + if (logger.isWarnEnabled() && mustLog(message, cause, 2)) { + logger.warn(message, cause); + } + } + + @Override + public void info(String message) { + if (logger.isInfoEnabled() && mustLog(message, null, 3)) { + logger.info(message); + } + } + + @Override + public void debug(String message, Throwable cause) { + if (logger.isDebugEnabled() && mustLog(message, cause, 4)) { + logger.warn(message, cause); + } + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public T adapt(Class adapt) { + if (adapt == Logger.class) { + return adapt.cast(logger); + } + return null; + } + + private static final record LogKey(String msg, String t, int type) { + + } + +} diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/ProjectorResolutionStrategy.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/ProjectorResolutionStrategy.java index d48f4cfb34..b0a512759b 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/ProjectorResolutionStrategy.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/ProjectorResolutionStrategy.java @@ -89,13 +89,13 @@ public Collection resolve(Map properties, IPro if (s.getSeverity() == IStatus.ERROR) { Set explanation = getExplanation(projector); // suppress "Cannot complete the request. Generating details." // log all transitive requirements which cannot be satisfied; this doesn't print the dependency chain from the seed to the units with missing requirements, so this is less useful than the "explanation" - logger.debug(StatusTool.collectProblems(s)); + logger.debug(StatusTool.toLogMessage(s)); explainProblems(explanation, MavenLogger::error); throw new ResolverException(explanation.stream().map(Object::toString).collect(Collectors.joining("\n")), selectionContext.toString(), StatusTool.findException(s)); } if (s.getSeverity() == IStatus.WARNING) { - logger.warn(StatusTool.collectProblems(s)); + logger.warn(StatusTool.toLogMessage(s)); } Collection newState = projector.extractSolution();