Saturday, April 16, 2016

Testing log4j2 plugins with custom string appender

There is a bunch of appenders in log4j2 ready for use - visit this manual to get the impression.
A custom appender class is not demanded to be implemented as a plugin.

There are several links, that worth looking:

  • This post explains how define the appender plugin.
  • This post shows an example of a none-plugin custom appender.
  • My favorite is this post about log4j2 customization with plugins. It suggests the extremely useful class StringAppender, which I used with small adjustments in my project for testing the plugins.

Custom string appender implementation


The class StringAppender4Tests appender implements output to the string stream. It is very useful for unit testing of plugins.
The appender code:
 public class StringAppender4Tests extends AbstractOutputStreamAppender<StringAppender4Tests.StringOutput4TestsStreamManager> {       
      static private LoggerContext context = (LoggerContext) LogManager.getContext(false);  
      static private Configuration configuration = context.getConfiguration();  
      StringOutput4TestsStreamManager outstreamMgr;       
      public static StringAppender4Tests startAppender(Logger logger, String nullablePatternString, String nullableHeaderString) {  
           StringAppender4Tests appender = StringAppender4Tests.createStringAppender(nullablePatternString, nullableHeaderString, null);  
           appender.addToLogger(logger, Level.INFO);  
           appender.start();  
           return appender;  
      }
      public static StringAppender4Tests startAppenderWithFilter(  
                Logger logger,   
                String nullablePatternString,   
                String nullableHeaderString,   
                Filter filter) {  
           StringAppender4Tests appender = StringAppender4Tests.createStringAppender(nullablePatternString, nullableHeaderString, filter);  
           appender.addToLogger(logger, Level.INFO);  
           appender.start();  
           return appender;  
      }
      public void stopAppender(Logger logger) {  
           LoggerConfig loggerConfig = configuration.getLoggerConfig(logger.getName());  
           loggerConfig.removeAppender(StringAppender4Tests.class.getSimpleName());  
      }
      public static void append(AppendProcedure procedure, Logger logger, String nullablePatternString, String nullableHeaderString) {  
           StringAppender4Tests appender = StringAppender4Tests.startAppender(logger, nullablePatternString, nullableHeaderString);  
           procedure.execute(appender);            
           appender.stopAppender(logger);  
      }
      public static void appendByFilter(AppendProcedure procedure, Logger logger, String nullablePatternString, Filter filter) {  
           StringAppender4Tests appender = StringAppender4Tests.startAppenderWithFilter(logger, nullablePatternString, null, filter);  
           procedure.execute(appender);            
           appender.stopAppender(logger);  
      }  
      private StringAppender4Tests(  
                String name,  
                Layout<? extends Serializable> layout,   
                Filter filter,  
                StringOutput4TestsStreamManager outstreamMgr,  
                boolean ignoreExceptions,   
                boolean immediateFlush) {  
           super(name, layout, filter, ignoreExceptions, immediateFlush, outstreamMgr);  
           this.outstreamMgr = outstreamMgr;  
      }
      private static StringAppender4Tests createStringAppender(String nullablePatternString,String nullableHeaderString,Filter nullableFilter){  
           ByteArrayOutputStream outputStream = new ByteArrayOutputStream();  
           PatternLayout layout;  
           if (nullablePatternString == null) {  
                layout = PatternLayout.createDefaultLayout();  
           }  
           else {  
                layout = PatternLayout.createLayout(nullablePatternString, configuration, null, null, true, false, nullableHeaderString, null);  
           }  
           StringOutput4TestsStreamManager streamManager = new StringOutput4TestsStreamManager(  
                     outputStream,   
                     StringOutput4TestsStreamManager.class.getSimpleName(),   
                     layout);  
           return new StringAppender4Tests(  
                     StringAppender4Tests.class.getSimpleName(),  
                     layout,  
                     nullableFilter,  
                     streamManager,  
                     false, //ignoreExceptions  
                     true); //immediateFlush  
      }  
      private void addToLogger(Logger logger, Level level) {  
           LoggerConfig loggerConfig = configuration.getLoggerConfig(logger.getName());  
           loggerConfig.addAppender(this, level, null);  
           context.updateLoggers();  
      }  
      public String getOutput() {  
           outstreamMgr.flush();  
           return new String(outstreamMgr.getStream().toByteArray());  
      }
      protected static class StringOutput4TestsStreamManager extends OutputStreamManager {  
           ByteArrayOutputStream stream;  
           protected StringOutput4TestsStreamManager(ByteArrayOutputStream os, String streamName, Layout<?> layout) {  
                super(os, streamName, layout);  
                stream = os;  
           }  
           public ByteArrayOutputStream getStream() {  
                return stream;  
           }  
      }
      @FunctionalInterface  
      public static interface AppendProcedure {  
           void execute(StringAppender4Tests appender);  
      }  
 }  

Testing of configuration plugins with the custom string appender


My earlier post Extending log4j2 format with plugins shows implementation examples of a configuration converter and configuration lookup plugins.

The test of the configuration converter plugin with the StringAppender:
 @Test  
 public void testConfigAppendedToLog() {  
     final String layoutPattern = "%config %m%n";  
     final Logger logger = LogManager.getRootLogger();  
     StringAppender4Tests.append( (appender) -> {  
          ConfigConverter.setConfiguration(configStub);  
          logger.error("Test");  
          assertTrue("Configuration not found in message", appender.getOutput().indexOf(configStub.toString().replace('\n', ' ')) != -1);  
     },   
     logger, layoutPattern, null);            
 }  
The test of configuration lookup plugin with the StringAppender:
 @Test  
 public void testHeaderLookupSubstitutor() {  
     final String headerFormat = "HEADER: ${config:all}";  
     String header = Log4jHelper.interpolate(headerFormat);  
     final String layoutPattern = "%m%n";  
     final Logger logger = LogManager.getRootLogger();  
     StringAppender4Tests.append((StringAppender4Tests appender) ->{  
         logger.info("Test");  
         assertTrue("Configuration not found in message", appender.getOutput().indexOf(configStub.toString()) != -1);  
     },   
     logger, layoutPattern, header);            
 }  

Testing of error pattern filter plugin with the custom string appender


My earlier post Creating errors log with the log4j2 filter plugin shows an implementation example of error pattern filter plugin.

The tests of the plugin are implemented with the StringAppender:
 @Test  
 public void testDropOnMatchFilter() {  
     ErrorPatternsFilter filter = ErrorPatternsFilter.createFilter(ERROR_PATTERNS_CLASS, true);  
     final Logger logger = LogManager.getRootLogger();  
     StringAppender4Tests.appendByFilter((appender) ->{  
          String errorMsg = "Error msg for logging test";  
          logger.info(errorMsg);  
          assertFalse("Message should not be filtered !", appender.getOutput().indexOf(errorMsg) == -1);  
          String patternedError = ErrorsPatterns4Tests.ERROR_PATTERN_1+ errorMsg;  
          logger.info(patternedError);  
          assertTrue("Message should be filtered !", appender.getOutput().indexOf(patternedError) == -1);  
     }, logger, LAYOUT, filter);  
 }  
 @Test  
 public void testAcceptOnMatchFilter() {  
     ErrorPatternsFilter filter = ErrorPatternsFilter.createFilter(ERROR_PATTERNS_CLASS, false);  
     final Logger logger = LogManager.getRootLogger();  
     StringAppender4Tests.appendByFilter((appender) ->{  
          String errorMsg = "Error msg for logging test";  
          logger.info(errorMsg);  
          assertFalse("Message should be filtered !", appender.getOutput().indexOf(errorMsg) != -1);  
          String patternedError = ErrorsPatterns4Tests.ERROR_PATTERN_1+ errorMsg;  
          logger.info(patternedError);  
          assertTrue("Message should not be filtered !", appender.getOutput().indexOf(patternedError) != -1);  
     }, logger, LAYOUT, filter);  
 }  

All the sources on Git.

No comments :

About the author

My Photo
I trust only simple code and believe that code should be handsome. This is not a matter of technology, but professional approach, consolidated after years of software development. I enjoy to cause things working and feel very happy, when I manage to solve a problem.
Back to Top