slf4j 装饰器正在记录装饰器 FQCN,而不是调用 FQCN
slf4j decorator is logging the decorator FQCN, not the calling FQCN
我从 slf4j Logger
class 创建了一个装饰器 class 因为我需要在 info
, debug
,等方法。我正在使用 log4j12 绑定。
除了记录的 FQCN 外它工作正常:它不是调用 class 的 FQCN,而是装饰器的 FQCN class。
package foo.bar;
import org.slf4j.Logger;
public class MyLogger implements Logger {
private final Logger logger;
private final boolean traceAndExit;
public MyLogger(Logger logger, boolean traceAndExit) {
this.logger = logger;
this.traceAndExit = traceAndExit;
} // MyLogger
@Override
public void info(String message) {
try {
logger.info(message);
} catch (Exception e) {
if (traceAndExit) {
traceAndExit(e);
} // if
} // catch
} // info
...
如果有任何其他 class foo.bar.OtherClass
我这样做:
MyLogger myLogger = new MyLogger(LoggerFactory.getLogger(OtherClass.class), true);
myLogger.info("this is a message");
然后我获取的日志打印foo.bar.MyLogger
而不是foo.bar.OtherClass
(%C
转换字符)。
知道如何正确执行此操作吗?我必须打印 FQCN。
不幸的是,您没有提到您使用的是什么日志记录实现。 slf4j 只是一个常见的 API 由各种日志实现共享。
幸运的是,至少 Logback、Log4j 和 Log4j 2 应该以相同的方式解释 %C
格式说明符。
Logback documentation and the Log4j 2 documentation 将 %C
定义为 "the fully-qualified class name of the caller issuing the logging request." 这分别发生在 ch.qos.logback.classic.spi.CallerData
和 org.apache.logging.log4j.util.ReflectionUtil
中。基本上,框架会检查堆栈跟踪,直到他们有理由确定已经离开了自己的代码库,然后使用他们在堆栈上遇到的 "next" class 。
在您的情况下,这是 MyLogger
class.
避免这种情况的最简单方法是完全放弃 %C
格式说明符。虽然在调试中很有用,但堆栈自省对性能有严重影响,无法干净地处理您的情况。
%c
格式说明符改为插入记录器的名称。如果正确初始化记录器:
// static logger
private static final Logger STATIC_LOG = LoggerFactory.getLogger(OtherClass.class);
// instance logger
private final Logger log = LoggerFactory.getLogger(getClass());
%c
产生预期的输出。这也允许高级日志记录解决方案,例如每个 class 多个记录器,例如审核日志记录。
如果您绝对必须使用 %C
说明符,Logback 提供了一个选项来设置调用者扫描忽略的框架包:
// during application startup
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.getFrameworkPackages().add("foo.bar.logging")
请注意,这将有效地禁用整个 foo.bar.logging
包的 %C
输出。 Log4j 2 似乎没有提供类似的配置选项。
我从 slf4j Logger
class 创建了一个装饰器 class 因为我需要在 info
, debug
,等方法。我正在使用 log4j12 绑定。
除了记录的 FQCN 外它工作正常:它不是调用 class 的 FQCN,而是装饰器的 FQCN class。
package foo.bar;
import org.slf4j.Logger;
public class MyLogger implements Logger {
private final Logger logger;
private final boolean traceAndExit;
public MyLogger(Logger logger, boolean traceAndExit) {
this.logger = logger;
this.traceAndExit = traceAndExit;
} // MyLogger
@Override
public void info(String message) {
try {
logger.info(message);
} catch (Exception e) {
if (traceAndExit) {
traceAndExit(e);
} // if
} // catch
} // info
...
如果有任何其他 class foo.bar.OtherClass
我这样做:
MyLogger myLogger = new MyLogger(LoggerFactory.getLogger(OtherClass.class), true);
myLogger.info("this is a message");
然后我获取的日志打印foo.bar.MyLogger
而不是foo.bar.OtherClass
(%C
转换字符)。
知道如何正确执行此操作吗?我必须打印 FQCN。
不幸的是,您没有提到您使用的是什么日志记录实现。 slf4j 只是一个常见的 API 由各种日志实现共享。
幸运的是,至少 Logback、Log4j 和 Log4j 2 应该以相同的方式解释 %C
格式说明符。
Logback documentation and the Log4j 2 documentation 将 %C
定义为 "the fully-qualified class name of the caller issuing the logging request." 这分别发生在 ch.qos.logback.classic.spi.CallerData
和 org.apache.logging.log4j.util.ReflectionUtil
中。基本上,框架会检查堆栈跟踪,直到他们有理由确定已经离开了自己的代码库,然后使用他们在堆栈上遇到的 "next" class 。
在您的情况下,这是 MyLogger
class.
避免这种情况的最简单方法是完全放弃 %C
格式说明符。虽然在调试中很有用,但堆栈自省对性能有严重影响,无法干净地处理您的情况。
%c
格式说明符改为插入记录器的名称。如果正确初始化记录器:
// static logger
private static final Logger STATIC_LOG = LoggerFactory.getLogger(OtherClass.class);
// instance logger
private final Logger log = LoggerFactory.getLogger(getClass());
%c
产生预期的输出。这也允许高级日志记录解决方案,例如每个 class 多个记录器,例如审核日志记录。
如果您绝对必须使用 %C
说明符,Logback 提供了一个选项来设置调用者扫描忽略的框架包:
// during application startup
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.getFrameworkPackages().add("foo.bar.logging")
请注意,这将有效地禁用整个 foo.bar.logging
包的 %C
输出。 Log4j 2 似乎没有提供类似的配置选项。