Java日志体系学习3--Log4j2

Log4j2

试验一下,引入依赖

 <!-- Log4j2 门面API-->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.13.1</version>
    </dependency>
    <!-- Log4j2 日志实现 -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.13.1</version>
    </dependency>

配置log4j配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
    <properties>
        <property name="LOG_HOME">D:/logs</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
        </Console>
        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n" />
        </File>
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n" />
        </RandomAccessFile>
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %msg%n" />
            <Policies>
                <OnStartupTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="1 MB" />
                <TimeBasedTriggeringPolicy />
            </Policies>
            <DefaultRolloverStrategy max="30" />
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="com.chen.log.Log4j2Test" level="info" additivity="false">
            <AppenderRef ref="rollingFile"/>
        </Logger>
        <Root level="trace">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

日志文件滚动更新

首先测试一下日志的滚动更新功能

public class Log4j2Test {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(Log4j2Test.class);
        for (int i=0; i<100000; i++) {
            logger.info("hello world");
        }
    }
}

最终输出日志文件如下:

 RollingFileAppender是Log4j2中的一种能够实现日志文件滚动更新(rollover)的Appender。

rollover的意思是当满足一定条件(如文件达到了指定的大小,达到了指定的时间)后,就重命名原日志文件进行归档,并生成新的日志文件用于log写入。如果还设置了一定时间内允许归档的日志文件的最大数量,将对过旧的日志文件进行删除操作。

<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %msg%n" />
            <Policies>
                <OnStartupTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="1 MB" />
                <TimeBasedTriggeringPolicy />
            </Policies>
            <DefaultRolloverStrategy max="30" />
        </RollingFile>

RollingFile实现日志文件滚动更新,依赖于TriggeringPolicy和RolloverStrategy。

其中,TriggeringPolicy为触发策略,其决定了何时触发日志文件的rollover,即When。

RolloverStrategy为滚动更新策略,其决定了当触发了日志文件的rollover时,如何进行文件的rollover,即How。

RollingFile的触发rollover的策略有CronTriggeringPolicy(Cron表达式触发)、OnStartupTriggeringPolicy(JVM启动时触发)、SizeBasedTriggeringPolicy(基于文件大小)、TimeBasedTriggeringPolicy(基于时间)、CompositeTriggeringPolicy(多个触发策略的混合,如同时基于文件大小和时间)。上述使用的是基于文件大小来触发。

DefaultRolloverStrategy指定了当触发rollover时的默认策略。

DefaultRolloverStrategy是Log4j2提供的默认的rollover策略,即使在log4j2.xml中没有显式指明,也相当于为RollingFile配置下添加了如下语句。DefaultRolloverStrategy默认的max为7。

<DefaultRolloverStrategy max="7"/>

 max参数指定了计数器的最大值。一旦计数器达到了最大值,过旧的文件将被删除。

max参数是与filePattern中的计数器%i配合起作用的,其具体作用方式与filePattern的配置密切相关。

1.如果filePattern中仅含有date/time pattern,每次rollover时,将用当前的日期和时间替换文件中的日期格式对文件进行重命名。max参数将不起作用。

如,filePattern="logs/app-%d{yyyy-MM-dd}.log"

2.如果filePattern中仅含有整数计数器(即%i),每次rollover时,文件重命名时的计数器将每次加1(初始值为1),若达到max的值,将删除旧的文件。

如,filePattern="logs/app-%i.log"

3.如果filePattern中既含有date/time pattern,又含有%i,每次rollover时,计数器将每次加1,若达到max的值,将删除旧的文件,直到data/time pattern不再符合,被替换为当前的日期和时间,计数器再从1开始。

如,filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log"

特别注意该种场景,由于在使用过程中一般配置日志文件带有日志以及序号,该种情况下文件的滚动策略是日期和序号配合使用,假定日文文件格式为yyyy-mm-dd-i max=30 则表示每天日志文件最多有30个 而不表示所有文件共有30个,后面还需要设置日志定时清除策略,否则日志会积累越来越多。

修改max值 代码测试如下:

<DefaultRolloverStrategy max="5" />

可知日志文件每分钟最多只会保存5个 ,仅表示每分钟最多5个。

删除日志文件

DefaultRolloverStrategy制定了默认的rollover策略,通过max参数可控制一定时间范围内归档的日志文件的最大个数。Log4j 2.5 引入了DeleteAction,使用户可以自己控制删除哪些文件,而不仅仅是通过DefaultRolloverStrategy的默认策略。

<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %msg%n" />
            <Policies>
                <OnStartupTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="1 MB" />
                <TimeBasedTriggeringPolicy />
            </Policies>
            <DefaultRolloverStrategy max= "5">
                <Delete basePath="${LOG_HOME}" maxDepth="2">
                    <IfFileName glob="*/myrollog-*.log" />
                    <IfLastModified age="1m" />
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

basePath指定了扫描开始路径,为baseDir文件夹。maxDepth指定了目录扫描深度,为2表示扫描baseDir文件夹及其子文件夹。IfFileName指定了文件名需满足的条件,IfLastModified指定了文件修改时间需要满足的条件。

再次测试发现程序只会保留1min之前的日志文件,其他日志都被清楚掉。

异步日志

首先测试一下同步和异步的性能差异

同步日志方式

<Logger name="com.chen.log.Log4j2Test" level="info" additivity="false">
     <AppenderRef ref="rollingFile"/>
</Logger>

​​​​​​​

 修改为异步方式

