我目前正在观察第三方库(即restfb)正在使用java.util.logging并且我看到这些日志最终在STDOUT中,即使我没有在我的logback中配置SLF4J控制台appender .XML。我的classpath中也有jul-to-slf4j桥。 jul-to-slf4j网桥是否仅在安装网桥时登录到由logback配置的appender,还是还记录到stdout?
答案 0 :(得分:76)
您需要致电SLF4JBridgeHandler.install()
。您还需要在java.util.logging中的根记录器上启用所有日志级别(以下摘录中的原因),并删除默认的控制台appender。
此处理程序将jul日志记录重定向到SLF4J。但是,只有日志 在j.u.l中启用将被重定向。例如,如果是日志语句 调用j.u.l.根据定义,记录器将禁用该语句 没有到达任何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
中启用它们,您将无法打开这些日志(由于上文摘录中提到的原因)。
答案 1 :(得分:35)
如SLF4JBridgeHandler的javadoc所述,您可以通过调用以编程方式安装SLF4JBridgeHandler:
// 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,因此无论负载如何,启用LevelChangePropagator都应该会产生良好的性能。
答案 2 :(得分:12)
我使用SLF4J和new Postgres driver 42.0.0
根据changelog,它使用java.util.logging
要获得驱动程序日志就足够了:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
添加logback.xml(logback-test.xml)
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<appender ...
<logger name="org.postgresql" level="trace"/>`
添加代码
static {
SLF4JBridgeHandler.install();
}
答案 3 :(得分:3)
我的解决方案:
SLF4JBridgeHandler.install();
java.util.logging.LogManager.getLogManager().getLogger("").setLevel( Level.INFO);
将jul-to-slf4j放在你的app libs或glassfish libs上,这些将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>
最后一个配置是为了避免被其他记录器污染
答案 4 :(得分:2)
解决方案似乎很好(考虑到JUL桥接的情况)并且对我有用,因为我只需要在 logback.groovy 文件中编写所有内容。< / p>
(如果你没有使用 logback.groovy 配置或 logback ,当然你必须将逻辑部分放入某个类(例如class MyApp { static { /* log init code here */ } ... }
)。)
<强>的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代码变量/函数作出反应,你可以在这里看到,例如 SLF4JBridgeHandler 或关于 webappDirName 的日志目录)
(保留文件完整,因为它可以更好地展示如何设置所有内容或作为凝视模板)
(可能与某人有关 - 我的环境: slf4j 1.7.5,logback 1.1.2,groovy 2.1.9 )