目录

log4j2

概述

按照官方的说法,Log4j2 是 Log4j 和 Logback 的替代。

官网地址

按照官方的说法,Log4j2 大大优于 Log4j 和 Logback。

那么,Log4j2 相比于先问世的 Log4j 和 Logback,它具有哪些优势呢?

  1. Log4j2 旨在用作审计日志记录框架。 Log4j 1.x 和 Logback 都会在重新配置时丢失事件。 Log4j 2 不会。在 Logback 中,Appender 中的异常永远不会对应用程序可见。在 Log4j 中,可以将 Appender 配置为允许异常渗透到应用程序。
  2. Log4j2 在多线程场景中,异步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延迟低几个数量级。
  3. Log4j2 对于独立应用程序是无垃圾的,对于稳定状态日志记录期间的 Web 应用程序来说是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应时间性能。
  4. Log4j2 使用插件系统,通过添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常轻松地扩展框架,而无需对 Log4j 进行任何更改。
  5. 由于插件系统配置更简单。配置中的条目不需要指定类名。
  6. 支持自定义日志等级
  7. 支持 lambda 表达式
  8. 支持消息对象
  9. Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二进制数组,这使得它能被各种 Appender 使用。
  10. Syslog Appender 支持 TCP 和 UDP 并且支持 BSD 系统日志。
  11. Log4j2 利用 Java5 并发特性,尽量小粒度的使用锁,减少锁的开销。

log4j2基础示例

log4j、logback、log4j2都是一种日志具体实现框架,所以既可以单独使用也可以结合slf4j一起搭配使用

引入maven依赖

1
2
3
4
5
6
7
8
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
</dependency>

测试demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Demo {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
        logger.trace("trace");
        logger.debug("debug");
        logger.info("info");
        logger.warn("warn");
        logger.error("error");
        logger.fatal("fatal");
    }
}

在没有写配置文件之前输出:

1
2
3
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
16:02:08.196 [main] ERROR  - error
16:02:08.197 [main] FATAL  - fatal

log4j2配置文件详解

配置文件位置

配置文件的格式:log2j配置文件可以是xml格式的,也可以是json格式的, 配置文件的位置:log4j2默认会在classpath目录下寻找log4j2.xml、log4j.json、log4j.jsn等名称的文件,如果都没有找到,则会按默认配置输出,也就是输出到控制台,也可以对配置文件自定义位置(需要在web.xml中配置),一般放置在src/main/resources根目录下即可

指定配置文件位置:

  • java方式

    1
    2
    3
    4
    5
    6
    
    File file = new File("log4j2.xml");
    BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
    final ConfigurationSource source = new ConfigurationSource(in);
    Configurator.initialize(null, source);
      
    Logger logger = LogManager.getLogger("myLogger");
    
  • web.xml方式

    1
    2
    3
    4
    5
    6
    7
    8
    
    <context-param>  
        <param-name>log4jConfiguration</param-name>  
        <param-value>/WEB-INF/conf/log4j2.xml</param-value>  
    </context-param>  
        
    <listener>  
        <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>  
    </listener> 
    

示例程序

使用根控制器输出到控制台上

log4j2.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

日志管理器获取的是根日志器LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); 对应的log4j2.xml中的Loggers节点下的Root,因为该根日志器的level=“info”,所以输出的info级别以上的日志

配置说明

log4j2.xml文件的配置大致结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Configuration
	properties
	Appenders
		Console
			PatternLayout
		File
		RollingRandomAccessFile
		Async
	Loggers
		Logger
		Root
			AppenderRef

Configuration

为根节点,有status和monitorInterval等多个属性

status的值有 “trace”, “debug”, “info”, “warn”, “error” and “fatal”,用于控制log4j2日志框架本身的日志级别

monitorInterval,含义是每隔多少秒重新读取配置文件,可以不重启应用的情况下修改配置

Appenders

输出源,用于定义日志输出的地方

log4j2支持的输出源有很多,有控制台Console、文件File、RollingRandomAccessFile、MongoDB、Flume 等

Console

控制台输出源是将日志打印到控制台上,开发的时候一般都会配置,以便调试

File

文件输出源,用于将日志写入到指定的文件,需要配置输入到哪个位置

RollingRandomAccessFile

该输出源也是写入到文件,不同的是比File更加强大,可以指定当文件达到一定大小(如20MB)时,另起一个文件继续写入日志,另起一个文件就涉及到新文件的名字命名规则,因此需要配置文件命名规则 这种方式更加实用,因为你不可能一直往一个文件中写,如果一直写,文件过大,打开就会卡死,也不便于查找日志。

  • fileName 指定当前日志文件的位置和文件名称
  • filePattern 指定当发生Rolling时,文件的转移和重命名规则
  • SizeBasedTriggeringPolicy 指定当文件体积大于size指定的值时,触发Rolling
  • DefaultRolloverStrategy 指定最多保存的文件个数
  • TimeBasedTriggeringPolicy 这个配置需要和filePattern结合使用,注意filePattern中配置的文件重命名规则是${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i,最小的时间粒度是mm,即分钟
  • TimeBasedTriggeringPolicy指定的size是1,结合起来就是每1分钟生成一个新文件。如果改成%d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件

NoSql

MongoDb,输出到MongDb数据库中

Flume

输出到Apache Flume(Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。)

Async

异步,需要通过AppenderRef来指定要对哪种输出源进行异步(一般用于配置RollingRandomAccessFile)

PatternLayout

控制台或文件输出源(Console、File、RollingRandomAccessFile)都必须包含一个PatternLayout节点,用于指定输出文件的格式(如 日志输出的时间 文件 方法 行数 等格式),例如 pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  %d{HH:mm:ss.SSS} 表示输出到毫秒的时间
  %t 输出当前线程名称
  %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
  %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
  %msg 日志文本
  %n 换行

  其他常用的占位符有:
  %F 输出所在的类文件名,如Log4j2Test.java
  %L 输出行号
  %M 输出所在方法名
  %l 输出语句所在的行数, 包括类名、方法名、文件名、行数

Loggers

日志控制器,分根日志器Root和自定义日志器,当根据日志名字获取不到指定的日志器时就使用Root作为默认的日志器,自定义时需要指定每个Logger的名称name(对于命名可以以包名作为日志的名字,不同的包配置不同的级别等),日志级别level,相加性additivity, 对于一般的日志器(如Console、File、RollingRandomAccessFile)一般需要配置一个或多个输出源AppenderRef;

每个logger可以指定一个level(TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF),不指定时level默认为ERROR

additivity指定是否同时输出log到父类的appender,缺省为true。

properties

使用来定义常量,以便在其他配置的时候引用,该配置是可选的,例如定义日志的存放位置

结合slf4j使用

引入依赖:

 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
           <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.5</version>
            </dependency>
<!--
有了桥接包就不需要log4j-api了
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.5</version>
            </dependency>
-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.7</version>
            </dependency>
<!--
桥接包log4j-slf4j-impl起到适配的作用,因为市面上的日志实现互不兼容,日志框架slf4j要想适用于日志实现log4j2,就需要使用桥接包

slf4j使用LoggerFactory创建Logger进行日志打印,底层实际上调用了log4j-slf4j-impl的StaticLoggerBinder类创建一个Log4jLoggerFactory,然后再由这个Log4jLoggerFactory创建一个Log4j2的Logger对象,这个Logger封装在log4j-slf4j-impl中的Log4jLogger里面,最后将Log4jLogger返回给slf4j,每次slf4j进行日志打印,实际上是log4j-slf4j-impl中的Log4jLogger调用log4j2进行日志打印
如果没有 log4j-slf4j-impl桥接包,slf4j将创建一个对象,里面都是空方法,所以不会打印出日志
-->
            <!-- 桥接:告诉Slf4j使用Log4j2 -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>2.2</version>
            </dependency>

配置:

 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
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <properties>
        <property name="LOG_HOME">/tmp/com.eh</property>
        <property name="FILE_NAME">log4j2</property>
        <property name="log.sql.level">info</property>
    </properties>


    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
      </Console>
      <!--charset解决中文乱码-->
            <PatternLayout charset="utf-8" pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
        </Console>

        <!--注意引入系统变量使用两个$, $$-->
        <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log"
                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd_HH-mm}-%i.log">
            <PatternLayout charset="utf-8" pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <!--            <AppenderRef ref="RollingRandomAccessFile" />-->
        </Root>

        <Logger name="com.eh.dao" level="${log.sql.level}" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
    </Loggers>
</Configuration>

测试结果:

1
2
3
4
5
6
$ ls -R /tmp/com.eh
2020-10              ehcache              java8.log.2020-10-09 log4j2.log
aaa.xml              java8.log            java8.log.2020-10-10

/tmp/com.eh/2020-10:
log4j2-2020-10-13_16-37-1.log