From 147e2efce7f2b605b0d368b3cf41aec1a43b63fd Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 11 Aug 2020 16:17:23 -0400 Subject: [PATCH] [JENKINS-63082] SLAVE_LOG_HANDLER should avoid synch logging (#4886) --- .../java/hudson/slaves/SlaveComputer.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 24cd1d52ac..653d80db98 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -102,6 +102,10 @@ import java.util.logging.SimpleFormatter; import java.util.stream.Stream; import static hudson.slaves.SlaveComputer.LogHolder.SLAVE_LOG_HANDLER; +import hudson.util.DaemonThreadFactory; +import hudson.util.NamingThreadFactory; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import org.jenkinsci.remoting.util.LoggingChannelListener; @@ -1025,15 +1029,18 @@ public class SlaveComputer extends Computer { public Void call() { SLAVE_LOG_HANDLER = new RingBufferLogHandler(ringBufferSize) { - ThreadLocal dummy = ThreadLocal.withInitial(() -> new SimpleFormatter()); + Formatter dummy = new SimpleFormatter(); // only ever accessed from one thread anyway + ExecutorService executor = new ScheduledThreadPoolExecutor(1, new NamingThreadFactory(new DaemonThreadFactory(), "SLAVE_LOG_HANDLER")); @Override - public /* not synchronized */ void publish(LogRecord record) { - // see LogRecord.writeObject for dangers of serializing non-String/null parameters - if (record.getMessage() != null && record.getParameters() != null && Stream.of(record.getParameters()).anyMatch(p -> p != null && !(p instanceof String))) { - record.setMessage(dummy.get().formatMessage(record)); - record.setParameters(null); - } + public synchronized void publish(LogRecord record) { super.publish(record); + executor.submit(() -> { // JENKINS-63082: unsafe to call methods of LogRecord here + // see LogRecord.writeObject for dangers of serializing non-String/null parameters + if (record.getMessage() != null && record.getParameters() != null && Stream.of(record.getParameters()).anyMatch(p -> p != null && !(p instanceof String))) { + record.setMessage(dummy.formatMessage(record)); + record.setParameters(null); + } + }); } };