Log4j2学习
一、日志门面和实现
三种门面:jcl(commons logging)、slf4j、log4j-api
具体实现和桥接关系如下图
log4j2的api和实现
二、运用
1.获取logger
getLogger()方法可接受不同参数,参数最终定义为该logger的名称,与配置文件中<logger/>标签name属性对应
LogManager.getLogger();
1.1.getLogger()调用流程
1.2.格式化输出
格式化方式与Java的Formatter语法一致;getFormatterLogger()与getLogger()区别在于两者内部使用不同的消息工厂,前者使用StringFormatterMessageFactory
LogManager.getFormatterLogger();
1.3.LogBuilder
为了解决传统中容易理解错误的语法,添加了构建器的模式到API中。LogBuilder允许在事件被记录之前将Marker、Throwable或Location添加到事件中。
private static Logger logger = LogManager.getLogger();
logger.atInfo().withMarker(marker).withLocation().withThrowable(exception).log("Login for user {} failed", userId);
LogBuilder最明显的优势:提供位置信息。使用无参数的定位方法时捕获位置信息的开销比必须在需要时计算位置信息要好得多。Log4j2可以简单地在固定索引处请求堆栈跟踪条目,而不必遍历堆栈跟踪来确定调用类。当然,如果布局不使用位置信息,则会导致性能降低。
2、配置文件
Log4j2的配置可以通过以下方式之一:
1、通过配置文件(最常用)
2、通过继承ConfigurationFactory和实现Configuration
3、通过调用配置界面中公开的API,以编程方式将组件添加到默认配置。
4、通过调用内部Logger类上的方法以编程方式。
xml配置文件结构:
一些简单的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- Configuration后面的status,这个用于设置框架内部的信息输出,可以不设置 -->
<!-- monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数,最小间隔5秒 -->
<configuration monitorInterval="5" status="debug">
<!-- 变量配置 -->
<Properties>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%level:级别,%logger:logger名称,%X{ctx:xx}:上下文map查找(%X{xx}),%location:位置信息,%msg:日志消息,%n:换行符;标识符可简写%date=%d -->
<!-- 上下文map设置:ThreadContext.put() -->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] [%level] [%X{loginId}] [%logger{36}] [%location] - %msg%n" />
<!-- 定义日志存储的路径 -->
<property name="FILE_PATH" value="/home/logs" />
</Properties>
<appenders>
<!-- 输出到控制台 target默认为SYSTEM_OUT,还可以是SYSTEM_ERR -->
<console name="Console" target="SYSTEM_OUT">
<!-- 输出日志的格式 -->
<PatternLayout pattern="${LOG_PATTERN}"/>
<!-- Filters过滤器集合,ThresholdFilter:日志级别过滤器 -->
<!-- 控制台只输出info及以上级别的信息,以下的拒绝 -->
<!-- onMatch="ACCEPT" 表示匹配该级别及以上 -->
<!-- onMatch="DENY" 表示不匹配该级别及以上 -->
<!-- onMatch="NEUTRAL" 表示该级别及以上的,由下一个filter处理,如果当前是最后一个,则表示匹配该级别及以上 -->
<!-- onMismatch="ACCEPT" 表示匹配该级别以下 -->
<!-- onMismatch="DENY" 表示不匹配该级别以下的 -->
<!-- onMismatch="NEUTRAL" 表示该级别及以下的,由下一个filter处理,如果当前是最后一个,则不匹配该级别以下 -->
<Filters>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</console>
<!-- 打印到普通文件 -->
<!-- appden:两次启动,日志追加到info.log -->
<File name="FileInfo" fileName="${FILE_PATH}/File/info.log" append="true">
<PatternLayout pattern="${LOG_PATTERN}" charset="uft-8" />
</File>
<!-- 有滚动功能的文件输出 -->
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/RollingFile/info.log" filePattern="${FILE_PATH}/RollingFile/INFO-%d{yyyy-MM-dd}_%i.log.gz">
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<!-- 触发滚动机制的策略 -->
<Policies>
<!-- modulate为true根据自然时间触发,为false根据启动时间触发,interval属性用来指定相隔多久滚动一次,默认根据filePattern中的时间戳中最小时间单位 -->
<!-- 例:interval="4" modulate="true" 最小单位为小时,启动时间为1:00,那出发时间为4-8-12 -->
<!-- 例:interval="4" modulate="false" 最小单位为小时,启动时间为1:00,那出发时间为5-9-13 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<!-- 根据info.log文件大小触发滚动,达到10MB后滚动 -->
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认同一文件夹下最多7个文件,超过7个开始覆盖 -->
<!-- max:文件下标最大数,min:文件下标最小数, compressionLevel:压缩级别,0-9依次压缩时间增长压缩级别越高,仅针对zip文件 -->
<DefaultRolloverStrategy max="15" compressionLevel="1" min="1" >
<!-- basePath:日志存放路径,maxDepth:路径深度,当前目录下为1 -->
<Delete basePath="${FILE_PATH}" maxDepth="2">
<!-- glob:需要匹配的文件 -->
<IfFileName glob="*.log" />
<!-- 保留120天以内的日志,age:与filePattern的最小时间单位相关 -->
<IfLastModified age="120d" />
</Delete>
</DefaultRolloverStrategy>
<!-- 在一个时间段内最大文件数10 -->
<DirectWriteRolloverStrategy maxFiles="10" />
</RollingFile>
<!-- 滚动随机访问文件,效率比RollingFile高 -->
<RollingRandomAccessFile name="RollingRandomAccessFileInfo" fileName="${FILE_PATH}/RollingRandomAccessFile/info.log" filePattern="${FILE_PATH}/RollingRandomAccessFile/INFO-%d{yyyy-MM-dd}_%i.log.gz">
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="15" compressionLevel="1" min="1" >
<Delete basePath="${FILE_PATH}" maxDepth="2">
<IfFileName glob="*.log" />
<IfLastModified age="120d" />
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
</appenders>
<!-- loggers中标签用于收集日志,只有定义了logger或root并引入相应appender,日志才能被记录,appender也才会生效 -->
<loggers>
<!-- Logger节点用来单独指定日志的形式,根据logger的名称来收集(getlogger()的名称与logger标签的name属性一致),logger收集后,root就不会收集 -->
<!-- level:收集日志级别,additivity:可追加性(false:该Logger只会在自己定义的appender里输出,而不会在父root中再次输出,即只输出一次)。不定义AppenderRef,当前logger只收集不输出,可做屏蔽功能 -->
<logger name="org.mybatis" level="info" additivity="false" includeLocation="false">
<AppenderRef ref="Console"/>
</logger>
<!-- 异步日志,需要引入disruptor的依赖;includeLocation:地址信息 -->
<Asynclogger name="com.xxx" level="all" additivity="false" includeLocation="false">
<appender-ref ref="RollingRandomAccessFileInfo"/>
</Asynclogger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="FileInfo"/>
<appender-ref ref="RollingFileInfo"/>
</root>
</loggers>
</configuration>
3、异步日志
3.1.异步实现方式
这种方式会使所有的日志都以异步实现
方式一:JVM启动参数(boot.ini)加上“-DLog4jContextSelector=
org.apache.logging.log4j.core.async.AsyncLoggerContextSelector”
使用时需要添加异步依赖
方式二:System.setProperty(“LOG4J_CONTEXT_SELECTOR”, “org.apache.logging.log4j.core.async.AsyncLoggerContextSelector”);
方式三:在log4j2.component.properties配置系统变量LOG4J_CONTEXT_SELECTOR=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
3.2.异步AsyncAppdener
AsyncAppender接受对其他Appender的引用,并使LogEvents在单独的线程上写入它们
4种队列类型
ArrayBlockingQueue:默认实现方式,有锁异步
DisruptorBlockingQueue:环形队列,循环写入,不用GC回收,无锁
JCToolsBlockingQueue:使用JCTools,提供非阻塞并发数据结构,使用MPSC有界无锁队列(多生产单一消费)
LinkedTransferQueue:Java7实现的LinkedTransferQueue
<Async name="com.bar" includeLocatio