Search code examples
javalogginglogbackrcp

Cannot send JUL -> Logback


I am trying to send the logging from a 3rd party app (which uses a JUL ConsoleHandler) to logback. I have followed all the other posts on this website but cannot get it to work. Am I doing something wrong?

  • I added the following to the beginning of my RCP class that implements the bundle activator

    public class MyTestActivator implements BundleActivator {
        static {
          LogManager.getLogManager().reset();
          SLF4JBridgeHandler.removeHandlersForRootLogger();
          SLF4JBridgeHandler.install();
          java.util.logging.Logger.getGlobal().setLevel(Level.FINEST);
    
        } 
        @Override
        public void start(final BundleContext bundleContext) throws 
          Exception {
            try {
                LoggerContext context = (LoggerContext) LoggerFactory.
                  getILoggerFactory();
    
                JoranConfigurator configurator = new JoranConfigurator();
                  configurator.setContext(context);
                try {
                    context.reset();
                    configurator.doConfigure(getClass().
                    getResourceAsStream("/logback.xml"));
                } catch (JoranException e) {
                    throw new IOException(e.getMessage(), e);
                }
                StatusPrinter.printInCaseOfErrorsOrWarnings(context);
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
    }
    
  • My feature.xml file contains:

    <plugin
          id="jul.to.slf4j"
          download-size="0"
          install-size="0"
          version="0.0.0"
          unpack="false"/>
    
    <plugin
          id="ch.qos.logback.classic"
          download-size="0"
          install-size="0"
          version="0.0.0"
          unpack="false"/>
    
    <plugin
          id="ch.qos.logback.core"
          download-size="0"
          install-size="0"
          version="0.0.0"
          unpack="false"/>
    
  • My logback.xml contains:

    <configuration>
        <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
            <resetJUL>true</resetJUL>
        </contextListener>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <root level="info">
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>
    

Update: I have also tried the following but it did not work as expected

    private static final java.util.logging.Logger[] pin;
    static {
        LogManager.getLogManager().reset();
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
        pin = new java.util.logging.Logger[] { 
            java.util.logging.Logger.getLogger(""),        
            java.util.logging.Logger.getLogger("3rd.party.package") };

        for (java.util.logging.Logger l : pin) {
            l.setLevel(Level.FINEST);
            for (Handler h : l.getHandlers()){
                if (h instanceof ConsoleHandler){
                    h.setFormatter(new Formatter(){
                        public final String format(LogRecord record) { 
                            if (record == null) {
                                return "";
                            } else {
                                return "CONSOLE_MSG:"+record.getMessage();
                            }
                        }
                    });
                }
            }
        }

    }
  • Update I believe I have it working by adding the following code in place of the for loop in the previous section:

    java.util.logging.Logger root = java.util.logging.Logger.getLogger("");
    java.util.logging.Logger myLog = java.util.logging.Logger.getLogger("3rd.party.package");
    myLog.setParent(root);
    myLog.setUseParentHandlers(true);
    java.util.logging.LogManager.getLogManager().getLogger( "" ).setLevel( Level.ALL );
    

The only downside is that I'll probably have to catch each logger and do this. When I looked in the debugger I noticed none of them had a parent (it was null)


Solution

  • Logger.getGlobal() is not the root of all JUL loggers. The root of all JUL loggers is Logger.getLogger(""). If you make programmatic changes to loggers then you have to pin those loggers so the settings are not reverted when they are garbage collected.

    You need to make sure that log records and being published to the parent handlers and all the way up to the root logger.

    private static final java.util.logging.Logger[] pin;
    static {
        //Pin first.
        pin = new java.util.logging.Logger[] { 
            java.util.logging.Logger.getLogger(""),
            java.util.logging.Logger.getLogger("3rd.party.package") };
    
        //Setup SLF4J
        LogManager.getLogManager().reset();
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        //SLF4JBridgeHandler.install();
        java.util.logging.Logger.getLogger("").addHandler(new SLF4JBridgeHandler());
    
        //Change JUL levels.
        for (java.util.logging.Logger l : pin) {
            l.setLevel(Level.FINEST);
        }
    }
    

    You should also try:

    public class MyTestActivator implements BundleActivator {        
        static {
            //Pin first.
            pin = new java.util.logging.Logger[] { 
            java.util.logging.Logger.getLogger(""),
            java.util.logging.Logger.getLogger("3rd.party.package") };
    
            //Change JUL levels.
            for (java.util.logging.Logger l : pin) {
               l.setLevel(Level.FINEST);
            }
        }
    
        @Override
        public void start(final BundleContext bundleContext) throws Exception {
            try {
                LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
    
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(context);
                try {
                    context.reset();
                    configurator.doConfigure(getClass().getResourceAsStream("/logback.xml"));
                    //Setup SLF4J
                    SLF4JBridgeHandler.removeHandlersForRootLogger();
                    SLF4JBridgeHandler.install();
                } catch (JoranException e) {
                    throw new IOException(e.getMessage(), e);
                }
                StatusPrinter.printInCaseOfErrorsOrWarnings(context);
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    Set the logback levels in the for the root logger to debug or trace. Same with setting the level for the logback console appender.

    The only downside is that I'll probably have to catch each logger and do this.

    Iteration over the installed the loggers can be performed but the logger tree will change over time.

    The only downside is that I'll probably have to catch each logger and do this. When I looked in the debugger I noticed none of them had a parent (it was null)

    That is odd. The parent should only be null if the logger is the root logger. Maybe bug in the LogManager you are using?

    Alternatively, you could add a new SLF4JBridgeHandler() to every logger you want to monitor but that is not as nice as monitoring the root logger.