Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback's architecture.

Information

Problems

  1. New package
    • org.apache.log4j changed to org.apache.logging.log4j

    • We may need to change in a bunch of modules when we try to convert to the new package


  2. DOM and Property Configurator was removed
  3. Log4J 2 over the old fashioned properties file is not supported so far
  4. API for setLevel / getEffectiveLevel - http://apache-logging.6191.n7.nabble.com/API-and-setLevel-getEffectiveLevel-td36238.html
  5. Unit-testing
    1. Add Appender

      Writer loggerOut = new StringWriter();
      final Layout layout = new EnhancedPatternLayout(EnhancedPatternLayout.TTCC_CONVERSION_PATTERN);
      Logger.getRootLogger().setLevel(Level.INFO); // Other tests might have set this to silent
      Logger.getRootLogger().addAppender(new WriterAppender(layout, loggerOut));

       

    2. Custom Appender

       
      public static class TestAppender extends AppenderSkeleton {
          public List<LoggingEvent> events = new ArrayList<>();
      
          @Override
          public void close() {
          }
      
          @Override
          public boolean requiresLayout() {
              return false;
          }
      
          @Override
          protected void append(LoggingEvent event) {
              events.add(event);
          }
      }

      Need to rewrite as a plugin.

    3. Custom Level

      public class LoggingLevel extends Level {
      
          public static final LoggingLevel AUDIT_TRAIL = new LoggingLevel(99, "AUDIT_TRAIL", 0);
      
          protected LoggingLevel(int level, String levelStr, int syslogEquivalent) {
              super(level, levelStr, syslogEquivalent);
          }
      
          public static Level toLevel(String sArg) {
              return AUDIT_TRAIL;
          }
      
          public static Level toLevel(int val) {
              return AUDIT_TRAIL;
          }
      
      }
  6. Log Tools can not start due to it relies on Log4j

    private AbstractBeanContainer populateContainer() {
        BeanItemContainer<LogLevelBean> container = new BeanItemContainer<>(LogLevelBean.class);
        Enumeration<Logger> loggers = logManager.getCurrentLoggers();
    
        while (loggers.hasMoreElements()) {
            Logger logger = loggers.nextElement();
            LogLevelBean logLevelBean = new LogLevelBean(
                    logger.getName(),
                    logger.getEffectiveLevel(),
                    // if the level is null, then that means the effectiveLevel is inherited from the parent,
                    // rather than copied from the level itself. therefore, we want to show the inherited tick
                    // (inherited = true)
                    logger.getLevel() == null
            );
    
            container.addBean(logLevelBean);
        }
    
        container.setItemSorter(new DefaultItemSorter() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((LogLevelBean) o1).getName().compareTo(((LogLevelBean) o2).getName());
            }
        });
    
        return container;
    }


 

Code Snippets

 

    public static void updateLoggers(final Appender appender, final Configuration config) {
        final Level level = null;
        final Filter filter = null;
        for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
            loggerConfig.addAppender(appender, level, filter);
        }
        config.getRootLogger().addAppender(appender, level, filter);
    }
    /**
     * http://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2
     */
    public static void setLevel(Logger logger, Level level) {
        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        final Configuration config = ctx.getConfiguration();

        LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName());
        LoggerConfig specificConfig = loggerConfig;

        // We need a specific configuration for this logger,
        // otherwise we would change the level of all other loggers
        // having the original configuration as parent as well

        if (!loggerConfig.getName().equals(logger.getName())) {
            specificConfig = new LoggerConfig(logger.getName(), level, true);
            specificConfig.setParent(loggerConfig);
            config.addLogger(logger.getName(), specificConfig);
        }
        specificConfig.setLevel(level);
        ctx.updateLoggers();
    }

 

 

 

  • No labels

6 Comments

  1. I tried out upgrading to log4j2 just by putting the new libraries in pom.xml (and excluding the old ones). I point to the configuration file via a JVM property.

    This seems to work just fine, and so far I haven't found any problems. Any classes using the old APIs should be handled by the log4j-1.2-api JAR. So far it seems that only the log-tools app is not working, but that was expected.

    Since we need log4j2 soon in our production setup, we'd love it if you could prioritize this upgrade, and are very happy to help you by reporting on our experiences.

    1. Yes, basically, it works because of using the bridge - http://logging.apache.org/log4j/2.x/log4j-1.2-api/. It allows applications coded to use Log4j 1.2 API to use Log4j 2 instead.

      In order to upgrade to Log4j 2 completely we're faced with some issues that I've mentioned above. Eg: new package, configuration, unit testing, etc
      See my branch for some piece of code of upgrading.

      HTH

      1. Hi,

        I took a look at your branch, but I will leave it to magnolia to officially do this upgrade in the product. I didn't want to patch a bunch of modules...

        I did however create a patch of the log-tools module, which is now working together with log4j2. Other than that I could not find any problems so far. The other issues you mention make sense, but don't seem to actually affect the system while running, since I assume they are handled by the log4j1 bridge API. AFAICT it was only the log-tools that really don't work with log4j2...

        You can find my patch of the log-tools app here: https://git.magnolia-cms.com/users/runger/repos/log-tools/browse?at=refs%2Fheads%2Flog4j2

        I'm very happy if you can use it, let me know if I need to make a pull request or change the permissions...

        Regards from Vienna,

        Richard

        1. I did another iteration on my patch of the log-tools app. It is now working well.

          There is a difference between log4j1 and log4j2 regarding the configuration.

          Whereas in log4j1, reading the loggers gave back the whole hierarchy (eg the entire "tree" of loggers defined by the configured loggers), in log4j2 you can only read out the loggers that were actually configured.

          Log4j1:   configuring 2 loggers, a.b.c.d  and a.b.e.f  gave you:

          a
          a.b
          a.b.c
          a.b.c.d
          a.b.e
          a.b.e.f

          Log4j2:    configuring 2 loggers, a.b.c.d  and a.b.e.f  gives you:

          a.b.c.d
          a.b.e.f


          This difference has an impact on the way the log-levels app works. If the entire tree is displayed, like in the old log4j, then it is enough to just offer the ability to set the level of each tree node.

          If only the actually configured loggers are displayed, then this is not expressive enough - the user needs the ability to add new loggers to the configuration.

          My recent patch adds a "New Logger" button to the log-levels app, which lets the user configure a completely new logger and adds it to the list.

          It is working nicely.

          1. Thank you Richard, ran into the same updating our own test setup (wink); by now we should be checking out your patch shortly.