Java的众多(叹息......)日志框架都可以很好地显示创建日志消息的方法的源文件名的行号:
log.info("hey");
[INFO] [Foo:413] hey
但是如果中间有一个辅助方法,那么实际的调用者将是辅助方法,而且信息量不大。
log_info("hey");
[INFO] [LoggingSupport:123] hey
有没有办法告诉日志记录系统在确定要打印的源位置时从callstack中删除一个帧?
我认为这是特定于实现的;我需要的是Log4J通过Commons Logging,但我很想知道其他选项。
答案 0 :(得分:33)
替代答案。
可以通过使用
方法请求log4j排除帮助程序类Category.log(String callerFQCN,Priority level,Object message,Throwable t)
并将帮助程序类指定为'callerFQCN'。
例如,这是一个使用帮助器的类:
public class TheClass {
public static void main(String...strings) {
LoggingHelper.log("Message using full log method in logging helper.");
LoggingHelper.logNotWorking("Message using class info method");
}}
和帮助者的代码:
public class LoggingHelper {
private static Logger LOG = Logger.getLogger(LoggingHelper.class);
public static void log(String message) {
LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);
}
public static void logNotWorking(String message) {
LOG.info(message);
} }
第一种方法将输出您的预期结果。
Line(TheClass.main(TheClass.java:4)) Message using full log method in logging helper. Line(LoggingHelper.logNotWorking(LoggingHelper.java:12)) Message using class info method
使用此方法时,Log4j将照常工作,如果不需要,则避免计算堆栈跟踪。
答案 1 :(得分:5)
请注意,提供行号是非常昂贵,无论是从Log4j还是以下内容中获得的。你必须接受这笔费用......
您可以使用以下API:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
stackTraceElement.getLineNumber();
更新:
你必须自己计算。所以:
根据您喜欢的记录器的方式,您的帮助方法可以:
答案 2 :(得分:4)
结果是有一个非常简单的解决方案,只需将FQCN(包装类'完全限定的类名)添加到记录器助手中:
public class MyLogger extends Logger {
private static final String FQCN = MyLogger.class.getName() + ".";
protected MyLogger(String name) {
super(name);
}
public void info(final Object msg) {
super.log(FQCN, Level.INFO, msg, null);
}
//etc...
在你的工人阶级,你只需:
public class MyClass {
private static final Logger LOG = MyLogger.getLogger();
private void test()
{
LOG.info("test");
}
}
答案 3 :(得分:2)
向KLE答案添加详细信息。 (抱歉,noob用户,不知道比创建单独答案更好的方法)
您可以将其放在MDC上下文中,而不是将行号粘贴到消息中。请参阅org.apache.log4j.MDC
例如:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
int l = stackTraceElement.getLineNumber();
MDC.put("myLineNumber", l);
允许用户在他们的log4j配置文件中使用mylineNumber
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="Line(%X{myLineNumber})- %m%n"/>
</layout>
注意:允许用户控制行号在消息中的显示位置和方式。但是,由于获取堆栈跟踪的成本非常高,因此您仍需要找到关闭该功能的方法。
答案 4 :(得分:2)
对于Log4j2,答案完全由使用logger包装器提供,如Example Usage of a Generated Logger Wrapper下的Log4j2手册中所述。可以简单地生成(使用那里所示的org.apache.logging.log4j.core.tools.Generate $ ExtendedLogger工具)具有单个STUB级别的记录器包装器,然后调整它以创建模仿logIfEnabled使用的自定义日志记录方法(FQCN,LEVEL,Marker,message,Throwable) - 可能忽略STUB级别并使用常规级别 - 然后根据需要删除或注释掉STUB级别及其方法)。为此,FormattedMessage可能会有所帮助。
然后,通过使用配置中给出的PatternLayout中的%l位置转换模式元素,或者更具体地使用%L,可以轻松地将源行尽管显示为完整位置信息的一部分。行号和/或%M方法转换。
答案 5 :(得分:1)
这不是开箱即用的。在这种情况下,您可以做的最好的事情是在调用者中创建记录器并将其传递给util方法。这样,您至少可以了解来电的来源。
答案 6 :(得分:1)
如果您有自己的日志记录实用程序方法,则可以将linenumber和filename添加到日志记录参数列表中并使用cpp路由。即,在编译之前预处理您的源代码以替换_ LINE _和_ FILE _等标记。作为一个额外的奖励,这不会像在运行时弄清楚那样多的资源。
答案 7 :(得分:0)
也许您可以使用堆栈跟踪元素实现日志帮助函数,获取行号,并使用带有某些特定注释的方法绕过框架,例如,
public @interface SkipFrame {}
// helper function
@SkipFrame // not necessary on the concrete log function
void log(String... message) {
// getStackTrace()...
int callerDepth = 2; // a constant number depends on implementation
StackTraceElement callerElement = null;
for (StackTraceElement e: stackTrace) {
String className, methodName = e.getClassName, getMethodName()...
Class callClass = Class.forName(className);
// since there maybe several methods with the same name
// here skip those overloaded methods
Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName);
SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class);
if (skipFrame != null)
continue; // skip this stack trace element
if (callerDepth-- == 0) {
callerElement = e;
break;
}
}
assert callerDepth == 0;
assert callerElement != null;
Log4j.info(callerElement.getLineNumber()... + "message... ");
}
@SkipFrame
void logSendMail(Mail mailObject) {
log("Send mail " + mailObject.getSubject());
}
因此,如果帮助函数是嵌套的,或者有更多利用的辅助函数,只需在所有这些函数上标记SkipFrame注释,您就会得到正确的源代码行。