简体   繁体   English

7 月到 SLF4J 桥接器

[英]JUL to SLF4J Bridge

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 console appender configured in my logback.xml.我目前正在观察一个 3rd 方库(即 restfb)正在使用 java.util.logging 并且我看到这些日志最终出现在 STDOUT 中,即使我没有在我的 logback.xml 中配置 SLF4J 控制台附加程序。 I also have the jul-to-slf4j bridge in my classpath.我的类路径中也有jul-to-slf4j桥。 Does the jul-to-slf4j bridge only log to the appenders configured by logback when the bridge is installed or does it also log to stdout? jul-to-slf4j 网桥是否仅在安装网桥时记录到由 logback 配置的附加程序,还是也记录到标准输出?

You need to call SLF4JBridgeHandler.install() .您需要调用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.您还需要在 java.util.logging 的根记录器中启用所有日志级别(原因在下面的摘录中)并删除默认的控制台附加程序。

This handler will redirect jul logging to SLF4J.这个处理程序会将 jul 日志重定向到 SLF4J。 However, only logs enabled in jul will be redirected.但是,只有在 jul 中启用的日志才会被重定向。 For example, if a log statement invoking a jul logger disabled that statement, by definition, will not reach any SLF4JBridgeHandler instance and cannot be redirected.例如,如果调用 jul 记录器的日志语句禁用了该语句,根据定义,将不会到达任何 SLF4JBridgeHandler 实例并且无法重定向。

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).出于性能原因,您可以将级别设置为高于最好的级别,但是如果不首先在java.util.logging启用它们,您将无法打开这些日志(出于上述摘录中提到的原因)。

As mentioned in the javadocs for SLF4JBridgeHandler , you get either install SLF4JBridgeHandler programmatically by invoking:SLF4JBridgeHandler的 javadocs 中所述,您可以通过调用以编程方式安装 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();

or via logging.properties或通过 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.至于性能, jul-to-slf4j桥接部分讨论了这个问题。 In essence, since you are already using logback, enabling the LevelChangePropagator should yield good performance regardless of the load.本质上,由于您已经在使用 logback,因此无论负载如何,启用LevelChangePropagator都应该产生良好的性能。

I use SLF4J and new Postgres driver 42.0.0我使用 SLF4J 和新的 Postgres 驱动程序 42.0.0

According changelog it use java.util.logging根据更改日志它使用 java.util.logging

To have driver logs it is enough:要有驱动程序日志就足够了:

  1. Add jul-to-slf4j bridge :添加jul-to-slf4j 桥

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

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)将 jul-to-slf4j 放在您的应用程序库或 glassfish 库上,这些将 JUL 重定向到 SLF4J(因此在我的情况下为 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最后一个配置是为了避免被其他记录器污染

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 .看起来不错的解决方案(考虑到 JUL 桥接的情况)并且对我有用,因为我只需要在logback.groovy文件中写入所有内容

  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 (eg like class MyApp { static { /* log init code here */ } ... } ) .) 如果您根本不使用logback.groovy配置或logback ,当然您必须将逻辑部分放入某个类中(例如class MyApp { static { /* log init code here */ } ... } 。 )

  2. src/logback.groovy : 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, eg SLF4JBridgeHandler or the log dir regarding webappDirName ) (推荐由我使用,因为您可以对 Java 代码变量/函数做出反应,如您在此处看到的,例如SLF4JBridgeHandler或有关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 ) (可能与某人有关 - 我的 env: slf4j 1.7.5, logback 1.1.2, groovy 2.1.9

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM