log4j2

简介

log4j2 是 log4j 1.x 的升级版,2015 年 5 月,Apache 宣布 log4j1.x 停止更新, 最后版本为 1.2.17。

log4j2 参考了 logback 的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:

  • 异常处理:在 logback 中,Appender 中的异常不会被应用感知到,但是在 log4j2 中,提供了一些异常处理机制
  • 性能提升:log4j2 相较于 log4j 1 和 logback 都具有很明显的性能提升
  • 自动重载配置:参考 logback 设计,支持自动刷新参数,可以动态修改日志级别,而不重启应用
  • 无垃圾机制: 大部分情况下,都可以使用其无垃圾机制,避免频繁的日志收集导致的 jvm gc

版本

在官网的下载页 (https://logging.apache.org/log4j/2.x/download.html),有提示支持各 jdk 大版本的最后 log4j 版本

image-20210112144330710

案例

创建项目

创建一个空的 maven 项目,结构如图:
image-20210112103447378

配置项目

添加依赖

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>${log4j2.version}</version>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>

我这里使用的版本为 2.14.0

添加 log4j2 配置

在 src/main/resources 下,创建文件 log4j2.xml,注意名字不要变

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error" monitorinterval="5">
	<!-- 先定义所有的 appender -->
	<appenders>
		<!-- 这个输出控制台的配置 -->
		<Console name="Console" target="SYSTEM_OUT">
			<!--             控制台只输出 level 及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
			<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
			<!--             这个都知道是输出日志的格式 -->
			<PatternLayout pattern="%d{HH🇲🇲ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
		</Console>
	<span class="hljs-comment">&lt;!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 --&gt;</span>
	<span class="hljs-comment">&lt;!--append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">File</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"log"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/log4j2.log"</span> <span class="hljs-attr">append</span>=<span class="hljs-string">"false"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{HH🇲🇲ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"</span>/&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">File</span>&gt;</span>

	<span class="hljs-comment">&lt;!--添加过滤器ThresholdFilter,可以有选择的输出某个级别以上的类别  onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝  --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">File</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ERROR"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/error.log"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">ThresholdFilter</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">onMatch</span>=<span class="hljs-string">"ACCEPT"</span> <span class="hljs-attr">onMismatch</span>=<span class="hljs-string">"DENY"</span>/&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{yyyy.MM.dd 'at' HH🇲🇲ss z} %-5level %class{36} %L %M - %msg%xEx%n"</span>/&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">File</span>&gt;</span>

	<span class="hljs-comment">&lt;!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">RollingFile</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"RollingFile"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/web.log"</span>
	             <span class="hljs-attr">filePattern</span>=<span class="hljs-string">"logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{yyyy-MM-dd 'at' HH🇲🇲ss z} %-5level %class{36} %L %M - %msg%xEx%n"</span>/&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">SizeBasedTriggeringPolicy</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"2MB"</span>/&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">RollingFile</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">appenders</span>&gt;</span>


<span class="hljs-comment">&lt;!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">loggers</span>&gt;</span>
	<span class="hljs-tag">&lt;<span class="hljs-name">root</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"trace"</span>&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"RollingFile"</span>/&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"Console"</span>/&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"ERROR"</span>/&gt;</span>
		<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"log"</span>/&gt;</span>
	<span class="hljs-tag">&lt;/<span class="hljs-name">root</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">loggers</span>&gt;</span>

</configuration>

先这样使用,后面再做详解

创建测试类

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2Test {
private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++) {
        <span class="hljs-comment">// trace level</span>
        logger.trace(<span class="hljs-string">"trace log ..."</span>);
        <span class="hljs-comment">// debug level</span>
        logger.debug(<span class="hljs-string">"debug log ..."</span>);
        <span class="hljs-comment">// info level</span>
        logger.info(<span class="hljs-string">"info log ..."</span>);
        <span class="hljs-comment">// error level</span>
        logger.error(<span class="hljs-string">"error log ..."</span>);
    }
}

}

运行测试

运行项目,控制台输出:
image-20210112103819043

用法

简介

log4j 2.x 版本不再支持像 1.x 中的.properties 后缀的文件配置方式,2.x 版本常用.xml 后缀的文件进行配置,除此之外还包含.json 和.jsn 配置文件

log4j2 虽然采用 xml 风格进行配置,依然包含三个组件, 分别是 Logger(记录器)、Appender(输出目的地)、Layout(日志布局)。

配置文件

根节点

根节点 Configuration 有两个属性:

  • status: 指定 log4j2 本身的日志级别, 包括 "trace", "debug", "info", "warn", "error" and "fatal"
  • monitorinterval: log4j 2.x 的新特点,自动重载配置。指定自动重新配置的监测间隔时间,单位是 s, 最小是 5s

有两个子节点 (表明可以定义多个 Appender 和 Logger)

  • Appenders: 输出源,用于定义日志输出的地方。
  • Loggers: 日志器

Appenders 节点

详见官网: https://logging.apache.org/log4j/2.x/manual/appenders.html

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

  • Console:控制台输出源是将日志打印到控制台上,开发的时候一般都会配置,以便调试
  • File:文件输出源,用于将日志写入到指定的文件,需要配置输入到哪个位置(例如:D:/logs/mylog.log)
  • 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)

常见的有三种子节点:

  • Console: 定义输出到控制台的 Appender
  • File: 定义输出到指定位置的文件的 Appender
  • RollingFile: 定义超过指定大小自动删除旧的创建新的的 Appender

通过在子节点中加入进行日志布局

配置 含义
%m 输出代码指定信息,如 info(“message”), 输出 message
%c 输出所属类的全名, Num 为类名输出的范围 如 "com.sun.aaa.classB",%c{2} 将使日志输出输出范围为:aaa.classB
%d 输出日志时间其格式为 可指定格式 如 %d{HH🇲🇲ss.SSS}, 输出到毫秒
%-5level 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补齐空格
%L 输出行号
%l 输出日志事件发生位置,包括类名、方法名、文件名、行数
%F 输出所在的类文件名,如 Log4j2Test.java
%M 输出所在方法名
%t 输出产生该日志事件的线程名
%r 输出从启动到显示该条日志信息所耗费的时间 (ms)
%logger 输出 logger 名称。Root Logger 因为没有名称,所以不会输出
%p 输出日志的优先级,即 FATAL ,ERROR 等
%n 换行符

Loggers 节点

日志器分根日志器 Root 和自定义日志器。
当根据日志名字 (Appender) 获取不到指定的日志器时,就使用 Root 作为默认的日志器。

  • Root: 用来指定项目的根日志,如果没有单独指定 Logger,那么就会默认使用该 Root 日志输出
  • Logger: 用来单独指定日志的形式,需要指定:
    • Logger 的名称 (name)。对于命名可以以包名作为日志的名字,不同的包配置不同的级别等
    • 日志级别 (level): TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF,不指定时 level 默认为 ERROR
    • 相加性 (additivity):是否同时输出 log 到父类的 appender,缺省为 true。 对于一般的日志器(如 Console、File、RollingRandomAccessFile),一般需要配置一个或多个输出源 AppenderRef;