转自:https://my.oschina.net/andylucc/blog/794867

摘要

我们在写日志的时候首先要获取logger,在每一个使用log4j的项目都有很多个地方要获取logger,这些logger是真实的被实例化的Logger对象,他们有可能被分散在无数不同的类中,日志体系结构讲的是这些logger对象是如何组织的,他们之间又有什么样的关系。

体系结构

我们举个具体的实例来看看,假设我的项目包结构如下:

项目结构

说明一下:com.flu.jdk包下面有两个类分别是LogTest1和LogTest2,然后在包com.flu包下面有一个LogTest3类,很显然,com.flu.jdk包是com.flu包的子包。假设我们在这三个类中分别通过LogManager.getLogger(xxx.class)获取三个logger实例,他们分别是logger1、logger2和logger3,我们将要讨论这三个logger的关系。

值得注意的是log4j的日志体系中,有一个特殊的日志对象叫做root(根),他是始终存在的,假设我们首先获取logger实例,log4j将构造下面这样一个图形(我不能把它叫做树):

当只有logger1的时候

当我们获取logger2实例的时候,这个图将变成:

当加入logger2日志实例时结构图

当我们获取logger3实例的时候,这个图又变了一个样,如下:

当加入logger3日志实例之后

仅仅才三个日志实例,图就搞的略复杂,可想log4j应用中,将会有无数的日志实例按照这个规律组成纷繁复杂的有向图结构,虽然看起来杂乱,但是又规律。那么问题来了,这样的结构有什么用呢?下一节我们将会看到这种结构对于日志配置继承的影响。

配置继承

log4j日志级别定义

在往下面看之前我们先来看看log4j对日志级别的定义:

public final static int OFF_INT = Integer.MAX_VALUE;
public final static int FATAL_INT = 50000;
public final static int ERROR_INT = 40000;
public final static int WARN_INT = 30000;
public final static int INFO_INT = 20000;
public final static int DEBUG_INT = 10000;
//public final static int FINE_INT = DEBUG_INT;
public final static int ALL_INT = Integer.MIN_VALUE;

很显然,log4j的日志级别有下面的关系:

OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL

log4j在写日志的时候只有当当前写日志的级别大于等于当前日志实例的配置级别的时候,日志写操作才生效,比如当前日志实例的配置级别为INFO,那么log.info会写成功,而log.debug则不会写。

日志写源码剖析

我们来看看一句简单的log.info("this is log message")的背后,先来看看一段源代码:

public void info(Object message) {
if(repository.isDisabled(Level.INFO_INT))
return;
if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
forcedLog(FQCN, Level.INFO, message, null);
}
public boolean isDisabled(int level) {
return thresholdInt > level;
}

首先看看当前写的日志级别是否被禁止的,默认的情况下thresholdInt为ALL,因此INFO的级别显然比ALL大,因此下面会继续看看INFO的级别是否大于等于当前日志实例生效的级别,this.getEffectiveLevel()获取的实例是什么呢?我们继续看看代码:

public Level getEffectiveLevel() {
for(Category c = this; c != null; c=c.parent) {
if(c.level != null)
return c.level;
}
return null; // If reached will cause an NullPointerException.
}

当前日志生效的级别逻辑为首先看看当前日志实例是否有配置级别,如果没有,那么就继续找当前日志实例的parent节点,按照上一节中所表述的,如果当前日志的日志级别没有配置,当找到root的日志级别,并根据root的日志级别来断定是否继续进行日志写。这里体现了日志级别的继承关系,其实不仅仅是日志级别,日志其他相关的配置也会基于这种继承的特性,比如appender组件等。

项目应用

了解Log4j的日志体系结构以及日志级别配置的继承特性之后,我们现在应该比较清楚项目中应该如何配置了。以Log4j.xml配置文件为例子,满足基本需求我们只需要配置root这个日志实例的日志级别即可,如下:

<root>
<level value="INFO" />
<appender-ref ref="CONSOLE" />
</root>

上面配置了root日志实例的日志级别为INFO,如果获取按照一定规范(当前类的权限定名作为日志实例名),那么我们可以保证所有的日志实例将继承root所配置的日志级别。

配置隔离

上面的配置略粗糙,假如我们想为不同的模块给予不同的配置怎么办呢?最常见的是业务日志与中间件日志,比如我们的业务业务包名为com.dianping.biz,而我们的rpc组件的包名字为com.dianping.pigeon,则我们可以使用下面方法给予不同的模块不同的配置:

<!--业务日志配置-->
<category name="com.dianping.biz">
<level value="INFO" />
<appender-ref ref="CONSOLE" />
</category> <!--pigeon组件日志配置-->
<category name="com.dianping.pigeon">
<level value="DEBUG" />
<appender-ref ref="CONSOLE" />
</category>

通过上面的配置,我们可以指定com.dianping.biz包下面所有类获取的logger都继承name为com.dianping.biz的日志配置,而com.dianping.pigeon包下面的所有类获取的logger都继承name为com.dianping.pigeon的日志篇日志。不过通常设计良好的中间件都定制了日志配置以确保中间件日志与业务日志隔离。

总结

昨天有个同事对log4j进行了一些分享,会上听的意犹未尽因此课下忍不住扒一扒log4j的内裤,日志作为java应用的一项重要内容,其不仅仅包括日志如何写、以什么格式写、以及日志写到哪里的问题,还包括性能、扩展性、分布式、日志实时分析等方面问题,本文在介绍log4j日志体系的基础之上稍微聊一下项目应用于配置隔离相关内容,如果读者有兴趣可以深入研究,必定收货满满。

当前日志生效的级别逻辑为首先看看当前日志实例是否有配置级别,如果没有,那么就继续找当前日志实例的parent节点,按照上一节中所表述的,如果当前日志的日志级别没有配置,当找到root的日志级别,并根据root的日志级别来断定是否继续进行日志写。这里体现了日志级别的继承关系,其实不仅仅是日志级别,日志其他相关的配置也会基于这种继承的特性,比如appender组件等。
这段话总结很好啊,不过这种继承关系也是有开关的,例如可以配置additivity=false,取消子log往父log的输出!

Log4j日志体系结构的更多相关文章

  1. Log4J日志配置详解

    一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使 ...

  2. log4j日志-liu

    log4j日志级别: http://michales003.iteye.com/blog/1160605 log4j日志配置详解: http://www.cnblogs.com/ITtangtang/ ...

  3. Log4j 输出的日志中时间比系统时间少了8小时的解决方法,log4j日志文件重复输出

    1. 第一个问题:时间少了8小时 Log4j 输出的日志中,时间比系统时间少了8小时,但是 eclipse 控制台输出的日志的时间却是对的. log4j配置如下: #all logger output ...

  4. 如何在JBoss WildFly 8 自定义log4j日志

    最近在 JBoss WildFly 8 下部署 Web应用,自定义的 log4j 日志不工作.console下无日志输出,用System.out.println都不输出内容到console. 原因是J ...

  5. paip.log4j 日志系统 参数以及最佳实践

    paip.log4j 日志系统 参数以及最佳实践   %d{yyyy-MM-dd HH:mm:ss} [thrd:%t] %5p   loger:%c   (%C.%M.%L)  - %m%n 201 ...

  6. commons-logging和Log4j 日志管理/log4j.properties配置详解

    commons-logging和Log4j 日志管理 (zz) 什么要用日志(Log)? 这个……就不必说了吧. 为什么不用System.out.println()? 功能太弱:不易于控制.如果暂时不 ...

  7. log4j日志输出到web项目指定文件夹

    感谢 eric2500 的这篇文章:http://www.cxyclub.cn/n/27860/ 摘要:尝试将log4j的文件日志输出到web工程制定目录,遇到了很多问题,最终在eric2500的指导 ...

  8. Hibernate4搭建Log4J日志管理(附Log4j.properties配置详解)

    1.首先加入slf4j的jar包,即slf4j-api-1.6.1.jar 在hibernate官网下载hibernate-release-4.2.2.Final.zip并解压,在hibernate- ...

  9. (转)log4j日志级别设置成DEBUG时输出Html代码等问题:

    log4j日志级别设置成DEBUG时输出Html代码等问题: 问题: log4j日志级别设置成DEBUG时会输出很多信息,包括一些Html代码 解决方案: log4j的控制是树形,所以在log4j.p ...

随机推荐

  1. IAR EWARM __iar_program_start, __iar_data_init3, __iar_copy_init3, __iar_zero_init3

    #include <stdint.h> // The type of a pointer into the init table. typedef void const * table_p ...

  2. [Office Web Apps]实现在线office文档预览

    摘要 在使用office web apps实现office文档在线预览的时候,需要注意的地方. web api web api作为owa在线预览服务回调的接口,这里面核心代码片段如下: using H ...

  3. 娓娓道来c指针 (7)指针运算

    (7)指针运算 在前几篇文章中,我们已经见过指针运算的使用场景,并多次使用指针运算来进行验证. 这里我们来特别地总结下.指针运算的本质含义. 在c语言中.如果p.pa.pb都是某种类型的指针,这种运算 ...

  4. AngularJS的增删改查、state嵌套案例,不涉及服务端

    本篇实践一个案例,大致是:左边有导航菜单,右边显示列表,并可对列表项编辑或删除,也可添加新的列表项.借此,可体会到:如何组织可扩展的AngualrJS文件结构,如何点击左侧菜单项右侧显示相应内容,an ...

  5. Unity3D实践系列10, Canvas画布的创建和使用

    Canvas是所有ui元素的父物体. 当添加一个Button类型的GameObject后,在"Hierarch"窗口中自动添加了一个Canvas,以及EventSystem. 在C ...

  6. 有关Delphi RTTI的一些文章

    Delphi RTTI 资料 Delphi 的RTTI机制浅探 Delphi XE的RTTI增强,动态Hook某些内部事件

  7. Spring Boot 2中对于CORS跨域访问的快速支持

    原文:https://www.jianshu.com/p/840b4f83c3b5 目前的程序开发,大部分都采用前后台分离.这样一来,就都会碰到跨域资源共享CORS的问题.Spring Boot 2 ...

  8. C#编程(五十一)----------链表

    原文链接: http://blog.csdn.net/shanyongxu/article/details/47024865 链表 LinkedList<T>集合类没有非泛型类的版本,它是 ...

  9. 【POI】maven引用POI的依赖,XSSFWorkbook依旧无法使用的问题。

    maven项目引用POI的jar: <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependenc ...

  10. Mysql 根据id查所有父级或子级

    mysql递归查询,mysql中从子类ID查询所有父类(做无限分类经常用到) 由于mysql 不支持类似 oracle with ...connect的 递归查询语法 之前一直以为类似的查询要么用存储 ...