From 3fa4eb33614e569c8278497dcee83e68cdb40e23 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 5 Jan 2021 11:36:59 +0100 Subject: [PATCH] Fix logging to use an inheritable thread local, fixes #295 --- .../org/mvndaemon/mvnd/daemon/Server.java | 3 +- .../mvnd/interactivity/DaemonPrompter.java | 5 +- .../mvnd/logging/internal/Slf4jLogger.java | 7 +- .../smart/LoggingExecutionListener.java | 3 +- .../logging/smart/LoggingOutputStream.java | 1 + .../smart/ProjectBuildLogAppender.java | 28 ++++- .../org/mvndaemon/mvnd/it/ExecOutputTest.java | 44 ++++++++ .../projects/exec-output/.mvn/maven.config | 3 + .../src/test/projects/exec-output/pom.xml | 103 ++++++++++++++++++ .../org/mvndaemon/mvnd/test/Greeting.java | 24 ++++ 10 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 integration-tests/src/test/java/org/mvndaemon/mvnd/it/ExecOutputTest.java create mode 100644 integration-tests/src/test/projects/exec-output/.mvn/maven.config create mode 100644 integration-tests/src/test/projects/exec-output/pom.xml create mode 100644 integration-tests/src/test/projects/exec-output/src/main/java/org/mvndaemon/mvnd/test/Greeting.java diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java index fdef83da2..6f548ea75 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java @@ -64,7 +64,6 @@ import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; import sun.misc.Signal; import sun.misc.SignalHandler; @@ -547,7 +546,7 @@ public T request(Message request, Class responseType, Pre loggingSpy.fail(t); } finally { sender.join(); - MDC.remove(ProjectBuildLogAppender.KEY_PROJECT_ID); + ProjectBuildLogAppender.setProjectId(null); } } catch (Throwable t) { LOGGER.error("Error while building project", t); diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/interactivity/DaemonPrompter.java b/daemon/src/main/java/org/mvndaemon/mvnd/interactivity/DaemonPrompter.java index 263454332..b65f8766d 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/interactivity/DaemonPrompter.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/interactivity/DaemonPrompter.java @@ -34,7 +34,6 @@ import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; @Named @Priority(10) @@ -147,7 +146,7 @@ private String formatMessage(String message, List possibleValues, String private void doDisplay(String message) throws IOException { try { Connection con = Objects.requireNonNull(Connection.getCurrent()); - String projectId = MDC.get(ProjectBuildLogAppender.KEY_PROJECT_ID); + String projectId = ProjectBuildLogAppender.getProjectId(); Message.ProjectEvent msg = Message.display(projectId, message); LOGGER.info("Sending display request: " + msg); con.dispatch(msg); @@ -159,7 +158,7 @@ private void doDisplay(String message) throws IOException { private String doPrompt(String message, boolean password) throws IOException { try { Connection con = Objects.requireNonNull(Connection.getCurrent()); - String projectId = MDC.get(ProjectBuildLogAppender.KEY_PROJECT_ID); + String projectId = ProjectBuildLogAppender.getProjectId(); String uid = UUID.randomUUID().toString(); Message.Prompt msg = new Message.Prompt(projectId, uid, message, password); LOGGER.info("Requesting prompt: " + msg); diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/logging/internal/Slf4jLogger.java b/daemon/src/main/java/org/mvndaemon/mvnd/logging/internal/Slf4jLogger.java index faec4cf46..234ec25e7 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/logging/internal/Slf4jLogger.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/logging/internal/Slf4jLogger.java @@ -20,7 +20,6 @@ import org.codehaus.plexus.logging.Logger; import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender; -import org.slf4j.MDC; /** * Adapt an SLF4J logger to a Plexus logger, ignoring Plexus logger API parts that are not classical and @@ -42,7 +41,7 @@ public class Slf4jLogger public Slf4jLogger(org.slf4j.Logger logger) { this.logger = logger; - this.projectId = MDC.get(ProjectBuildLogAppender.KEY_PROJECT_ID); + this.projectId = ProjectBuildLogAppender.getProjectId(); } public void debug(String message) { @@ -140,8 +139,8 @@ public String getName() { } private void setMdc() { - if (projectId != null && MDC.get(ProjectBuildLogAppender.KEY_PROJECT_ID) == null) { - MDC.put(ProjectBuildLogAppender.KEY_PROJECT_ID, projectId); + if (projectId != null && ProjectBuildLogAppender.getProjectId() == null) { + ProjectBuildLogAppender.setProjectId(projectId); } } diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingExecutionListener.java b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingExecutionListener.java index 66eb8063d..08a903bd3 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingExecutionListener.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingExecutionListener.java @@ -17,7 +17,6 @@ import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.ExecutionListener; -import org.slf4j.MDC; public class LoggingExecutionListener implements ExecutionListener { @@ -141,7 +140,7 @@ public void forkedProjectFailed(ExecutionEvent event) { private void setMdc(ExecutionEvent event) { if (event.getProject() != null) { - MDC.put(ProjectBuildLogAppender.KEY_PROJECT_ID, event.getProject().getArtifactId()); + ProjectBuildLogAppender.setProjectId(event.getProject().getArtifactId()); } } } diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingOutputStream.java b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingOutputStream.java index 08a334dfd..e895c2600 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingOutputStream.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/LoggingOutputStream.java @@ -47,6 +47,7 @@ public void write(int b) throws IOException { super.write(b); if (buf.isEol()) { String line = new String(buf.toByteArray(), 0, buf.size() - LINE_SEP.length); + ProjectBuildLogAppender.updateMdc(); consumer.accept(line); buf.reset(); } diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/ProjectBuildLogAppender.java b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/ProjectBuildLogAppender.java index 76a79e650..3d9267762 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/ProjectBuildLogAppender.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/logging/smart/ProjectBuildLogAppender.java @@ -23,13 +23,39 @@ import java.util.Map; import org.apache.maven.shared.utils.logging.LoggerLevelRenderer; import org.apache.maven.shared.utils.logging.MessageUtils; +import org.slf4j.MDC; /** * This Maven-specific appender outputs project build log messages * to the smart logging system. */ public class ProjectBuildLogAppender extends AppenderBase { - public static final String KEY_PROJECT_ID = "maven.project.id"; + + private static final String KEY_PROJECT_ID = "maven.project.id"; + private static final ThreadLocal PROJECT_ID = new InheritableThreadLocal<>(); + + public static String getProjectId() { + return PROJECT_ID.get(); + } + + public static void setProjectId(String projectId) { + if (projectId != null) { + PROJECT_ID.set(projectId); + MDC.put(KEY_PROJECT_ID, projectId); + } else { + PROJECT_ID.remove(); + MDC.remove(KEY_PROJECT_ID); + } + } + + public static void updateMdc() { + String id = getProjectId(); + if (id != null) { + MDC.put(KEY_PROJECT_ID, id); + } else { + MDC.remove(KEY_PROJECT_ID); + } + } private String pattern; private PatternLayout layout; diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/ExecOutputTest.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/ExecOutputTest.java new file mode 100644 index 000000000..4c3a6b2c1 --- /dev/null +++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/ExecOutputTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mvndaemon.mvnd.it; + +import java.io.IOException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.mvndaemon.mvnd.assertj.TestClientOutput; +import org.mvndaemon.mvnd.client.Client; +import org.mvndaemon.mvnd.client.DaemonParameters; +import org.mvndaemon.mvnd.junit.MvndTest; + +@MvndTest(projectDir = "src/test/projects/exec-output") +public class ExecOutputTest { + + @Inject + Client client; + + @Inject + DaemonParameters parameters; + + @Test + void cleanInstall() throws IOException, InterruptedException { + + final TestClientOutput output = new TestClientOutput(); + client.execute(output, "clean", "verify", "-e", "-Dmvnd.log.level=DEBUG").assertSuccess(); + output.messagesToString() + .contains("ProjectLogMessage{projectId='exec-output', message='[INFO] [stdout] Hello world!'}"); + + } +} diff --git a/integration-tests/src/test/projects/exec-output/.mvn/maven.config b/integration-tests/src/test/projects/exec-output/.mvn/maven.config new file mode 100644 index 000000000..4230c2417 --- /dev/null +++ b/integration-tests/src/test/projects/exec-output/.mvn/maven.config @@ -0,0 +1,3 @@ +-Dmaven.wagon.httpconnectionManager.ttlSeconds=120 +-Dmaven.wagon.http.retryHandler.requestSentEnabled=true +-Dmaven.wagon.http.retryHandler.count=10 diff --git a/integration-tests/src/test/projects/exec-output/pom.xml b/integration-tests/src/test/projects/exec-output/pom.xml new file mode 100644 index 000000000..c59ee5693 --- /dev/null +++ b/integration-tests/src/test/projects/exec-output/pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + org.mvndaemon.mvnd.test.exec-output + exec-output + 0.0.1-SNAPSHOT + jar + + + UTF-8 + 1.8 + 1.8 + + 2.5 + 3.8.0 + 2.4 + 2.6 + 2.22.2 + 3.0.0 + + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run + package + + exec + + + false + java + true + + -classpath + + org.mvndaemon.mvnd.test.Greeting + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/src/test/projects/exec-output/src/main/java/org/mvndaemon/mvnd/test/Greeting.java b/integration-tests/src/test/projects/exec-output/src/main/java/org/mvndaemon/mvnd/test/Greeting.java new file mode 100644 index 000000000..c5c986b19 --- /dev/null +++ b/integration-tests/src/test/projects/exec-output/src/main/java/org/mvndaemon/mvnd/test/Greeting.java @@ -0,0 +1,24 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mvndaemon.mvnd.test; + +public class Greeting { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } + +}