关于Logger的结构

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

早天看到一遍关于JDK日志层级的文章,原文:

http://tutorials.jenkov.com/java-logging/logger-hierarchy.html

一路被这个文章讲的晕头转向。什么 forward,Passed,propagated等等。

JDK为什么把他设计的那么复杂,今天看一下JDK的代码。发现他测试的结果是对的,但讲法的差强人意。

wKiom1muWV3gwg80AAA3S19K2O0113.png-wh_50

日志结构却如上图树形所示,但Logger之间的父子关系是通过单独的索引结构表示的。如上图红色线表示logger(com.jenkoe.web)的父logger为rootlogger,在寻祖的过程中,中间结点若是虚结点,由直接跳过。这就是文件所说的:

 if you do create a Logger like this:

Logger logger  = Logger.getLogger("com.jenkov.web");

... and call getParent() method, you will get the Logger with the name "".

一旦中间的结点有值(由虚变实)需要更新其孩子结点的父索引(具体可看LogContext.addLocalLoader源码)。


再说Filter的传递问题,文件讲得太不好理解有两个点:

  1.  accept or reject 只发生在当前logger的filter上,若当前logger无filter,则accept。

  2. 若当前logger accept这个日志,则传播到上层logger,一直到根。

太晦涩,看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   public  void  log(LogRecord record) {
         if  (!isLoggable(record.getLevel())) {
             return ;
         }
         Filter theFilter = filter;
         if  (theFilter !=  null  && !theFilter.isLoggable(record)) {
             return ;
         }
 
         // Post the LogRecord to all our Handlers, and then to
         // our parents' handlers, all the way up the tree.
 
         Logger logger =  this ;
         while  (logger !=  null ) {
             final  Handler[] loggerHandlers = isSystemLogger
                 ? logger.accessCheckedHandlers()
                 : logger.getHandlers();
 
             for  (Handler handler : loggerHandlers) {
                 handler.publish(record);
             }
 
             final  boolean  useParentHdls = isSystemLogger
                 ? logger.useParentHandlers
                 : logger.getUseParentHandlers();
 
             if  (!useParentHdls) {
                 break ;
             }
 
             logger = isSystemLogger ? logger.parent : logger.getParent();
         }
     }

只用调用当前logger的filter,则所谓的传播是useParentHandler属性在起做作用,若为true,则递归调用祖辈logger的Handler,没有父logger的filter什么事。


在下面关于日志级别的问题,正如上面的代码。只在当前logger里进行了级别检查, 也没传播与否的说法。

文中:

Logger logger      = Logger.getLogger("");
Logger logger1     = Logger.getLogger("1");
Logger logger1_2   = Logger.getLogger("1.2");

logger1  .setLevel(Level.WARNING);
logger1_2.setLevel(Level.INFO);

logger     .info("msg:");
logger1    .info("msg: 1");
logger1_2  .info("msg: 1.2");

The result of this code is that the INFO message logged on the bottom Logger (named 1.2) is now logged, but it is still not propagated up the hierarchy. Well, it is, but the middle Logger filters it out, because the middle Loggerhas a log level of WARNING set. Thus, the message is not logged by the middle Logger nor propagated up the hierarchy.


不知道作者是否代码粘错了。这里没有propagated, 并不是middle Logger的level问题,而是因为  middle Logger和bottom Logger 都没有指定handler。最后全都传导到Root logger(它有默认的handler)。

若代码改成:

1
2
3
4
5
6
7
8
9
10
11
12
Logger logger      = Logger.getLogger( "" );
Logger logger1     = Logger.getLogger( "1" );
Logger logger1_2   = Logger.getLogger( "1.2" );
 
logger1    .addHandler( new  ConsoleHandler());
logger1_2  .addHandler( new  ConsoleHandler());
logger1  .setLevel(Level.WARNING);
logger1_2.setLevel(Level.INFO);
 
logger     .info( "msg:" );
logger1    .info( "msg: 1" );
logger1_2  .info( "msg: 1.2" );

结果就是:

1
2
3
4
5
6
7
8
九月 05, 2017 4:29:15 下午 java.util.logging.LogManager$RootLogger log
信息: msg:
九月 05, 2017 4:29:15 下午 guojje.SAPITest main
信息: msg: 1.2
九月 05, 2017 4:29:15 下午 guojje.SAPITest main
信息: msg: 1.2
九月 05, 2017 4:29:15 下午 guojje.SAPITest main
信息: msg: 1.2

所以很多费解的文章都是有问题的。


本文转自 anranran 51CTO博客,原文链接:http://blog.51cto.com/guojuanjun/1673879

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
Java
Error和Exception的关系以及区别
文章解释了Java中Error和Exception的区别和关系,其中Error表示严重的系统级问题,通常不可被程序捕获处理,而Exception表示可以被捕获和处理的异常,分为已检查和未检查两种类型。
37 0
|
6月前
|
监控 安全 API
orhanobut/logger - 强大的Android日志打印库
orhanobut/logger - 强大的Android日志打印库
290 1
|
6月前
|
云安全 安全 Java
那些曾经遇到过的logger
那些曾经遇到过的logger
76 0
|
Java Spring
logger的日志笔记
logger的日志笔记
67 0
Logger.error方法之打印错误异常的详细堆栈信息
Logger.error方法之打印错误异常的详细堆栈信息
733 0
|
Java
浅谈slf4j,logger中的{}功能
浅谈slf4j,logger中的{}功能
104 0
|
存储 运维 监控
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架(上)
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架
542 0
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架(上)
|
XML Java 程序员
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架(中)
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架(中)
478 0
最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架(中)
|
监控 Java API
打印 Logger 日志时,需不需要再封装一下工具类?
在开发过程中,打印日志是必不可少的,因为日志关乎于应用的问题排查、应用监控等。现在打印日志一般都是使用 slf4j,因为使用日志门面,有助于打印方式统一,即使后面更换日志框架,也非常方便。在 《Java 开发手册》中也有相关的规约。
377 0
|
Arthas SQL 监控
动态修改LOGGER日志级别
大多数情况下,我们会在打印日志时定义日志的LOGGER级别,用来控制输出的信息范围。 一方面,过多的输出会影响查看日志的效率,另一方面,过少的日志让问题定位变得困难。 但当线上出现问题时,线上容器通常定义在info级别,发生一些疑难问题时,光靠info级别的日志很难定位问题。 一个典型的场景:在一些需要打印MySQL语句的场景,如果你正在使用MyBatis框架,由于MyBaits中SQL语句是DEBUG级别的信息,通常在线上容器就没法看到。
658 1