From a52d665180a7b11dcd64673c3f08b719f6c9f908 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 31 Mar 2015 10:13:23 -0400 Subject: [PATCH] [JENKINS-27392] SimpleBuildWrapper.createLoggerDecorator --- .../jenkins/tasks/SimpleBuildWrapper.java | 18 +++++-- .../jenkins/tasks/SimpleBuildWrapperTest.java | 49 +++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java b/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java index d23a202f95..2e7bb05e81 100644 --- a/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java +++ b/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java @@ -28,6 +28,7 @@ import hudson.AbortException; import hudson.EnvVars; import hudson.FilePath; import hudson.Launcher; +import hudson.console.ConsoleLogFilter; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; @@ -177,12 +178,19 @@ public abstract class SimpleBuildWrapper extends BuildWrapper { } } + /** + * Allows this wrapper to decorate log output. + * @param build as is passed to {@link #setUp(Context, Run, FilePath, Launcher, TaskListener, EnvVars)} + * @return a filter which ignores its {@code build} parameter and is {@link Serializable}; or null (the default) + * @since 1.608 + */ + public @CheckForNull ConsoleLogFilter createLoggerDecorator(@Nonnull Run build) { + return null; + } + @Override public final OutputStream decorateLogger(AbstractBuild build, OutputStream logger) throws IOException, InterruptedException, Run.RunnerAbortedException { - // Doubtful this can be supported. - // Decorating a TaskListener would be more reasonable. - // But for an AbstractBuild this is called early in Run.execute, before setUp. - // And for other kinds of builds, it is unclear what this would even mean. - return logger; + ConsoleLogFilter filter = createLoggerDecorator(build); + return filter != null ? filter.decorateLogger(build, logger) : logger; } @Override public final Launcher decorateLauncher(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException, Run.RunnerAbortedException { diff --git a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java index f65cf065c5..09869387cc 100644 --- a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java +++ b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java @@ -28,7 +28,11 @@ import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; +import hudson.console.ConsoleLogFilter; +import hudson.console.LineTransformationOutputStream; +import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import hudson.model.BuildListener; import hudson.model.Computer; import hudson.model.Descriptor; import hudson.model.FreeStyleBuild; @@ -45,18 +49,26 @@ import hudson.tasks.BuildWrapperDescriptor; import hudson.tasks.Shell; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; import java.util.Collections; +import java.util.Locale; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Assume; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.CaptureEnvironmentBuilder; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.TestBuilder; import org.jvnet.hudson.test.TestExtension; public class SimpleBuildWrapperTest { + @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); @Rule public JenkinsRule r = new JenkinsRule(); @Rule public TemporaryFolder tmp = new TemporaryFolder(); @@ -162,7 +174,44 @@ public class SimpleBuildWrapperTest { return true; } } + } + @Issue("JENKINS-27392") + @Test public void loggerDecorator() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); + p.getBuildWrappersList().add(new WrapperWithLogger()); + p.getBuildersList().add(new TestBuilder() { + @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + listener.getLogger().println("sending a message"); + return true; + } + }); + r.assertLogContains("SENDING A MESSAGE", r.buildAndAssertSuccess(p)); + } + public static class WrapperWithLogger extends SimpleBuildWrapper { + @Override public void setUp(Context context, Run build, FilePath workspace, Launcher launcher, TaskListener listener, EnvVars initialEnvironment) throws IOException, InterruptedException {} + @Override public ConsoleLogFilter createLoggerDecorator(Run build) { + return new UpcaseFilter(); + } + private static class UpcaseFilter extends ConsoleLogFilter implements Serializable { + private static final long serialVersionUID = 1; + @SuppressWarnings("rawtypes") // inherited + @Override public OutputStream decorateLogger(AbstractBuild _ignore, final OutputStream logger) throws IOException, InterruptedException { + return new LineTransformationOutputStream() { + @Override protected void eol(byte[] b, int len) throws IOException { + logger.write(new String(b, 0, len).toUpperCase(Locale.ROOT).getBytes()); + } + }; + } + } + @TestExtension("loggerDecorator") public static class DescriptorImpl extends BuildWrapperDescriptor { + @Override public String getDisplayName() { + return "WrapperWithLogger"; + } + @Override public boolean isApplicable(AbstractProject item) { + return true; + } + } } } -- GitLab