文章目录
一、为什么要用log4j2
1、性能大幅提升
log4j2 相较于log4j 和 logback 都具有明显的性能提升,约有18倍性能提升。
2、自动重载配置(热加载)
log4j2参考了logback的设计,会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
3、无垃圾机制
log4j2 在大部分情况下,都可以使用其设计的一套无垃圾机制【对象重用、内存缓冲】,避免频繁的日志收集导致的 jvm gc
二、如何从0接入log4j2
1、maven配置
1.1 方法一(适合spring)
<T> T <!--log4j2核心包-log4j2的日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.0</version>
</dependency>
<!--log4j2核心包-log4j2的日志核心逻辑-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.0</version>
</dependency>
<!-- Web项目需添加 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.17.0</version>
</dependency>
<!--使用log4j2 的适配器用于与slf4j保持桥接绑定--->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.0</version>
</dependency>
<!-- slf4j核心包-slf4j的日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
方法二(适合springBoot)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 使用 上面的log4j2 库,从 parent 中移除 logging 这个库->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
2、log4j2.xml配置
添加 log4j2.xml 配置文件到 resource 目录下
2.1 log4j2.xml 示例
网上参考的很多
<?xml version="1.0" encoding="UTF-8"?>
<!--
status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出
monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。此处表示每隔600秒重读一次配置文件
-->
<Configuration status="OFF" monitorInterval="600">
<!--日志级别:TRACE < DEBUG < INFO < WARN < ERROR < FATAL-->
<!--如果设置为WARN,则低于WARN的信息都不会输出-->
<Properties>
<!-- 配置日志文件输出目录,此处为项目根目录下的logs文件夹 -->
<Property name="LOG_HOME">/app/applogs</Property>
<!-- 配置日志文件名称前缀 -->
<Property name="LOG_FILE_NAME">cu-upcc-ccc1-provider</Property>
<!-- 配置输出日志级别-通用日志 -->
<Property name="LOG_LEVEL">INFO</Property>
<!-- 配置输出日志级别-重要日志1 -->
<Property name="LOG_LEVEL_V1">DEBUG</Property>
<!-- 配置日志输出源appender的名称 -->
<Property name="APPENDER_NAME">APP-PROVIDER</Property>
<!-- 配置子Logger是否继承父Logger的输出源,true-是,false-否,默认true -->
<Property name="LOGGER_ADDITIVITY">true</Property>
<!-- 触发日志文件分割的时间间隔,此配置必须和filePattern协调, 如果后者是精确到HH, 则此配置项单位是小时,若后者精确到dd, 则此配置项单位是天-->
<Property name="POLICY_TIME_BASED_INTERVAL">1</Property>
<!-- 日志清理规则 从basePath起清理日志文件的目录层级深度 -->
<Property name="DELETE_MAX_DEPTH">1</Property>
<!-- 日志清理规则 文件名称匹配规则 -->
<Property name="DELETE_IF_FILE_NAME">*.log-*</Property>
<!-- 日志清理规则 归档日志文件存在时长匹配规则,此配置项必须和filePattern协调, 后者是精确到HH, 这里就要写成xH, xd就不起作用, 另外, 数字最好>2, 否则可能造成删除的时候, 最近的文件还处于被占用状态,导致删除不成功! -->
<Property name="DELETE_IF_LAST_MODIFIED_AGE">24H</Property>
</Properties>
<Appenders>
<!--这个输出控制台的配置-->
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="${LOG_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
<!--日志输出的格式-->
<PatternLayout pattern="|%d{yyyy-MM-dd HH:mm:ss.SSS}|%5p|%5t|%4c:%L|%X{PtxId}|%X{clientNodeName}|%X{serverNodeName}|%X{clientSysName}|%X{serverSysName}|%X{txId}|%X{logType}|%m|%n" />
</Console>
<RollingRandomAccessFile name="${APPENDER_NAME}" fileName="${LOG_HOME}/${LOG_FILE_NAME}.log"
filePattern="${LOG_HOME}/${LOG_FILE_NAME}.log-%d{yyyyMMddHH}">
<PatternLayout pattern="|%d{yyyy-MM-dd HH:mm:ss.SSS}|%5p|%5t|%4c:%L|%X{PtxId}|%X{clientNodeName}|%X{serverNodeName}|%X{clientSysName}|%X{serverSysName}|%X{txId}|%X{logType}|%m|%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="${POLICY_TIME_BASED_INTERVAL}"/>
<!--<SizeBasedTriggeringPolicy size="50MB"/>-->
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="${LOG_HOME}/" maxDepth="${DELETE_MAX_DEPTH}">
<IfFileName glob="${DELETE_IF_FILE_NAME}" />
<!--!Note: 这里的age必须和filePattern协调, 后者是精确到HH, 这里就要写成xH, xd就不起作用
另外, 数字最好>2, 否则可能造成删除的时候, 最近的文件还处于被占用状态,导致删除不成功!-->
<IfLastModified age="${DELETE_IF_LAST_MODIFIED_AGE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<!-- alarm plugins -->
<AlarmLog4j2Appender name="AlarmLog4j2Appender" enableApollo="true" apNameSpace="cu-upcc-ccc1-provider">
<PatternLayout pattern="|%d{yyyy-MM-dd HH:mm:ss.SSS}|%m" />
</AlarmLog4j2Appender>
</Appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<Loggers>
<Logger name="com.epay.cu1.upcc1.ccc1.provider" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="com.epay.ts.lacs.log" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="com.epay.ts.piss" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="org.springframework" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="com.ctrip.framework" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="org.apache" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="feign.Logger" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<Logger name="com.alibaba.druid" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="${APPENDER_NAME}"/>
</Logger>
<logger name="com.broad.assessment.business.dao" level="${LOG_LEVEL_V1}">
<AppenderRef ref="${APPENDER_NAME}"/>
</logger>
<logger name="org.mybatis" level="${LOG_LEVEL_V1}">
<AppenderRef ref="${APPENDER_NAME}"/>
</logger>
<logger name="com.unicompayment.basictools" level="${LOG_LEVEL_V1}">
<AppenderRef ref="${APPENDER_NAME}"/>
</logger>
<!-- alarm plugins -->
<Logger name="com.epay.cu1.upcc1.ccc1.provider" level="${LOG_LEVEL}" additivity="${LOGGER_ADDITIVITY}">
<AppenderRef ref="AlarmLog4j2Appender"/>
</Logger>
<Root level="${LOG_LEVEL}">
<Appender-Ref ref="Console"/>
</Root>
</Loggers>
</Configuration>
2.2 log4j2.xml 主要模块功能说明
2.2.1 Configuration(日志配置管理容器)
可以理解为该日志配置的最外层一个大容器,包含了所有的Appenders、上下文范围内的过滤器、LoggerConfigs以及StrSubstitutor.的引用。主要参数介绍:
status-日志级别//<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
monitorInterval-热加载配置的间隔时间//Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数
2.2.2 Loggers 日志记录器
Loggers 是通过调用LogManager.getLogger方法获得的。
默认,子Logger会继承父Logger的appender,也就是说子Logger会在父Logger的appender里输出.
主要参数介绍:
additivity-叠加性(继承性)//若是additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出
2.2.3 Appender(输出终端)
不同的输出位置被称为Appender。
1)可以是console、RollingFile文件、
远程socket服务器、Apache Flume、JMS以及远程 UNIX 系统日志守护进程;
2)一个Logger可以绑定多个不同的Appender(输出终端);
3)对LoggerConfig B来说,它的父级为A,A的父级为root。
如果在root中定义了一个Appender为console,
那么所有启用了的logging request都会在console中打印出来;
主要子元素及参数介绍:
####### 2.2.3.1 Filters-过滤器
<Filters>
<!--限制日志级别再debug及以上,在info以下-->
<ThresholdFilter level="debug"/>
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
/* 过滤器都有两个属性onMatch和onMismatch,指定匹配过滤器和不匹配过滤器时候执行的操作,有三个取值ACCEPT,DENY,NEUTRAL;
与防火墙过滤的规则相似,log4j2的过滤器也将返回三类状态:Accept(接受), Deny(拒绝) 或Neutral(中立)*/
/*
Accept意味着不用再调用其他过滤器了,这个LogEvent将被执行
Deny意味着马上忽略这个event,并将此event的控制权交还给过滤器的调用者
Neutral则意味着这个event应该传递给别的过滤器,如果再没有别的过滤器可以传递了,那么就由现在这个过滤器来处理
*/
</Filters>
####### 2.2.3.2 PatternLayout -输出日志的格式
<!--日志格式-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
/*定义输出的格式:
%r 指的是程序运行至输出这句话所经过的时间(以毫秒为单位);
%t 指的是发起这一log request的线程;
%c 指的是log的level;
%m 指的是log request语句携带的message。
%n 为换行符。
*/
####### 2.2.3.3 Policies-策略
<Policies>
<!--每隔一天转存-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--文件大小-->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
/*
TimeBasedTriggeringPolicy -执行时间触发(转存)策略;
SizeBasedTriggeringPolicy -文件大小触发(转存)策略;
*/
####### 2.2.3.4 DefaultRolloverStrategy
max="10"
/*DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了10
*/
三、log4j2 原理(源码实现)
1、启动时做了什么
LogManager类是启动日志容器的入口。
从源码可以看出,这个类中的有一个静态块是关键。
1)调用者只要引包jar包,启动项目时,就自动加载这个logManager中的static静态块,
去初始化日志上下文工厂LoggerContextFactory(默认的配置文件是Log4jContextFactory)
及日志上下文容器LoggerContext
2)log4j2.xml被加载—初始化加载日志容器的过程中就会加载的log4j2.xml配置文件,根据log4j2.xml内容,初始化日志管理需要的一些单例对象,以供后面日志写入时使用