Log4j升级到Log4j2

栏目: Java · 发布时间: 6年前

内容简介:Log4j升级到Log4j2

我们的系统一直用的是公司统一的日志类来打印日志,看了下内部实现,仅仅是对Slf4j门面+Log4j这套做了一些封装,加入了扩展的能力,各个模块在扩展点文件中配置日志路径、级别和包名,由 工具 类动态生成日志配置文件,由于一直没出问题所以没太关注,但是最近有个环境经常出现响应慢、Full GC频繁的问题,我在分析线程堆栈时发现了2个问题:一个是Log4j打日志有一定概率会出现死锁;另一个是有个缓存类调用非常频繁,很多线程都卡在打印日志那里,分析了下发现Log4j里每个Logger对象一把锁,同一个类里面打印日志的方法只能串行执行,效率非常低,经过研究对比了几个日志框架之后决定先把我们组的日志框架迁移到Log4j2,后面在部门内推广使用。

Log4j死锁问题

在Log4j 1.x版本中同步打印日志的时候,在高并发场景下会出现死锁导致cpu使用率异常飙升,其原因是当线程写日志的时候需要获取到Logger和Appender, org.apache.log4j.Logger 类继承于 org.apache.log4j.Category、Appender 继承于 org.apache.log4j.AppenderSkeleton 。通过Log4j 1.x中Category源码和Appender源码可以知道,当多线程并发时,可能会因为相互持有Logger和Appender发生死锁,而且同一个Logger对象只能串行打印日志,在高并发场景会容易引起性能问题。

看下打印日志的过程,调用Logger类打印日志的方法会调用Category的callAppenders()方法,这个方法会锁住Category对象,如下所示:

public void callAppenders(LoggingEvent event) {
    int writes = 0;

    for(Category c = this; c != null; c = c.parent) {
        synchronized(c) {
            if (c.aai != null) {
                writes += c.aai.appendLoopOnAppenders(event);
            }

            if (!c.additive) {
                break;
            }
        }
    }
}

上面这段代码最终会调用到AppenderSkeleton的doAppend()方法,可以看到doAppend()是个同步方法,也就是说打印一条日志要先后获取两个对象同步锁,在高并发的情况下,两次lock就很容易造成死锁,特别是在业务逻辑里面如果还有其他获取锁的逻辑死锁的概率就更高了。

public synchronized void doAppend(LoggingEvent event) {//省略 }

整个流程如下所示:

Log4j升级到Log4j2

Log4j2的优点

Log4j2是Log4j 1.x的升级版,与之前的版本Log4j 1.x相比、有重大的改进,主要变更点总结在下表:

优化点 说明
API实现分离 Log4j2将API与实现分离开来,原先Log4j 1.x版本只需引入log4j的依赖,Log4j2版本需要引入log4j-api和log4j-core两个依赖
自动加载新的配置 Log4j 2.x 和Logback都新增了自动加载日志配置文件的功能,而且配置发生改变时不会丢失任何日志事件,可以在Configuretion中设置monitorInterval设置检查配置文件变更的时间间隔
支持变量参数的占位符功能 Log4j2和Slf4j一样,支持支持变量参数的占位符功能,对字符串打印和输出做了很多优化,新的Markers和flow tracing可以帮助追踪程序工作流
性能较大提升 Log4j2相对于Log4j 1.x,具有更快的执行速度,由于重写了内部的实现,在某些特定的场景上面,甚至可以比之前的速度快上10倍,比如内部的消息队列采用ArrayBlockingQueue,相对于原始的ArrayList和锁操作来说,管程类的消息队列拥有更好地性能
异步性能 Asynchronous Loggers是Log4j2新增的日志器,异步日志器在其内部实现采用了LMAX Disruptor(一个无锁的线程间通信库)技术,Disruptor主要通过环形数组结构、元素位置定位和精巧的无锁设计(CAS)实现了在高并发情形下的高性能
死锁问题的解决 log4j 2.x中充分利用 Java 5的并发支持,并且以最低级别执行锁定

Slf4j静态绑定介绍

Slf4j作为一个日志门面,提供统一的打印日志接口,使得用户能够很方便的使用和切换想要的logging实现框架;静态绑定的实现是通过提供一个适配层,每个日志实现框架如果要适配Slf4j的api,那就提供一个适配Slf4j门面的适配包,适配包里都有一个包名和类名都相同的类 org.slf4j.impl.StaticLoggerBinder ,这个类的功能就是调用具体的日志库;SLF4j的使用者只要把适配包和slf4j-api的jar包放入classpath中,打印日志时LoggerFactory调用StaticLoggerBinder类中的getLoggerFactory()方法返回相应的ILoggerFactory实例,最后通过ILoggerFactory实例获取Logger具体实例,如下所示:

public interface LoggerFactoryBinder {

 public ILoggerFactory getLoggerFactory();

   public String getLoggerFactoryClassStr();

}
public interface ILoggerFactory {
 //适配包里的getLogger方法会返回一个适配了slf4j接口的Logger	
 public Logger getLogger(String name);

}

下面这个图片是官网提供的常见的一些log实现和绑定:

Log4j升级到Log4j2

需要注意的是Slf4j在classpath下只支持一个适配包(slf4j-simple、slf4j-log4j12、slf4j-jdk14、logback-classic、log4j-slf4j-impl等)。如果在classpath下存在多个桥接包,Slf4j会随机加载一个,把加载出来的适配包打印出来,如下所示:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/bjwanghanbing/.m2/repository/org/slf4j/slf4j-log4j12/1.7.6/slf4j-log4j12-1.7.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/bjwanghanbing/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.8.2/log4j-slf4j-impl-2.8.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

Log4j2与Slf4j集成

除了添加slf4j-api外,还需要添加与slf4j门面集成的适配器log4j-slf4j-impl,maven配置如下:

<!-- Slf4j门面 -->
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.6</version>
</dependency>
<!-- 桥接:告诉Slf4j使用Log4j2 -->
<dependency> 
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.8.2</version>
</dependency>
<dependency>
	<groupId>org.apache.logging.log4j</groupId>
	<artifactId>log4j-api</artifactId>
	<version>2.8.2</version>
</dependency>
<dependency>
	<groupId>org.apache.logging.log4j</groupId>
	<artifactId>log4j-core</artifactId>
	<version>2.8.2</version>
</dependency>
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

以上所述就是小编给大家介绍的《Log4j升级到Log4j2》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Looking For a Challenge

Looking For a Challenge

the University of Warsaw / the University of Warsaw / 2012-11 / 0

LOOKING FOR PROGRAMMING CHALLENGES? Then this book is for you! Whether it's the feeling of great satisfaction from solving a complex problem set, or the frustration of being unable to solve a task,......一起来看看 《Looking For a Challenge》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具