7 月 至 SLF4J 桥

发布于 2025-01-02 02:12:26 字数 267 浏览 0 评论 0原文

我目前观察到第 3 方库(即 Restfb)正在使用 java.util.logging,并且我看到这些日志最终出现在 STDOUT 中,即使我没有在 logback.xml 中配置 SLF4J 控制台附加程序。我的类路径中还有 jul-to-slf4j 桥。 jul-to-slf4j 桥接器仅在安装桥接器时记录到由 logback 配置的附加程序,还是也记录到 stdout?

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. I also have the jul-to-slf4j bridge in my classpath. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

千纸鹤 2025-01-09 02:12:26

您需要调用 SLF4JBridgeHandler.install()< /a>.您还需要在 java.util.logging 中启用根记录器的所有日志级别(原因见下面的摘录)并删除默认的控制台附加程序。

此处理程序会将 jul 日志记录重定向到 SLF4J。但是,仅记录
七月启用的将被重定向。例如,如果一条日志语句
根据定义,调用 jul 记录器会禁用该语句
无法到达任何 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 中启用这些日志,您将无法打开这些日志首先(出于上面摘录中提到的原因)。

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

呢古 2025-01-09 02:12:26

正如 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();

或通过logging.properties

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

至于性能,jul-to-slf4j 桥讨论了这个问题。本质上,由于您已经在使用 logback,因此启用 LevelChangePropagator 应该会产生良好的性能无论负载如何。

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.

困倦 2025-01-09 02:12:26

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

根据 changelog 它使用 java.util.logging

要获得驱动程序日志就足够了:

  1. 添加 jul-to-slf4j 桥

    <依赖>;
        org.slf4j;
        jul-to-slf4j
        <版本>${slf4j.version}
        <范围>运行时
    
    
  2. 在 logback 中添加。 xml(logback-test.xml)

    ;
        true
    
    
    <附加器...
    
    <记录器名称=“org.postgresql”级别=“trace”/>
    
  3. 在代码中添加

    <前><代码>静态{
    SLF4JBridgeHandler.install();
    }

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();
    }
    
你的往事 2025-01-09 02:12:26

我的解决方案:

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

将 jul-to-slf4j 放在您的应用程序库或 glassfish 库上,这些将 JUL 重定向到 SLF4J (因此在我的情况下重定向到 LOG4J),

然后对于 Jersey,您可以执行以下操作:

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

最后一个配置是为了避免被污染由其他记录器

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

长途伴 2025-01-09 02:12:26

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

  1. 如果您根本没有使用logback.groovy配置或logback,当然您必须将逻辑部分到某个类中(例如 class MyApp { static { /* 在此处记录初始化代码 */ } ... }。)

  2. src/logback.groovy

    <前><代码>导入org.slf4j.bridge.SLF4JBridgeHandler
    导入 ch.qos.logback.classic.jul.LevelChangePropagator

    // 用于调试:只是为了在之前记录/初始化某些内容时查看它
    System.out.println( '我的 myapp logback.groovy 正在加载' )

    // 另请参阅:http://logback.qos.ch/manual/configuration.html#LevelChangePropagator
    // 重定向 JUL 记录器的性能加速
    def lcp = new LevelChangePropagator()
    lcp.context = 上下文
    lcp.resetJUL = true
    context.addListener(lcp)

    // 仅 JUL 桥需要: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"

    附加器(“STDOUT”,ConsoleAppender){
    编码器(PatternLayoutEncoder){
    模式=日志模式
    }
    }

    /*// dev 中的注释不会创建虚拟的空文件
    附加器(“ROLLING”,RollingFileAppender){//产品
    编码器(PatternLayoutEncoder){
    模式 =“%date %.-1level [%thread] %20.20logger{10} %msg%n”
    }
    滚动策略(TimeBasedRollingPolicy){
    FileNamePattern = "${WEBAPP_DIR}/log/orgv-fst-gwt-%d{yyyy-MM-dd}.zip"
    }
    }
    */

    附加器(“文件”,FileAppender){//开发

    // 登录到 myapp/tmp (独立于在 dev/prod 或 junit 模式下运行:

    //System.out.println( 'DEBUG: WEBAPP_DIR env prop: "."='+new File('.').absolutePath+', \${WEBAPP_DIR}=${WEBAPP_DIR}, env=' + System.getProperty (“WEBAPP_DIR”))
    字符串 webappDirName = "war"
    if ( new File( "./../"+webappDirName ).exists() ) // 我们没有在 junit 测试中运行
    文件=“../tmp/myapp.log”
    else // 联合测试
    文件=“tmp/myapp-junit-tests.log”

    编码器(PatternLayoutEncoder) { 模式 = logPattern }
    }

    // 没有 JUL 桥:
    //root(WARN, ["STDOUT", "ROLLING"]) // 产品
    //root(DEBUG, ["STDOUT", "FILE"]) // dev

    // 使用 JUL 桥:(解决方法:请参阅上面的链接)
    def rootLvl = 警告
    根(跟踪,[/*“STDOUT”,*/“文件”])
    // 我手动添加了我知道我的库所基于的所有“根包目录”
    // 根级别至少到第二个“包目录级别”
    // 根据您使用的库,您可以删除条目,但我建议
    // 添加常用条目(如果您愿意,请随意编辑这篇文章
    // 在任何地方增强它)
    记录器(“antlr”,rootLvl)
    记录器(“de”,rootLvl)
    记录器(“ch”,rootLvl)
    记录器(“com”,rootLvl)
    记录器(“java”,rootLvl)
    记录器(“javassist”,rootLvl)
    记录器(“javax”,rootLvl)
    记录器(“junit”,rootLvl)
    记录器(“groovy”,rootLvl)
    记录器(“网络”,rootLvl)
    记录器(“组织”,rootLvl)
    记录器(“太阳”,rootLvl)

    // 我的记录器设置

    记录器(“myapp”,调试)

    //logger( "org.hibernate.SQL", DEBUG ) // debug: 以 DEBUG 模式记录 SQL 语句
    //logger( "org.hibernate.type", TRACE ) // 调试:以 TRACE 模式记录 JDBC 参数
    logger( "org.hibernate.type.BasicTypeRegistry", WARN ) // 无趣

    scan("30 秒") // 每 x 秒重新加载/应用更改配置

建议我使用,因为您可以使用 Java 代码变量/函数进行反应,如您在此处看到的,例如 SLF4JBridgeHandler 或日志关于 webappDirName 的 dir)

(保留文件完整,因为它可以更好地印象如何设置所有内容或作为起始模板)

(可能与某人相关 - 我的环境: slf4j 1.7.5、logback 1.1.2、groovy 2.1.9

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)

棒棒糖 2025-01-09 02:12:26

除了Dev 的回答中提供的配置命令之外。在我正在使用的代码中,有很多跟踪级别(FINEST)消息,其中包含由第 3 方库生成的 isLoggable 检查。因此,无条件启用所有级别看起来并不是一个好主意。

我正在使用 SLF4J Simple Logger,它没有动态更改日志记录级别的方法。因此,我添加了以下代码片段,以根据通过系统属性传递的 slf4j 简单记录器配置自动调整 java util 日志记录级别:

import java.util.logging.Level;
import java.util.logging.Logger;

class Main {
    static {
        java.util.Map<String,Level> levelMap = new java.util.HashMap<>();
        levelMap.put("TRACE", Level.FINEST);
        levelMap.put("DEBUG", Level.FINE);
        levelMap.put("INFO", Level.INFO);
        levelMap.put("WARN", Level.WARNING);
        levelMap.put("ERROR", Level.SEVERE);
        levelMap.put("OFF", Level.OFF);

        for (String property : System.getProperties().stringPropertyNames()) {
            if (property.startsWith(org.slf4j.simple.SimpleLogger.LOG_KEY_PREFIX)) {

                String logger = property.replaceFirst(org.slf4j.simple.SimpleLogger.LOG_KEY_PREFIX, "");
                String value = System.getProperty(property).toUpperCase();

                Level level = levelMap.getOrDefault(value, Level.INFO);
                Logger.getLogger(logger).setLevel(level);
            }
        }

        org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger();
        org.slf4j.bridge.SLF4JBridgeHandler.install();
    }
}

PS 此代码片段不处理类路径上 simplelogger.properties 文件中定义的简单记录器设置。假设这些是静态的,并且可以根据需要执行相应的 JUL 调整。

Logback 中可用的 LevelChangePropagator 基本上执行相同的操作,但动态跟踪级别更改。

In addition to configuration commands provided at answer by Dev. In the code I'm working with there is a lot of trace-level (FINEST) messages wrapped with isLoggable check generated by 3rd-party libraries. So enabling all the levels unconditionally doesn't look like a good idea.

I'm using SLF4J Simple Logger and it doesn't have means to dynamically change logging levels. Thus I've added following snippet of code to automatically tune java util logging levels based on slf4j simple logger configuration passed via system properties:

import java.util.logging.Level;
import java.util.logging.Logger;

class Main {
    static {
        java.util.Map<String,Level> levelMap = new java.util.HashMap<>();
        levelMap.put("TRACE", Level.FINEST);
        levelMap.put("DEBUG", Level.FINE);
        levelMap.put("INFO", Level.INFO);
        levelMap.put("WARN", Level.WARNING);
        levelMap.put("ERROR", Level.SEVERE);
        levelMap.put("OFF", Level.OFF);

        for (String property : System.getProperties().stringPropertyNames()) {
            if (property.startsWith(org.slf4j.simple.SimpleLogger.LOG_KEY_PREFIX)) {

                String logger = property.replaceFirst(org.slf4j.simple.SimpleLogger.LOG_KEY_PREFIX, "");
                String value = System.getProperty(property).toUpperCase();

                Level level = levelMap.getOrDefault(value, Level.INFO);
                Logger.getLogger(logger).setLevel(level);
            }
        }

        org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger();
        org.slf4j.bridge.SLF4JBridgeHandler.install();
    }
}

P.S. this snippet doesn't handle Simple Logger settings defined in simplelogger.properties file on your classpath. Assuming those are static and corresponding JUL tuning could be performed as needed.

The LevelChangePropagator available in Logback basically does the same but dynamically tracks Level changes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文