【问题标题】:logback doesn't print method or line numberlogback 不打印方法或行号
【发布时间】:2020-04-16 21:09:44
【问题描述】:

这是一个使用 Groovy 的 Gradle 项目,用于应用代码和测试代码。但我使用 logback.xml 文件进行配置。

这里可能很重要的一个因素是我使用 Groovy @Slf4j 注释来提供记录器。

%method%line 转换词通常分别打印为“invoke”和“-1”(尽管有时“invoke0”和“-2”)。

有趣的是,它有时会打印方法和编号:例如,当它是带有Exception 参数的ERROR 级别日志时:

log.error( 'indexSearcher.indexReader.close() threw Exception', e )

...我认为这与带有“位置”数据的e 对象有关,然后 logback 可以利用这些数据。但只是偶尔会打印出带有方法和行号的INFO 级别消息:这很令人费解。

我见过有人在使用异步 appender 时遇到问题,但我的 appender 是 ROLLING_FILE (RollingFileAppender)。这不是异步追加器的扩展。

我尝试了其他 SO 问题中记录的其他补救措施(例如 here):我已将这些行添加到我的附加程序中:

<includeCallerData>true</includeCallerData>
<param name="locationInfo" value="true" />

...没有解决问题。

某处有人说有必要确保调试数据在某个时候打开。在 Groovy 环境中,我不确定如何尝试这个想法。

【问题讨论】:

  • 问题出现是因为groovy默认进行动态调用。用于测试 - 尝试使用 @CompileStatic 仅注释一种方法,它将打印正确的方法名称和行号。
  • @daggett 非常感谢您解开这个谜团。我不知道为什么“动态调用”会导致这种奇怪的现象,特别是对于这个特定的模块:超出我的工资等级。
  • groovy 在运行时而不是在编译时搜索所有方法。并间接调用它们——例如通过反射Method.invoke(...)
  • 是的,明白这一点:Groovy 显然是一种动态语言......我仍然完全不明白为什么动态方法调用会导致像 logback 这样的模块出现这种故障,尤其是间歇性故障:我推测堆栈帧信息会以某种方式被剥离......有时。我可能会尝试研究它,但对我能理解的期望并不高(更不用说找到解决方案了)。

标签: logging groovy logback slf4j


【解决方案1】:

logback.groovy

appender("STDOUT", ConsoleAppender) {
  encoder(PatternLayoutEncoder) {
    pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level {%logger} - %class.%method:%line - %msg%n"
  }
}
root(DEBUG, ["STDOUT"])

X.groovy

@GrabConfig(systemClassLoader=true)
@GrabResolver(name='maven2',root='http://repo1.maven.org/maven2/')
@Grab(group='ch.qos.logback', module='logback-classic', version='1.2.3')
@Grab(group='org.slf4j', module='slf4j-api', version='1.7.30')

import groovy.util.logging.Slf4j

@Slf4j
class A {
    def f() {
        log.info 'msg-info-2'       //line 11
        log.with{
            info  'msg-info-1'      //line 13
            //new Exception("test").printStackTrace(System.out)
        }
    }
}

def a = new A()
a.f()

命令groovy X.groovy 打印:

12:24:43.134 [main] INFO  {A} - A.f:11 - msg-info-1
12:24:43.139 [main] INFO  {A} - sun.reflect.NativeMethodAccessorImpl.invoke0:-2 - msg-info-2

为什么

在 logback 中有一个类 CallerData,其方法为 extract,它基于 Throwable 参数将调用者数据信息提取为一个数组,并返回一个 StackTraceElement 数组。它试图清理堆栈跟踪,但是对于 groovy 来说它可能非常复杂。

例如,如果您取消注释行 new Exception("test").printStackTrace(System.out)

你会看到这样的堆栈跟踪:

java.lang.Exception: test
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    ... + 11 elements
    at A$_f_closure1.doCall(X.groovy:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    ... + 15 elements
    at A.f(X.groovy:12)
    ... + 20 elements

CallerData 尝试从堆栈跟踪中跳过技术性的 groovy 元素,但未能检测到正确的元素。


解决方案

可以为 logback 上下文修改 Framework Packages

我不知道如何从 logback.xml 做到这一点,但我发现如何使用 logback.groovy 做到这一点

logback.groovy(已修改)

也许对于您的情况,您必须添加另一个框架(忽略)包...

context.getFrameworkPackages().addAll([
    "sun.reflect", 
    "java.lang.reflect", 
    "org.codehaus.groovy", 
    "groovy.lang.MetaMethod",
    "jdk.internal.reflect"
    ])

appender("STDOUT", ConsoleAppender) {
  encoder(PatternLayoutEncoder) {
    pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level {%logger} - %class.%method:%line - %msg%n"
  }
}
root(DEBUG, ["STDOUT"])

使用此配置,上面的代码可以正确打印行:

13:12:14.410 [main] INFO  {A} - A.f:11 - msg-info-2
13:12:14.416 [main] INFO  {A} - A$_f_closure1.doCall:13 - msg-info-1

【讨论】:

  • 非常感谢!这看起来很神奇。我将了解如何将配置文件从 .xml 文件切换到 .groovy 文件。自从我开始使用 Groovy 以来,我就知道你得到的令人难以置信的堆栈跟踪。 StackTraceUtils 有一个 sanitize 甚至 deepSanitize 方法来尝试解决这个问题。毫无疑问,您的解决方案是有代价的(时间),但是......哇! (注意,我想知道 logback 的作者 Ceki 是否能够使用这些 sanitize 方法来使事物在 Groovy 上下文中发挥作用)
  • 我已经对此进行了相当多的测试:它有时可以工作,但(到目前为止)并没有解决问题。从我的第一个实验开始,它仍然经常打印“invoke”和“-1”,特别是当日志消息包含插值的 Groovy 占位符(或任何你称之为:${var})的时候。此外,它有时无法正确打印该方法:“core.MiscSpec.$spock_feature_1_2:38”:第 38 行,是的,但该功能也有名称。是不是还有更多的包需要添加到框架包中?
  • core.MiscSpec.$spock_feature_1_2 没有什么可做的 - 因为在编译后可能是一个真正的类/方法名......为了检测额外的框架包,您可以添加到模式 %class 占位符和何时您看到不正确的类名 - 只需将其包添加到列表中即可。
  • +1 女士/先生,我向您的天才致敬!我包括了%class 并揭示了另一个包包括:“jdk.internal.reflect”......现在我真的得到了结果。为了您的答案的未来读者,我将建议对您的答案进行编辑。
猜你喜欢
  • 1970-01-01
  • 2012-12-06
  • 2015-06-12
  • 1970-01-01
  • 2015-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多