Skip to content

Commit

Permalink
Filter duplicate resolver messages
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
laeubi committed Dec 11, 2023
1 parent 7cd82de commit 7780923
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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> T adapt(Class<T> adapt);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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<MavenRepositoryLocation> repositoryLocations;
Expand Down Expand Up @@ -121,8 +120,8 @@ public File getLocalRepositoryRoot() {
}

@Override
public MavenLogger getLogger() {
return new MavenLoggerAdapter(logger, false);
public synchronized MavenLogger getLogger() {
return mavenLogger;
}

@Override
Expand Down Expand Up @@ -153,15 +152,15 @@ public Collection<ReactorProject> getProjects() {

private Optional<MavenSession> 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<MavenProject, Set<LogKey>> 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> T adapt(Class<T> adapt) {
if (adapt == Logger.class) {
return adapt.cast(logger);
}
return null;
}

private static final record LogKey(String msg, String t, int type) {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ public Collection<IInstallableUnit> resolve(Map<String, String> properties, IPro
if (s.getSeverity() == IStatus.ERROR) {
Set<Explanation> 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<IInstallableUnit> newState = projector.extractSolution();

Expand Down

0 comments on commit 7780923

Please sign in to comment.