您的位置:首页 > 编程语言 > Java开发

Java日志框架

2017-12-20 17:11 309 查看
Java日志框架的行号是怎么获取到的?

用过很多框架,如log4j,logback等。每种框架都支持在日志中输出行号,那框架是如何得到行号的呢?

以logback来说。

在发起logger.info("xxx");调用时,会build一个LoggingEvent对象。在此对象中有获取当前当前调用栈的方法:

1     public StackTraceElement[] getCallerData() {
2         if (callerDataArray == null) {
3             callerDataArray = CallerData
4                             .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
5         }
6         return callerDataArray;
7     }


在这里,通过new Threwable(),然后遍历StackTraceElement来获取当前调用有效栈。因为堆栈是从当前点往外逐层叠加的,那遍历栈时判断栈归属类是否为ch.qos.logback.classic.Logger.class.getName() || org.apache.log4j.Category || org.slf4j.Logger || 是否在框架自身的package内。当跳出此循环后,即定位到日志输出行。在StackTraceElement中有lineNumber记录,直接读取即可得到此行号。此过程如下:

1     /**
2      * Extract caller data information as an array based on a Throwable passed as
3      * parameter
4      */
5     public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, List<String> frameworkPackageList) {
6         if (t == null) {
7             return null;
8         }
9
10         StackTraceElement[] steArray = t.getStackTrace();
11         StackTraceElement[] callerDataArray;
12
13         int found = LINE_NA;
14         for (int i = 0; i < steArray.length; i++) {
15             if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) {
16                 // the caller is assumed to be the next stack frame, hence the +1.
17                 found = i + 1;
18             } else {
19                 if (found != LINE_NA) {
20                     break;
21                 }
22             }
23         }
24
25         // we failed to extract caller data
26         if (found == LINE_NA) {
27             return EMPTY_CALLER_DATA_ARRAY;
28         }
29
30         int availableDepth = steArray.length - found;
31         int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth;
32
33         callerDataArray = new StackTraceElement[desiredDepth];
34         for (int i = 0; i < desiredDepth; i++) {
35             callerDataArray[i] = steArray[found + i];
36         }
37         return callerDataArray;
38     }


在扩展日志框架,在内容中插入自定义的内容时,需要注意框架包声明。

因为是在扩展中调用具体日志框架实例,那日志输出的行号会定位到你的扩展实现中,此时丢失业务日志输出行号。从上述逻辑可以看出,在定位有效堆栈的时候,框架package是一个过滤条件,那将扩展所在的package加入框架package中即可解决此问题。

这样看来,日志要获取输出点行号是比较复杂的一个过程,日志输出如果过多,节点资源损耗预计比较可观。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: