JUL到SLF4J桥

我目前观察到第三方库(即restfb)正在使用java.util.logging,我看到这些日志最终在STDOUT,即使我没有在我的logback.xml中configuration的SLF4J控制台appender。 我的classpath中也有jul-to-slf4j桥。 jul-to-slf4j bridge是否只在日志logging到stdout的时候才loginlogbackconfiguration的appender?

您需要调用SLF4JBridgeHandler.install() 。 您还需要在java.util.logging中启用根日志logging器中的所有日志级别(原因摘录如下),并删除默认的控制台appender。

这个处理程序将把jul日志redirect到SLF4J。 但是,只有在jul中启用的日志才会被redirect。 例如,如果调用jullogging器的日志语句根据定义禁用了该语句,将不会到达任何SLF4JBridgeHandler实例,并且不能被redirect。

整个过程可以像这样完成

 import java.util.logging.LogManager; import java.util.logging.Logger; import org.slf4j.bridge.SLF4JBridgeHandler; LogManager.getLogManager().reset(); SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); Logger.getLogger("global").setLevel(Level.FINEST); 

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

正如在SLF4JBridgeHandler的javadoc中所提到的,您可以通过调用以编程方式安装SLF4JBridgeHandler:

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

或通过logging.properties

  // register SLF4JBridgeHandler as handler for the jul root logger handlers = org.slf4j.bridge.SLF4JBridgeHandler 

至于性能方面, jul-to-slf4j桥的部分讨论了这个问题。 实质上,由于您已经在使用logback,所以启用LevelChangePropagator应该会产生良好的性能,不pipe负载如何。

我的解决scheme

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

将jul-to-slf4j放在你的应用库或glassfish库上,这些将JULredirect到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> 

最后一个configuration是为了避免被其他logging器污染

看起来不错的解决scheme(考虑到JUL桥接的情况),并为我工作,因为我只需要在logback.groovy文件中写入所有内容

  1. 如果你根本不使用logback.groovyconfiguration或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 

(build议由我使用,因为你可以在Java代码variables/函数中作出反应,例如SLF4JBridgeHandler或关于webappDirName的日志目录)

(留下完整的文件,因为它给人一个更好的印象,如何设置一切或作为一个盯着模板)

(可能与某人有关 – 我的env: slf4j 1.7.5,logback 1.1.2,groovy 2.1.9

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

根据changelog它使用java.util.logging

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

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