JUL 呼叫 SLF4J 桥

我目前观察到一个第三方库(即 restfb)正在使用 java.util.log,我看到这些日志最终出现在 STDOUT 中,尽管我没有在 logback.xml 中配置 SLF4J 控制台附加器。我的类路径中还有 Jul-to-slf4j桥。Jul-to-slf4j 桥是否只在安装桥时记录到由 logback 配置的附加器,还是也记录到 stdout?

64888 次浏览

你得打电话给 SLF4JBridgeHandler.install()。您还需要在 java.util.log 中启用根日志记录器上的所有日志级别(原因在下面的摘录中) ,并删除默认的控制台附加程序。

这个处理程序会将 jul 日志重定向到 SLF4J 将被重定向。例如,如果一个 log 语句 调用 j.u.llogger 禁用该语句,根据定义,will 不能到达任何 SLF4JBridgeHandler 实例并且不能被重定向。

整个过程可以这样完成

import java.util.logging.Logger;
import org.slf4j.bridge.SLF4JBridgeHandler;


SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
Logger.getLogger("").setLevel(Level.FINEST); // Root logger, for example.

出于性能方面的原因,您可以将这个级别设置为更高的级别,但是如果不首先在 java.util.logging中启用这些日志,您将无法打开这些日志(出于上面摘录中提到的原因)。

正如在针对 桥接处理程序的 javadocs 中提到的,通过调用:

 // 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();

或者通过 logging.properties

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

至于性能,关于 Jul-to-slf4j桥的部分讨论了这个问题。实际上,由于您已经在使用 logback,启用 水平变化传播器应该会产生良好的性能,而不管负载如何。

我的解决办法是:

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

将 JUL-to-SLF4J 放在应用程序库或 glassfish 库中,这些将 JUL 重定向到 SLF4J (因此在我的例子中是 LOG4J)

然后对于新泽西,你可以这样做:

<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>

最后一个配置是避免被其他日志记录器污染

看起来不错的解决方案(考虑到使用 JUL 桥接的情况) ,并且适用于我,因为我使用的是 只需要在 < em > logback.groovy 文件中写入所有内容

  1. (如果根本没有使用 < em > logback.groovy 配置或 < em > logback ,当然你必须 将逻辑部分放入某个类中(例如 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
    

(建议我使用,因为您可以使用 Java 代码 vars/function 作出反应,正如您在这里看到的,例如 桥接处理程序或关于 WebappDirName的日志目录)

(保持文件完整,因为它提供了一个更好的印象,如何一切都可以设置或作为一个起始模板)

(可能与某人有关-我的 env: Slf4j 1.7.5,logback 1.1.2,groovy 2.1.9)

我用的是 SLF4J 和 新的 Postgres 驱动程序42.0.0

根据 更改日志,它使用 java.util.log

拥有驱动程序日志就足够了:

  1. 加入 Jul-to-slf4j 桥:

    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
    </dependency>
    
  2. 添加 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. 加入代码

    static {
    SLF4JBridgeHandler.install();
    }