<AsyncLogger name="com.chen.log.Log4j2Test" level="info" additivity="false">
     <AppenderRef ref="rollingFile"/>
</AsyncLogger>

 使用同步方式5.4s 异步方式2.9 可知异步方式性能优势明显。

同步日志,即当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句。

同步日志原理

 Log4j2中日志输出的详细过程如下:

1.首先使用全局Filter对日志事件进行过滤。

Log4j2中的日志Level分为8个级别,优先级从高到低依次为OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。全局Filter的Level为ALL,表示允许输出所有级别的日志。logger.info()请求输出INFO级别的日志,通过。

2.使用Logger的Level对日志事件进行过滤。

Logger的Level为TRACE,表示允许输出TRACE级别及以上级别的日志。logger.info()请求输出INFO级别的日志,通过。

3.生成日志输出内容Message。

使用占位符的方式输出日志,输出语句为logger.info("increase {} from {} to {}", arg1, arg2, arg3)的形式,最终输出时{}占位符处的内容将用arg1,arg2,arg3的字符串填充。

log4j2用Object[]保存参数信息,在这一阶段会将Object[]转换为String[],生成含有输出模式串"increase {} from {} to {}"和参数数组String[]的Message,为后续日志格式化输出做准备。

4.生成LogEvent。

LogEvent中含有loggerName(日志的输出者),level(日志级别),timeMillis(日志的输出时间),message(日志输出内容),threadName(线程名称)等信息。

5.使用Logger配置的Filter对日志事件进行过滤。

Logger配置的Filter的Level为DEBUG,表示允许输出DEBUG及以上级别的日志。logger.info()请求输出INFO级别的日志,通过。

6.使用Logger对应的Appender配置的Filter对日志事件进行过滤。

Appender配置的Filter配置的INFO级别日志onMatch=ACCEPT,表示允许输出INFO级别的日志。logger.info()请求输出INFO级别的日志,通过。

7.判断是否需要触发rollover。

此步骤不是日志输出的必须步骤,如配置的Appender为无需进行rollover的Appender,则无此步骤

因为使用RollingFileAppender,且配置了基于文件大小的rollover触发策略,在此阶段会判断是否需要触发rollover。判断方式为当前的文件大小是否达到了指定的size,如果达到了,触发rollover操作。

8.PatternLayout对LogEvent进行格式化,生成可输出的字符串。

常见参数意义如下:

参数

意义

%d
日期格式,默认形式为2012-11-02 14:34:02,781
%p
日志级别
%c{1.}
%c表示Logger名字,{1.}表示精确度。若Logger名字为org.apache.commons.Foo,则输出o.a.c.Foo。
%t
处理LogEvent的线程的名字
%m
日志内容
%n
行分隔符。"\n"或"\r\n"。

在此步骤,PatternLayout将根据Pattern的模式,利用各种Converter对LogEvent的相关信息进行转换,最终拼接成可输出的日志字符串。

如DatePatternConverter对LogEvent的日志输出时间进行格式化转换;LevelPatternConverter对LogEvent的日志级别信息进行格式化转换;LoggerPatternConverter对LogEvent的Logger的名字进行格式化转换;MessagePatternConverter对LogEvent的日志输出内容进行格式化转换等。

经各种Converter转换后,LogEvent的信息被格式化为指定格式的字符串。

9.使用OutputStream,将日志输出到文件。

将日志字符串序列化为字节数组,使用字节流OutoutStream将日志输出到文件中。如果配置了immediateFlush为true,打开app.log就可观察到输出的日志了。 

异步日志原理

使用异步日志进行输出时,日志输出语句与业务逻辑语句并不是在同一个线程中运行,而是有专门的线程用于进行日志输出操作,处理业务逻辑的主线程不用等待即可执行后续业务逻辑。

Log4j2中的异步日志实现方式有AsyncAppender和AsyncLogger两种。其中,AsyncAppender采用了ArrayBlockingQueue来保存需要异步输出的日志事件;AsyncLogger则使用了Disruptor框架来实现高吞吐。

AsyncAppender

每个Async Appender,内部维护了一个ArrayBlockingQueue,并将创建一个线程用于输出日志事件,如果配置了多个AppenderRef,将分别使用对应的Appender进行日志输出。

AsyncLogger 

上述程序中,main线程作为生产者,EventProcessor线程作为消费者。

生产者生产消息

当运行到类似于logger.info、logger.debug的输出语句时,将生成的LogEvent放入RingBuffer中。

消费者消费消息

如果RingBuffer中有LogEvent需要处理,EventProcessor线程从RingBuffer中取出LogEvent,调用Logger相关联的Appender输出LogEvent(具体输出过程与同步过程相同,同样需要过滤器过滤、PatternLayout格式化等步骤)。

如果RingBuffer中没有LogEvent需要处理,EventProcessor线程将处于等待阻塞状态(默认策略)。

需要注意的是,虽然在log4j2.xml中配置了多个AsyncLogger,但是并不是每个AsyncLogger对应着一个处理线程,而是仅仅有一个EventProcessor线程进行日志的异步处理。

AsyncLogger的内部使用了Disruptor框架,大大提升了性能,后面可以研究一下。

启动任务 观察一下线程情况

​​​​​​​

 参考链接

​​​​​​Log4j2中的同步日志与异步日志 - Ye_yang - 博客园

Log4j2中RollingFile的文件滚动更新机制 - Ye_yang - 博客园

黑马程序员java日志框架教程,全面深入学习多种java日志框架_哔哩哔哩_bilibili

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值