JUL to SLF4J Bridge

前端 未结 5 1380
小蘑菇
小蘑菇 2020-11-27 11:57

I\'m currently observing that a 3rd party library (namely restfb) is using java.util.logging and I\'m seeing those logs end up in STDOUT even though I don\'t have an SLF4J c

相关标签:
5条回答
  • 2020-11-27 12:14

    You need to call SLF4JBridgeHandler.install(). You also need to enable all log levels at the root logger (reason in excerpt below) in java.util.logging and remove the default console appender.

    This handler will redirect jul logging to SLF4J. However, only logs enabled in j.u.l. will be redirected. For example, if a log statement invoking a j.u.l. logger disabled that statement, by definition, will not reach any SLF4JBridgeHandler instance and cannot be redirected.

    The whole process can be accomplished like so

    import java.util.logging.Logger;
    import org.slf4j.bridge.SLF4JBridgeHandler;
    
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
    Logger.getLogger("").setLevel(Level.FINEST); // Root logger, for example.
    

    You can set the level to something higher than finest for performance reasons, but you won't be able to turn those logs on without enabling them in java.util.logging first (for the reason mentioned above in the excerpt).

    0 讨论(0)
  • 2020-11-27 12:16

    Solution that seems nice (considering the circumstances with the JUL bridging) and works for me, since I only have to write everything in the logback.groovy file.

    1. (If you are not using logback.groovy configuration or logback at all, of course you have to put the logic part into some class (e.g. like class MyApp { static { /* log init code here */ } ... }).)

    2. src/logback.groovy:

       import org.slf4j.bridge.SLF4JBridgeHandler
       import ch.qos.logback.classic.jul.LevelChangePropagator
      
       // for debug: just to see it in case something is logging/initialized before
       System.out.println( 'my myapp logback.groovy is loading' )
      
       // see also: http://logback.qos.ch/manual/configuration.html#LevelChangePropagator
       // performance speedup for redirected JUL loggers
       def lcp = new LevelChangePropagator()
       lcp.context = context
       lcp.resetJUL = true
       context.addListener(lcp)
      
       // needed only for the JUL bridge: http://stackoverflow.com/a/9117188/1915920
       java.util.logging.LogManager.getLogManager().reset()
       SLF4JBridgeHandler.removeHandlersForRootLogger()
       SLF4JBridgeHandler.install()
       java.util.logging.Logger.getLogger( "global" ).setLevel( java.util.logging.Level.FINEST )
      
       def logPattern = "%date |%.-1level| [%thread] %20.20logger{10}|  %msg%n"
      
       appender("STDOUT", ConsoleAppender) {
           encoder(PatternLayoutEncoder) {
               pattern = logPattern
           }
       }
      
       /*// outcommenting in dev will not create dummy empty file
       appender("ROLLING", RollingFileAppender) {  // prod
           encoder(PatternLayoutEncoder) {
               Pattern = "%date %.-1level [%thread] %20.20logger{10}  %msg%n"
           }
           rollingPolicy(TimeBasedRollingPolicy) {
               FileNamePattern = "${WEBAPP_DIR}/log/orgv-fst-gwt-%d{yyyy-MM-dd}.zip"
           }
       }
       */
      
       appender("FILE", FileAppender) {  // dev
      
           // log to myapp/tmp (independent of running in dev/prod or junit mode:
      
           //System.out.println( 'DEBUG: WEBAPP_DIR env prop:  "."='+new File('.').absolutePath+',  \${WEBAPP_DIR}=${WEBAPP_DIR},  env=' + System.getProperty( "WEBAPP_DIR" ))
           String webappDirName = "war"
           if ( new File( "./../"+webappDirName ).exists() )  // we are not running within a junit test
               file = "../tmp/myapp.log"
           else  // junit test
               file = "tmp/myapp-junit-tests.log"
      
           encoder(PatternLayoutEncoder) { pattern = logPattern }
       }
      
       // without JUL bridge:
       //root(WARN, ["STDOUT", "ROLLING"])  // prod
       //root(DEBUG, ["STDOUT", "FILE"])  // dev
      
       // with JUL bridge: (workaround: see links above)
       def rootLvl = WARN
       root(TRACE, [/*"STDOUT",*/ "FILE"])
       // I manually added all "root package dirs" I know my libs are based on to apply
       // the root level to the second "package dir level" at least
       // depending on your libs used you could remove entries, but I would recommend
       // to add common entries instead (feel free to edit this post if you like to
       // enhance it anywhere)
       logger( "antlr", rootLvl )
       logger( "de", rootLvl )
       logger( "ch", rootLvl )
       logger( "com", rootLvl )
       logger( "java", rootLvl )
       logger( "javassist", rootLvl )
       logger( "javax", rootLvl )
       logger( "junit", rootLvl )
       logger( "groovy", rootLvl )
       logger( "net", rootLvl )
       logger( "org", rootLvl )
       logger( "sun", rootLvl )
      
      
       // my logger setup
      
       logger( "myapp", DEBUG )
      
      
       //logger( "org.hibernate.SQL", DEBUG )  // debug: log SQL statements in DEBUG mode
       //logger( "org.hibernate.type", TRACE )  // debug: log JDBC parameters in TRACE mode
       logger( "org.hibernate.type.BasicTypeRegistry", WARN )  // uninteresting
      
       scan("30 seconds")  // reload/apply-on-change config every x sec
      

    (recommended to be used by me since you can react with Java code vars/functions as you can see in here with, e.g. SLF4JBridgeHandler or the log dir regarding webappDirName)

    (left the file complete since it gives a better impression how everything can be setup or as a starting template)

    (may be relevant to somebody - my env: slf4j 1.7.5, logback 1.1.2, groovy 2.1.9)

    0 讨论(0)
  • 2020-11-27 12:20

    My solution :

    SLF4JBridgeHandler.install();
    java.util.logging.LogManager.getLogManager().getLogger("").setLevel( Level.INFO);
    

    placing jul-to-slf4j on your app libs or glassfish libs, these redirect JUL to SLF4J (and thus in my case to LOG4J)

    then for Jersey, you could do something like :

    <logger name="com.sun.jersey" additivity="false">
        <level value="WARN" />
        <appender-ref ref="JVM" />
        <appender-ref ref="CONSOLE" />
    </logger>   
    
    <logger name="com.sun.common.util.logging" additivity="false">
        <level value="ERROR" />
        <appender-ref ref="JVM" />
        <appender-ref ref="CONSOLE" />
    </logger>
    

    the last config is to avoid to be polluted by other loggers

    0 讨论(0)
  • 2020-11-27 12:23

    As mentioned in the javadocs for SLF4JBridgeHandler, you get either install SLF4JBridgeHandler programmatically by invoking:

     // Optionally remove existing handlers attached to j.u.l root logger
     SLF4JBridgeHandler.removeHandlersForRootLogger();  // (since SLF4J 1.6.5)
    
     // add SLF4JBridgeHandler to j.u.l's root logger, should be done once during
     // the initialization phase of your application
     SLF4JBridgeHandler.install();
    

    or via logging.properties

     // register SLF4JBridgeHandler as handler for the j.u.l. root logger
     handlers = org.slf4j.bridge.SLF4JBridgeHandler
    

    As for performance, the section on jul-to-slf4j bridge discusses this issue. In essence, since you are already using logback, enabling the LevelChangePropagator should yield good performance regardless of the load.

    0 讨论(0)
  • 2020-11-27 12:25

    I use SLF4J and new Postgres driver 42.0.0

    According changelog it use java.util.logging

    To have driver logs it is enough:

    1. Add jul-to-slf4j bridge:

      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>jul-to-slf4j</artifactId>
          <version>${slf4j.version}</version>
          <scope>runtime</scope>
      </dependency>
      
    2. Add in logback.xml (logback-test.xml)

      <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
          <resetJUL>true</resetJUL>
      </contextListener>
      
      <appender ...
      
      <logger name="org.postgresql" level="trace"/>`
      
    3. Add in code

      static {
          SLF4JBridgeHandler.install();
      }
      
    0 讨论(0)
提交回复
热议问题