DevOps Zone is brought to you in partnership with:

Daniel Doubrovkine (aka dB.) is one of the tallest engineers at Art.sy. He founded and exited a successful Swiss start-up in the 90s, worked for Microsoft Corp. in Redmond, specializing in security and authentication, dabbled in large scale social networking and ran a big team that developed an expensive Enterprise product in NYC. After turning open-source cheerleader a few years ago in the worlds of C++, Java and .NET, he converted himself to Ruby and has been slowly unlearning everything he learned in the last 15 years of software practice. Daniel has posted 46 posts at DZone. You can read more from them at their website. View Full User Profile

Implementing My First Jenkins Plugin: AnsiColor

08.03.2012
| 5479 views |
  • submit to reddit
I installed Jenkins last week for the very first time. A couple of days later I was able to publish my first plugin, called AnsiColor, which colorizes ANSI output. It’s the plugin you’ve all been waiting for.

The Jenkins plugin tutorial is quite good, I recommend you just follow it. It has a maven-based cookbook to generate a new project. But if you’re like me, you’ll reconstruct a plugin from scratch (and possibly trade time for a better understanding). I’ll just mention a few things that could have been helpful to me.

Basics

A plugin extends hudson.Plugin. This class isn’t even necessary, but it’s a good opportunity to setup a logger that’s going to tell us that the plugin is actually being loaded. Whether you’re testing the plugin locally or running a production instance of Jenkins, this will come handy.

    public class PluginImpl extends Plugin {
        private final static Logger LOG = Logger.getLogger(PluginImpl.class.getName());
     
        public void start() throws Exception {
            LOG.info("starting ansicolor plugin");
        }
    }


Processing Build Output

Our goal is to process build output and insert HTML color markup. So the first task is to find a Jenkins extension point from here that exposes build log data. I found the very promising ConsoleLogFilter. A simple extension is marked with @Extension and I thought I was done.

    @Extension
    public class AnsiColorConsoleLogFilter extends ConsoleLogFilter {
     
        @SuppressWarnings("unchecked")
        @Override
        public OutputStream decorateLogger(AbstractBuild build, OutputStream logger)
                throws IOException, InterruptedException {
            return new AnsiColorizer(logger, build.getCharset());
        }
     
    }


What’s that AnsiColorizer? It’s a stream processing class that inherits from hudson.console.LineTransformationOutputStream that overrides a method called eol, called for each output line. It decorates our logger. Notice that the bytes passed into the eol method are pre-allocated, hence the len parameter. You get a lot more bytes than in the current line, but it’s garbage from previous output after len.

The following code will strip all ANSI markup.

    @Override
    protected void eol(byte[] b, int len) throws IOException {
        String ansiEncodedString = new String(b, 0, len);
        AnsiString ansiString = new AnsiString(ansiEncodedString);
        String plainString = ansiString.getPlain().toString();
        byte[] plainBytes = plainString.getBytes();
        out.write(plainBytes, 0, plainBytes.length);
    }


Pretty simple, right? Well, it only works if we want to remove stuff or add text and doesn’t work for HTML. Console output gets HTML-encoded as it passed through subsequent filtering, so inserting HTML, such as color, will end up encoded too. Sad face.

Console Notes

Jenkins has another extension point, BuildWrapper. It will add an option to every build project to enable the decoration of the build logger to which we can attach a ConsoleAnnotationDescriptor. All this is rather convoluted, but constructed with good intentions of being able to stream data. As a recent Rubyist I raised all of my eyebrows time-and-again – I forgot how much people love factories in Java. Anyway, that lets you insert ConsoleNote elements before and after a line of log output. The note is HTML. But ANSI characters can be anywhere in the string, so how is this helpful?

Lets use the extra brain cells that didn’t die while sorting out wrappers, factories, decorators and annotators. Given a string, such as “Hello ]32mCruel Java World”, how do we make it display “Hello <span style=”color: green”>Cruel Java World</span>” given that we can only prepend and append text? Like this.

    Hello <span style=”color: green”>Cruel Java World</span>
    <span style="display: none">Hello ]32mCruel Java World</span>

I know, it’s a total hack, but it works and nobody will complain.

    String colorizedData = colorize(this.data);
    if (! colorizedData.contentEquals(this.data)) {
        text.addMarkup(charPos, colorizedData);
        text.addMarkup(charPos, charPos + text.length(), "<span style=\"display: none;\">", "</span>");
    }

ansicolor

Source Code

Full plugin source code is here on Github.


Published at DZone with permission of its author, Daniel Doubrovkine. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)