2

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)

ekjcfn3902039
  • 1,673
  • 3
  • 29
  • 54

1 Answers1

2

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.

jmehrens
  • 10,580
  • 1
  • 38
  • 47
  • Hi thanks for the tips. Unfortunately it did not work for me. I updated the main post to reflect what I tried. Is there a different way I should be doing this? – ekjcfn3902039 Jul 12 '17 at 13:22
  • @ekjcfn3902039 Your code is still not pinning the root logger. The root logger has to be stored in the pin array. – jmehrens Jul 12 '17 at 14:16
  • I went ahead and updated the code shown above. It didn't make a difference for me to add the root logger to the pin – ekjcfn3902039 Jul 12 '17 at 19:50
  • @ekjcfn3902039 Did you try to set `false` in the logback.xml? – jmehrens Jul 12 '17 at 20:05
  • I just tried that, but no luck. The results were the same. – ekjcfn3902039 Jul 13 '17 at 12:29
  • I updated the code to change the LogFormatter to prove I have access to the correct logger. It changes as expected. However, it is only written to the console(shows up in red color instead of black) and not to the log files. Even though it's a ConsoleHandler, I should still be able to grab it and redirect to logback, right? – ekjcfn3902039 Jul 13 '17 at 13:58
  • @ekjcfn3902039 You should see the `SLF4JBridgeHandler` added to the root logger. If that handler is not attached then that is why you are not seeing JUL messages rerouted to logback. – jmehrens Jul 13 '17 at 14:37
  • Yes, I see the SLF4JBridgeHandler attached to the root. Any other suggestions? – ekjcfn3902039 Jul 13 '17 at 15:15
  • I think I solved it, but it may require adding a parent logger to every existing logger in the 3rd party app. See update above. Thanks for your help in pointing me to the right direction. – ekjcfn3902039 Jul 13 '17 at 19:55
  • 1
    Yeah, decompiling the 3rd party package shows they are creating a logger with a single name (MyClass instead of my.pkg.MyClass) and setting useParentHandlers(false) , which may be the cause. – ekjcfn3902039 Jul 14 '17 at 13:15