log4j2 xml配置

log4j2配置详解 log4j2XML配置详解:监控与自定义日志输出策略-CSDN博客

 

Xml格式

<!-- status是log4j2自身内部的信息输出,可以不设置 
     monitorInterval是自动检测修改配置文件和重新配置本身,设置间隔秒数
-->
<configuration status="WARN" monitorInterval="3600">
    <Properties>
        <!-- 公共配置 -->
        <!-- log文件存放路径./当前工程目录下 -->
        <property name="basePath">./log2</property>
        <!-- 输入信息格式 -->
        <!-- 带行号显示,对日志输出的性能有影响,在控制台上点击可以直接定位到代码 -->
        <property name="consoleLogPattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l - %m%n</property>
        <property name="logFilePattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %C.%M[%L line] - %m%n</property>
        <!-- 日志默认切割的最小单位 -->
        <property name="fileSize">100MB</property>
        <!-- 日志默认输出级别 -->
        <property name="outputLogLevel">DEBUG</property>
    </Properties>
    <!--定义appender -->
    <appenders>
        <!-- =======================================用来定义输出到控制台的配置======================================= -->
        <Console name="Console" target="SYSTEM_OUT">
            <!-- 设置控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 设置输出格式,不设置默认为:%m%n -->
            <PatternLayout pattern="${consoleLogPattern}"/>
        </Console>
   
        <!-- ============================打印INFO级别的日志到文件中======================= -->
        <!-- fileName:日志保存路径,filePattern:日志压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面进行压缩 -->
        <RollingFile name="InfoFile" fileName="${basePath}/info.log" filePattern="${basePath}/%d{yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${logFilePattern}"/>
            <Policies>
            <!--    interval:设置为1,filePattern是%d{yyyy-MM-dd}到天的格式,则间隔一天生成一个文件,如果设置为12,filePattern是%d{yyyy-MM-dd-HH}到小时的格式,则间隔12小时生成一个文件 
                    modulate:日志默认同类型日志,为true将以0点为边界进行调整
                -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="${fileSize}"/>
            </Policies>
            <!-- 日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
            <DefaultRolloverStrategy max="100" />
            <Filters>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>
        <!-- ============================打印ERROR级别的日志到文件中=======配置和INFO的一样,就修改下保存文件名称和压缩文件名称==================== -->
        <RollingFile name="ErrorFile" fileName="${basePath}/error.log" filePattern="${basePath}/%d{yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${logFilePattern}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="${fileSize}"/>
            </Policies>
            <DefaultRolloverStrategy max="100" />
            <Filters>
                <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>
    </appenders>
    <!--定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!-- 设置java.sql包下的日志只打印INFO及以上级别的日志,此设置可以支持sql语句的日志打印 -->
        <logger name="java.sql" level="INFO" additivity="false">
            <appender-ref ref="Console"/>
        </logger>
        <!--建立一个默认的root的logger-->
        <root level="${outputLogLevel}">
            <appender-ref ref="Console"/>
            <appender-ref ref="InfoFile"/>
            <appender-ref ref="ErrorFile"/>
        </root>
    </loggers>
</configuration>

配置

自动配置

log4j2支持4种格式的配置:json、yaml、xml、peroperties。加载顺序为:

  1. 系统属性 log4j2.configurationFile 指定的配置文件(可以是Url)。
  2. log4j2-test.properties
  3. log4j2-test.yaml、log4j2-test.yml
  4. log4j2-test.json、log4j2-test.jsn
  5. log4j2-test.xml
  6. log4j2.properties
  7. log4j2.yaml 、log4j2.yml
  8. log4j2.json、log4j2.jsn
  9. log4j2.xml
  10. DefaultConfiguration

Configuration 元素属性

  • monitorInterval:自动监控更新,单位(秒)
  • status:log4j2的日志等级

Policy 触发策略

TimeBasedTriggeringPolicy

参数名称 类型 描述
interval integer 根据日期格式中最具体的时间单位来决定应该多久发生一次rollover。例如,在日期模式中小时为具体的时间单位,那么每4小时会发生4次rollover,默认值为1
modulate boolean 表示是否调整时间间隔以使在时间间隔边界发生下一个rollover。例如:假设小时为具体的时间单元,当前时间为上午3点,时间间隔为4,第一次发送rollover是在上午4点,接下来是上午8点,接着是中午,接着是下午4点等发生。

TimeBasedTriggeringPolicy切分文件策略,是基于filePattern中的%d{yyyy-MM-dd-HH-mm-ss}来决定到底采用哪种时间单位(天、小时、分钟、秒等)。filePattern日期格式精确到哪一位,interval 也精确到哪一个单位。注意filePattern中配置的文件重命名规则是 %d{yyyy-MM-dd HH-mm-ss}-%i,最小的时间粒度是 ss,即秒钟。 TimeBasedTriggeringPolicy 默认的 size 是 1,结合起来就是每 1 秒钟生成一个新文件。如果改成 %d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件

modulate,说明是否对封存时间进行调制。若 modulate=true, 则封存时间将以 0 点为边界进行偏移计算。比如,modulate=true,interval=4hours, 那么假设上次封存日志的时间为 03:00,则下次封存日志的时间为 04:00, 之后的封存时间依次为 08:00,12:00,16:00。

切分也是基于最后一部分,增加 interval。是以程序启动时间。

SizeBasedTriggeringPolicy

size属性:可以为字节,千字节,兆字节,千兆字节,通过在数值后面指定一个后缀 KB,MB 或者 GB

CronTriggeringPolicy

基于Cron表达式的触发策略。

<CronTriggeringPolicy schedule="0/5 * * * * ?" />

Strategy 滚动策略

DefaultRolloverStrategy

DefaultRolloverStrategy制定了默认的rollover策略,通过max参数可控制一定时间范围内归档的日志文件的最大个数。

    <DefaultRolloverStrategy fileIndex="nomax">
        <Delete basePath="${LOG_HOME}" maxDepth="2">
            <IfFileName glob="*.log.gz">
                <IfAny>
                    <IfAccumulatedFileSize exceeds="100M"/>
                    <IfAccumulatedFileCount exceeds="100"/>
                    <IfLastModified age="30d"/>
                </IfAny>
            </IfFileName>
        </Delete>
    </DefaultRolloverStrategy>
参数 Type Description
fileIndex String 可选值 max, min,nomax。nomax:将忽略DefaultRolloverStrategy的最大值和最小值。
min integer 最小文件数,默认1
max integer 最大文件数,默认7。一旦达到这个值,旧的存档将在随后的滚动中被删除。
compressionLevel integer 设置压缩级别,0-9,其中0 =无,1 =最佳速度,到9 =最佳压缩。仅对ZIP文件实现。
tempCompressedFilePattern String 归档日志文件在压缩过程中的文件名模式。

:第四次和随后的归档,foo-1.log被删除,foo-2.log被重命名为foo-1.log,foo-3.log被重命名为foo-2.log,foo.log被重命名为foo-3.log。创建新的foo.log文件并继续写入。

:第四次和随后的归档,删除foo-3.log,将foo-2.log重命名为foo-3.log,将foo-1.log重命名为foo-2.log,将foo.log重命名为foo -1.log。创建新的foo.log文件并继续写入。

参数 Type Description
basePath String 从哪开始扫描。
maxDepth int 最大扫描层次。maxDepth为0时只扫描baseDir本身maxDepth为1时只扫描baseDir目录下文档
pathConditions PathCondition[] 可以指定一个或多个PathCondition元素。如果指定了多个PathCondition元素,则需要所有的PathCondition结果都为true才会进行删除。

PathCondition也可以嵌套。如果进行嵌套,则是先判断外层的PathCondition,然后进行内层的判断。如果没有嵌套,则是按顺序进行判断。
也可以创建自定义条件或使用内置条件:
IfFileName 如果文件名与此参数匹配则结果为true,此参数为正则表达式或 glob的文件。
IfLastModified 最后修改时间早于或等于此参数则结果为true,此参数为duration。
IfAccumulatedFileCount 文件数超过指定个数则结果为true,此参数为整型。
IfAccumulatedFileSize 所有文件总大小达到此参数则结果为true,此参数为KB、MB、GB。

IfAll 如果此标签下的所有条件都配置成功(逻辑与),则结果为true。
IfAny 如果此标签下的任何一个条件匹配成功(逻辑或),则结果为true。
IfNot 如果此标签下的所有条件都不匹配(逻辑非),则结果为true。

Filters

Log4j允许在以下4个位置中指定过滤器:

  1. appendersloggersproperties元素处于同一级别。这些过滤器可以接受 或者在将事件传递给LoggerConfig之前拒绝事件。
  2. logger元素中。这些过滤器可以接受或拒绝特定记录器的事件。
  3. appender元素中。这些筛选器可以阻止或导致事件被 appender。
  4. appender引用元素中。这些过滤器用于确定记录器是否应路由 将事件添加到appender。

ThresholdFilter

默认值: <ThresholdFilter level="INFO" onMatch="NEUTRAL" onMismatch="DENY"/>

说明:

  • onMatch:大于、等于 level 级别的。
  • onMismatch:小于 level 级别的。
  • ACCEPT: 表示接受该事件,不再将其传递到过滤器链的下一个元素。
  • DENY: 表示拒绝该事件,不再将其传递到过滤器链的下一个元素。
  • NEUTRAL: 表示不做出决定,将事件传递到过滤器链的下一个元素。
            <Filters>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>

MarkerFilter

      <filters>
        <MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
        <MarkerFilter marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
      </filters>

ThreadContextMapFilter

        <ThreadContextMapFilter>
          <KeyValuePair key="test" value="123"/>
        </ThreadContextMapFilter>

properties格式

#过滤ERROR以上信息
appender.E.filter.threshold.type = ThresholdFilter
appender.E.filter.threshold.level = FATAL
appender.E.filter.threshold.onMatch = DENY
appender.E.filter.threshold.onMisMatch=NEUTRAL


appender.info_only.filter.threshold.type = LevelRangeFilter
appender.info_only.filter.threshold.minLevel = INFO
appender.info_only.filter.threshold.maxLevel = INFO
appender.info_only.filter.threshold.onMatch = ACCEPT
appender.info_only.filter.threshold.onMisMatch=DENY

附录

参考

官网

Filters 类型

properties语法

#公告配置,log文件存放路径
property.filename = ./log22
#控制台数据
appender.console.type = Console
appender.console.name = console
#输出布局类型
appender.console.layout.type = PatternLayout
#输入信息格式
appender.console.layout.pattern =  %-d{yyyy-MM-dd HH:mm:ss,SSS} [%p] %c %m%n
#级别过滤
appender.console.filter.threshold.type = ThresholdFilter
#日志输出的级别
appender.console.filter.threshold.level = info
#===================自定义文件输入====================
appender.infoFile.type = RollingFile
appender.infoFile.name = infoLogFile
#输出文件保存地址
appender.infoFile.fileName=${filename}/info.log
#输入文件滚动时,文件重命名规则
appender.infoFile.filePattern = ${filename}/info-%d{MM-dd-yy-HH-mm-ss}-%i.log
appender.infoFile.layout.type = PatternLayout
#输入信息格式
appender.infoFile.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c{1} [%t] - %msg%n
appender.infoFile.policies.type = Policies
#输入日志文件大小的触发策略
appender.infoFile.policies.size.type = SizeBasedTriggeringPolicy
#输入日志文件有100MB触发策略
appender.infoFile.policies.size.size=100MB
appender.infoFile.strategy.type = DefaultRolloverStrategy
appender.infoFile.strategy.max = 5
#INFO级别数据写入INFO.log文件中,
appender.infoFile.filter.threshold.type = ThresholdFilter
appender.infoFile.filter.threshold.level = INFO
appender.infoFile.filter.threshold.onMatch = ACCEPT
appender.infoFile.filter.threshold.onMisMatch=DENY
#第二个配置error的和上面配置info的一样就是名称要修改下
appender.errorFile.type = RollingFile
appender.errorFile.name = errorLogFile
appender.errorFile.fileName=${filename}/error.log
appender.errorFile.filePattern = ${filename}/error-%d{MM-dd-yy-HH-mm-ss}-%i.log
appender.errorFile.layout.type = PatternLayout
appender.errorFile.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c{1} [%t] - %msg%n
appender.errorFile.policies.type = Policies
appender.errorFile.policies.size.type = SizeBasedTriggeringPolicy
appender.errorFile.policies.size.size=100MB
appender.errorFile.strategy.type = DefaultRolloverStrategy
appender.errorFile.strategy.max = 5
appender.errorFile.filter.threshold.type = ThresholdFilter
appender.errorFile.filter.threshold.level = INFO
appender.errorFile.filter.threshold.onMatch = ACCEPT
appender.errorFile.filter.threshold.onMisMatch=DENY
#根记录器
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = console
rootLogger.appenderRef.errorFile.ref = errorLogFile
rootLogger.appenderRef.errorFile.level = error
rootLogger.appenderRef.infoFile.ref = infoLogFile
rootLogger.appenderRef.infoFile.level = info
 

Spring Boot 整合 slf4j+log4j2 实现日志管理

 

背景

logging.apache

Java 中比较常用的日志框架

  • log4j(Log for Java):Apache 的一个开源项目,七种日志级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE
  • logback:是一个很成熟的日志框架,其实 logBack 和 log4j 出自一个人之手,这个人就是 Ceki Gülcü。logback 比 log4j 大约快 10 倍、消耗更少的内存,迁移成本也很低,自动压缩日志、支持多样化配置、不需要重启就可以恢复 I/O 异常等优势
  • log4j2:作者认为,log4j2已经不仅仅是 log4j 的一个升级版本了,而是从头到尾被重写的,这可以认为这其实就是完全不同的两个框架

Spring Boot 默认使用 logback,但相比较而言,log4j2 在性能上面会更好。SpringBoot 高版本都不再支持 log4j,而是支持 log4j2。log4j2,在使用方面与 log4j 基本上没什么区别,比较大的区别是 log4j2 不再支持 properties 配置文件,支持 xml、json 格式的文件。

《阿里巴巴Java开发手册》,其中有一条规范做了「强制」要求:

应用中不可直接使用日志系统(Log4j Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用日志门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

Java 简易日志门面(Simple Logging Facade for Java,缩写 SLF4J),它并不是真正的日志框架,他是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用。可以在软件部署的时候决定要使用的 Logging 框架,目前主要支援的有 Java logging API、log4j 及 logback 等框架。

理解 SLF4J

接口用于定制规范,可以有多个实现,使用时是面向接口的(导入的包都是 slf4j 的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,所以可以任意的更换实现而不用更改代码中的日志相关代码。

比如:slf4j 定义了一套日志接口,项目中使用的日志框架是logback,开发中调用的所有接口都是 slf4j 的,不直接使用 logback,调用是 自己的工程调用 slf4j 的接口,slf4j 的接口去调用 logback 的实现,可以看到整个过程应用程序并没有直接使用 logback,当项目需要更换更加优秀的日志框架时(如log4j2)只需要引入 log4j2 的 jar 和 Llg4j2 对应的配置文件即可,完全不用更改 Java 代码中的日志相关的代码 logger.info(“xxx”),也不用修改日志相关的类的导入的包( import org.slf4j.Logger; import org.slf4j.LoggerFactory;)

总结:使用日志接口便于更换为其他日志框架。

One More Thing:上面的这几段话是参考文章中截取的,也让我确实理解了为何推荐使用 SLF4J 的原因。这种做法感觉就是有点「面向接口编程」的思想,今天也查阅了一些这方面的资料,也让我想起了为何项目中写 Service 代码时,往往是先写个接口、然后在写个该接口的实现类。待有时间好好研究一些这块的优点!

性能分析

!性能评测](https://gitee.com/michael_xiang/images/raw/master/BBsUn0.jpg)

可以看到在同步日志模式下, Logback的性能是最糟糕的。

异步日志模式

log4j2的性能无论在同步日志模式还是异步日志模式下都是最佳的!那本文就介绍的是使用 log4j2 作为 slf4j 的具体实现。

log4j2 依赖

复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!-- 去掉logback配置 -->
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- 引入log4j2依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
</dependencies>
复制代码

 

log4j2 使用

复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// 这了两种写法都 OK,推荐第一种,不用每次都要修改类名
private static final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Logger logger = LogManager.getLogger(UserController.class);
//...
logger.debug("this is debug");
logger.info("this is info");
复制代码

 

log4j2 日志级别

从小到大依次是:all、trace、debug、info、warn、error、fatal、off

由于我们使用的是 slf4j 接口包,该接口包中只提供了未标有删除线的日志级别的输出。

log4j2 配置文件结构

配置文件的主要结构如下:

  • Appenders:
    • Appender
      • Filter
      • Layout
      • Policies
      • Strategy
  • Loggers
    • Logger
    • RootLogger

Appender

Appender 可以理解为一个管道,定义了日志内容的去向(保存位置)。

  • 配置一个或者多个 Filter
  • 配置 Layout 来控制日志信息的输出格式。
  • 配置 Policies 以控制日志何时(When)进行滚动。
  • 配置 Strategy 以控制日志如何(How)进行滚动。

注意点:

  • 多个 appender 不能指向同一个日志文件,否则会报错:Configuration has multiple incompatible Appenders pointing to the same resource 'logs/mybatis-demo-warn.log'
  • ImmediateFlush=true,一旦有新日志写入,立马将日志写入到磁盘的文件中。当日志很多,这种频繁操作文件显然性能很低下
  • immediateFlush:log4j2 接收到日志事件时,是否立即将日志刷到磁盘。默认为 true。
  • BufferedIO: 文件流写出是否使用缓冲,true 表示使用,默认值为 false 即不使用缓冲。测试显示,即使在启用immediateFlush 的情况下,设置 bufferedIO=true 也能提高性能。
  • 一个 LogConfig 可以使用多个 appender,一个 appender 也可以被多个 LogConfig 使用

官宣——Appender

PatternLayout

这是常用的日志格式化类,其它日志格式化类很少用。

<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

常用说明:

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

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

关于 pattern 的格式点击 官宣——Pattern Layout

Filter

Filters 决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受)DENY(拒绝)NEUTRAL(中立)

常用的 Filter 实现类有:

  • LevelRangeFilter
  • TimeFilter
  • ThresholdFilter

简单说就是 log4j2 中的过滤器 ACCEPT 和 DENY 之后,后续的过滤器就不会执行了,只有在 NEUTRAL 的时候才会执行后续的过滤器

复制代码
<Console name="Console">

    <!--
        设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器
        最后一个过滤器建议设置 onMismatch="DENY", 不然日志就输出了。
    -->
    <Filters>

        <!-- 从大到小:error, warn, info, debug, trace -->
        <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="NEUTRAL" />

        <!-- 只允许在每天的 8点~8点半 之间输出日志 -->
        <TimeFilter start="08:00:00" end="08:30:00" onMatch="ACCEPT" onMismatch="DENY" />
    </Filters>

    <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
</Console>
复制代码

 

LevelRangeFilter 对它们进行了 ACCEPT,而剩下的 trace Msg 和 debug Msg 则会经过下一个过滤器。

Policy

Policy & Strategy

  • Policy 是用来控制日志文件何时(When)进行 Rolling/滚动的;
  • Strategy 是用来控制日志文件如何(How)进行 Rolling/滚动的。

所谓「日志滚动」就是当达到设定的条件后,日志文件进行切分。比如:工程师想让系统中的日志按日进行切分,并且按月归档。

Rolling 的意思是当满足一定条件后,就重命名原日志文件用于备份,并重新生成一个新的日志文件。例如需求是每天生成一个日志文件,但是如果一天内的日志文件体积已经超过 1G,就重新生成。两个条件满足一个即可

Policy常用的实现类:

  • SizeBasedTriggeringPolicy,根据日志文件的大小进行滚动。单位有:KBMBGB
  • CronTriggeringPolicy,使用 Cron 表达式进行日志滚动,很灵活
  • TimeBasedTriggeringPolicy,这个配置需要和 filePattern 结合使用,注意 filePattern 中配置的文件重命名规则。滚动策略依赖于 filePattern 中配置的最具体的时间单位,根据最具体的时间单位进行滚动。这种方式比较简洁。CronTriggeringPolicy 策略更强大

在 TimeBasedTriggeringPolicy 标签中加上了 modulate 属性并设置为 true,该属性的意思是是否对日志生成时间进行调制。若为 true,则日志时间将以 0 点为边界进行偏移计算。例如第一次日志保存时间是 3 点,modulate为 trueinterval 是 4h。那么下次生成日志时间是 4点,08:00,12:00……。

复制代码
<Appenders>
    <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" >
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

        <Policies>
            <!-- 每 5s 翻滚一次 -->
            <!--<CronTriggeringPolicy schedule="0/5 * * * * ?" />-->

            <!--
                filePattern中最具体的时间单位是 秒。
                这里用 TimeBasedTriggeringPolicy 替换 CronTriggeringPolicy

                注意:modulate属性是指从启动时间开始算5秒,还是从0秒开始算5秒,运行一下就明白了。
                modulate: true(默认值) // 会从启动时间开始算 5秒
                modulate: false // 从 0秒开始算
            -->
            <TimeBasedTriggeringPolicy interval="5" modulate="true"/>
            <SizeBasedTriggeringPolicy size="10 MB"/>
        </Policies>
        <DefaultRolloverStrategy max="10" />
    </RollingRandomAccessFile>
</Appenders>
复制代码

 

Strategy

Strategy常用的实现类:

  • DefaultRolloverStrategy
  • DirectWriteRolloverStrategy

这两个 Strategy 都是控制如何进行日志滚动的。

DefaultRolloverStrategy 默认的 max为 7。

<DefaultRolloverStrategy max="7"/>

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

注意:不要认为 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"

Appender 类型

FileAppender(File)、RandomAccessFileAppender(RandomAccessFile)

  • 相同点:写入日志信息到文件
  • 不同点:使用的 I/O 实现类不同,前者使用 FileOutputStream,后者使用 RandomAccessFile

官方文档说是在 bufferedIO=true (默认是 true )的情况下,性能提升 20% ~ 200%

常用属性:

  • fileName:来指定文件位置,文件或目录不存在则会自动创建。
  • immediateFlush:是否每次写入都要立刻刷新到硬盘中。默认 true,如果使用默认值可能会影响性能。

RollingFileAppender(RollingFile)、RollingRandomAccessFileAppender(RollingRandomAccessFile)

  • 相同点:写入日志信息到文件
  • 不同点:使用的 I/O 实现类不同,前者使用 FileOutputStream,后者使用 RandomAccessFile
  • 上一对的实现类不能进行「日志滚动」,而带有 rolling 字样的 appender 就可以实现「滚动」功能。有「滚动」,会判断是否满足封存文件的要求,执行日志存档操作。

RollingRandomAccessFile Appender,相比 RollingFileAppender有很大的性能提升,官网宣称是20-200%

复制代码
<RollingRandomAccessFile name="File" fileName="logs/app.log"
                            filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" >
    <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
    <Policies>
        <!-- 每 5s 翻滚一次 -->
        <CronTriggeringPolicy schedule="0/5 * * * * ?" />
        <SizeBasedTriggeringPolicy size="10 MB"/>
    </Policies>
    <DefaultRolloverStrategy max="10" />
</RollingRandomAccessFile>
</Appenders>
复制代码

 

常用属性:

  • filePattern:指定当发生Rolling时,文件的转移和重命名规则。至于其中的 $${date:hh-mm} 是表示了一个文件夹(以 小时-分钟)命名。
  • DefaultRolloverStrategy 指定了如何(How)进行翻滚,并且指定了最大翻滚次数(影响%i参数值),超过次数之后会按照相应的规则删除旧日志。
  • Policies: 这里就是规定了何时进行滚动(When),可以有多个Policy。
    • CronTriggeringPolicy,比如设置每 5s 进行一次翻滚
    • SizeBasedTriggeringPolicy 指定当文件体积大于size指定的值时,触发Rolling。例如,如果当前文件超过了 10MB,但是文件的名字还没有进行翻滚(建立新文件),那么就会用%i的方式进行翻滚。

如果配置的是 RollingFile 或 RollingRandomAccessFile,则必须配置一个 Policy

翻滚理解

假设计数器次数设为2次 <DefaultRolloverStrategy max="2" />filePattern 中既含有 date/time pattern,又含有 %i

当满足翻滚触发条件时(时间间隔到了 OR 文件大小超了),就会启动 Rolling

app.log

第一次翻滚:app.log app.1.log // app.log -> app.1.log
第二次翻滚:app.log app.1.log app.2.lop // app.log -> app.2.log

一个循环结束,到达了最大保存数 2 了,那么,app1.log 会被删除,下一个 app3.log 就会覆盖 app2.logapp2.log会改名为app1.log

第三次翻滚:app.log app.2.lop app.3.lop // app.log -> app.3.log
第四次翻滚:app.log app.3.lop app.4.lop // app.log -> app.4.log

理解:编号最近的一次也就是最新的一次 log,而采取了 Policy 方式的日志,fileName 中保存的日志将不会是全量的日志,而是根据你 Policy 的条件切分后的最近一次的日志内容。

一个 Appender 示例

按月归档日志,按日进行切分,限制单文件大小为 500MB, 一天最多生成20个文件,也就是(20 * 500)MB大小的日志

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>
        <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                 filePattern="logs/$${date:yyyy-MM}/%d{yyyy-MM-dd}.app.%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
                <SizeBasedTriggeringPolicy size="500MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingRandomAccessFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>

</Configuration>
复制代码

 

Logger

简单说 Logger 就是一个路由器,指定类、包中的日志信息流向哪个管道,以及控制他们的流量(日志级别)

Logger 部分为两个 Logger:

  • RootLogger(必须配置)
  • Logger

注意:Logger 中也可以加过滤器的!

日志重复打印问题

如果 Root 中的日志包含了 Logger 中的日志信息,并且 AppenderRef 是一样的配置,则日志会打印两次。

这时候我们需要使用一个 Logger 的属性来解决,那就是 additivity,其默认值为 true,需要配置为false

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>
        <Console name="Console">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>
    </Appenders>

    <Loggers>
        <Logger name="me.master.snail.log.LogMain" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <Root level="trace">
            <AppenderRef ref="Console"/>
            <Filters>
                <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="DENY" />
            </Filters>
        </Root>
    </Loggers>
</Configuration>
复制代码

 

  • RootLogger 只能有 1 个,普通的 Logger 可以定义多个,可以细致到给某个类定义;
  • 多个 Logger 配置重复了,在日志文件中会重复;
  • 每一个 Logger 对应的 name 是包路径,表示在 name 包下的类使用 AppenderRef 指向的日志模板来输出日志;
  • 不同的 LogConfig 之间其实是有继承关系的,子 LogConfig 会继承 parent 的属性,而所有 LogConfig 都继承自 Root LogConfig。所以即使只配置了 root logger,你一样可以在任何地方通过 LoggerFactory.getLogger 获取一个 logger 对象,记录日志;
  • 先配置一个 Root,让所有需要使用日志的 logger 继承,然后对有特别需要的 logger 进行特殊的配置,比如我们希望 org.springframework 包只记录 error以及 warn 级别的 log,再比如,我们希望能显示mybatis 执行的 sql 的日志,都可以进行个性化的配置;

Logger 等级实验

复制代码
<logger name="org.springframework" level="INFO" additivity="true">
    <AppenderRef ref="InfoLog"/>
</logger>

<Root level="ERROR" additivity="true">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="InfoLog"/>
    <AppenderRef ref="WarnLog"/>
    <AppenderRef ref="ErrorLog"/>
</Root>
复制代码

 

  • ROOT 等级设为 ERROR 时,org.springframework Logger 等级设为 OFF 时,发现原来的 warn.log 和 info.log 文件中,都只有级别大于或等于 ERROR 的日志信息了;
  • ROOT 等级设为 ERROR 时,org.springframework Logger 等级设为 INFO 时,发现info.log 文件中,增加了 org.springframework 包的相关 INFO 级别的日志信息了;

总结:

  • Logger 日志等级和 appender 日志等级的关系:logger 日志等级和 appender 日志等级,谁「高」听谁的;
  • 普通 Logger 的优先级高
 

Log4j2中RollingFile的文件滚动更新机制

 

一、什么是RollingFile

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

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

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

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

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

Log4j2提供了默认的rollover策略DefaultRolloverStrategy。

下面通过一个log4j2.xml文件配置简单了解RollingFile的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{yyyy-MM-dd HH}.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy interval="1"/>
        <SizeBasedTriggeringPolicy size="250MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

上述配置文件中配置了一个RollingFile,日志写入logs/app.log文件中,每经过1小时或者当文件大小到达250M时,按照app-2017-08-01 12.log的格式对app.log进行重命名并归档,并生成新的文件用于写入log。

其中,fileName指定日志文件的位置和文件名称(如果文件或文件所在的目录不存在,会创建文件。)

filePattern指定触发rollover时,文件的重命名规则。filePattern中可以指定类似于SimpleDateFormat中的date/time pattern,如yyyy-MM-dd HH,或者%i指定一个整数计数器。

TimeBasedTriggeringPolicy指定了基于时间的触发策略。
SizeBasedTriggeringPolicy指定了基于文件大小的触发策略。

二、TriggeringPolicy

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

其中,SizeBasedTriggeringPolicy(基于日志文件大小)、TimeBasedTriggeringPolicy(基于时间)或同时基于文件大小和时间的混合触发策略最常用。

SizeBasedTriggeringPolicy

SizeBasedTriggeringPolicy规定了当日志文件达到了指定的size时,触发rollover操作。size参数可以用KB、MB、GB等做后缀来指定具体的字节数,如20MB。

<SizeBasedTriggeringPolicy size="250MB"/>

TimeBasedTriggeringPolicy

TimeBasedTriggeringPolicy规定了当日志文件名中的date/time pattern不再符合filePattern中的date/time pattern时,触发rollover操作。

比如,filePattern指定文件重命名规则为app-%d{yyyy-MM-dd HH}.log,文件名为app-2017-08-25 11.log,当时间达到2017年8月25日中午12点(2017-08-25 12),将触发rollover操作。

参数名
类型
描述
interval integer

此参数需要与filePattern结合使用,规定了触发rollover的频率,默认值为1。假设interval为4,若filePattern的date/time pattern的最小时间粒度为小时(如yyyy-MM-dd HH),则每4小时触发一次rollover;若filePattern的date/time pattern的最小时间粒度为分钟(如yyyy-MM-dd HH-mm),则每4分钟触发一次rollover。

modulate boolean

指明是否对interval进行调节,默认为false。若modulate为true,会以0为开始对interval进行偏移计算。例如,最小时间粒度为小时,当前为3:00,interval为4,则以后触发rollover的时间依次为4:00,8:00,12:00,16:00,...。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{yyyy-MM-dd HH}-%i.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

上述配置文件中,filePattern中yyyy-MM-dd HH最小时间粒度为小时,TimeBasedTriggeringPolicy中interval使用默认值1,将每1小时触发一次rollover。

若将filePattern改为filePattern=“logs/app-%d{yyyy-MM-dd HH-mm}-%i.log”,yyyy-MM-dd HH-mm最小时间粒度为分钟,将每1分钟触发一次rollover。

CompositeTriggeringPolicy

将多个TriggeringPolicy放到Policies中表示使用复合策略

<Policies>
    <TimeBasedTriggeringPolicy />
    <SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>

如上,同时使用了TimeBasedTriggeringPolicy、SizeBasedTriggeringPolicy,有一个条件满足,就会触发rollover。

三、DefaultRolloverStrategy

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

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

<DefaultRolloverStrategy max="7"/>

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

注意:不要认为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"

 

假设fileName为logs/app.log,SizeBasedTriggeringPolicy的size为10KB,DefaultRolloverStrategy的max为3。

根据filePattern配置的不同分为以下几种情况:

情况1:filePattern中仅含有date/time pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <RollingFile name="RollingFile" fileName="logs/app.log"
        filePattern="logs/app-%d{yyyy-MM-dd}.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="10KB"/>
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>
filePattern="logs/app-%d{yyyy-MM-dd}.log",指定当发生rollover时,将按照app-%d{yyyy-MM-dd}.log的格式对文件进行重命名。

每次触发rollover时,将按照如下方式对文件进行rollover。

第X次rollover
当前用于写入log的文件
归档的文件
描述
0 app.log - 所有的log都写进app.log中。
1 app.log app-2017-08-17.log

当app.log的size达到10KB,触发第1次rollover,app.log被重命名为app-2017-08-17.log。新的app.log被创建出来,用于写入log。

2 app.log

app-2017-08-17.log

当app.log的size达到10KB,触发第2次rollover,原来的app-2017-08-17.log将删除。app.log被重命名为app-2017-08-17.log。新的app.log文件被创建出来,用于写入log。

情况2:filePattern中仅含有整数计数器(%i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <RollingFile name="RollingFile" fileName="logs/app.log"
        filePattern="logs/app-%i.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="10KB"/>
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>
filePattern="logs/app-%i.log",其余配置同上。

每次触发rollover时,将按照如下方式对文件进行rollover。

第X次rollover
当前用于写入log的文件
归档的文件
描述
0 app.log - 所有的log都写进app.log中。
1 app.log app-1.log

当app.log的size达到10KB,触发第1次rollover,app.log被重命名为app-1.log。新的app.log被创建出来,用于写入log。

2 app.log

app-1.log

app-2.log

当app.log的size达到10KB,触发第2次rollover,app.log被重命名为app-2.log。新的app.log被创建出来,用于写入log。

3 app.log

app-1.log

app-2.log

app-3.log

当app.log的size达到10KB,触发第3次rollover,app.log被重命名为app-3.log。新的app.log被创建出来,用于写入log。

4 app.log

app-1.log

app-2.log

app-3.log

当app.log的size达到10KB,触发第4次rollover,app-1.log被删除(即最初的、最旧的app.log)。app-2.log被重命名为app-1.log,app-3.log被重命名为app-2.log,app.log被重命名为app-3.log。新的app.log被创建出来,用于写入log。

情况3:如果filePattern中既含有date/time pattern,又含有%i计数器

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
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <RollingFile name="RollingFile" fileName="logs/app.log"
        filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="10KB"/>
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>
filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log",同时指定了TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy的触发策略,每1分钟或者文件大小达到10KB,将触发rollover。

每次触发rollover时,将按照如下方式对文件进行rollover。

第X次rollover
当前用于写入log的文件
归档的文件
描述
0 app.log - 所有的log都写进app.log中。
1 app.log app-2017-08-17 20-52-1.log

当app.log的size达到10KB,触发第1次rollover,app.log被重命名为app-2017-08-17 20-52-1.log。新的app.log被创建出来,用于写入log。

2 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

当app.log的size达到10KB,触发第2次rollover,app.log被重命名为app-2017-08-17 20-52-2.log。新的app.log被创建出来,用于写入log。

3 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

当app.log的size达到10KB,触发第3次rollover,app.log被重命名为app-2017-08-17 20-52-3.log.log。新的app.log被创建出来,用于写入log。

4 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

当app.log的size达到10KB,触发第4次rollover,因计数器的值到达max值,app-2017-08-17 20-52-1.log被删除(即最初的、最旧的app.log)。app-2017-08-17 20-52-2.log被重命名为app-2017-08-17 20-52-1.log,app-2017-08-17 20-52-3.log被重命名为app-2017-08-17 20-52-2.log,app.log被重命名为app-2017-08-17 20-52-3.log。新的app.log被创建出来,用于写入log。

5 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

当前时间变为app-2017-08-17 20-53,触发第5次rollover,app-2017-08-17 20-52-1.log被删除。app-2017-08-17 20-52-2.log被重命名为app-2017-08-17 20-52-1.log,app-2017-08-17 20-52-3.log被重命名为app-2017-08-17 20-52-2.log,app.log被重命名为app-2017-08-17 20-52-3.log。新的app.log被创建出来,用于写入log。
6 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

app-2017-08-17 20-53-1.log

当app.log的size达到10KB,触发第6次rollover,app.log被重命名为app-2017-08-17 20-53-1.log。新的app.log被创建出来,用于写入log。

 

总结:

1.max参数是与filePattern中的计数器%i配合起作用的,若filePattern为filePattern="logs/app-%d{yyyy-MM-dd}.log">,由于没有设置%i计数器,max参数将不起作用。

2.max参数不是需要保留的文件的最大个数。如情况3,日志文件date/time pattern不再符合filePattern时,计算器将被重置为1,日志总个数超过了max的指定值。

可认为max参数规定了一定时间范围内归档文件的最大个数。

四、DeleteAction

DefaultRolloverStrategy制定了默认的rollover策略,通过max参数可控制一定时间范围内归档的日志文件的最大个数。

Log4j 2.5 引入了DeleteAction,使用户可以自己控制删除哪些文件,而不仅仅是通过DefaultRolloverStrategy的默认策略。

注意:通过DeleteAction可以删除任何文件,而不仅仅像DefaultRolloverStrategy那样,删除最旧的文件,所以使用的时候需要谨慎!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Properties>
    <Property name="baseDir">logs</Property>
  </Properties>
  <Appenders>
    <RollingFile name="RollingFile" fileName="${baseDir}/app.log"
          filePattern="${baseDir}/app-%d{yyyy-MM-dd}.log.gz">
      <PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
      <CronTriggeringPolicy schedule="0 0 0 * * ?"/>
      <DefaultRolloverStrategy>
        <Delete basePath="${baseDir}" maxDepth="2">
          <IfFileName glob="*/app-*.log.gz" />
          <IfLastModified age="60d" />
        </Delete>
      </DefaultRolloverStrategy>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

上述配置文件中,Delete部分便是配置DeleteAction的删除策略,指定了当触发rollover时,删除baseDir文件夹或其子文件下面的文件名符合app-*.log.gz且距离最后的修改日期超过60天的文件。

其中,basePath指定了扫描开始路径,为baseDir文件夹。maxDepth指定了目录扫描深度,为2表示扫描baseDir文件夹及其子文件夹。

IfFileName指定了文件名需满足的条件,IfLastModified指定了文件修改时间需要满足的条件。

DeleteAction常用参数如下:

参数名
类型
描述
basePath String

必填。目录扫描开始路径。

 

maxDepth int

扫描的最大目录深度。0表示basePath指定的文件自身。Integer.MAX_VALUE表示扫描所有的目录层。默认值为1,表示仅扫描basePath下的文件。

testMode boolean

如果为true,实际的文件不会被删除,删除文件的信息会打印到log4j2的INFO级别的log中。可使用此参数测试配置是否符合预测。默认为false。

pathConditions PathCondition[]

删除文件的过滤条件,满足指定条件的文件将会被删除,可以指定一个或多个。

如果指定多个pathCondition,需要同时满足。Conditions可以嵌套,当嵌套配置时,只有当满足了外部的contion时,才能对内部的condition进行判断。如果Conditions不是嵌套的,会可能以任意顺序进行判断。

Conditions也可以通过使用IfAll,IfAny,IfNot等类似于AND,OR,NOT的condition,实现复杂的condition。

  • IfFileName-判断文件的文件名是否满足正则表达式或glob表达式
  • IfLastModified-判断文件的修改时间是否早于指定的duration
  • IfAccumulatedFileCount-判断在遍历文件树的时候,文件个数是否超过了指定值
  • IfAccumulatedFileSize-判断在遍历文件树的时候,文件的总大小是否超过了指定值
  • IfAll-判断嵌套的condition是否都满足
  • IfAny-判断嵌套的condition是否有一个满足
  • IfNot-判断嵌套的condition是否不满足

五、程序测试demo

public class HelloWorld {
 
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
        try{
            //通过打印i,日志文件中数字越小代表越老
            for(int i = 0; i < 50000; i++) {
                logger.info("{}", i);
                logger.info("logger.info\n");
                Thread.sleep(100);//为了防止50000条很快跑完,sleep一段时间
            }
        catch (InterruptedException e) {}
    }
}

1.测试基于时间触发

filePattern最小时间粒度为秒,将每5秒触发一次rollover

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <!--<RollingFile name="RollingFile" fileName="logs/app.log"-->
                     <!--filePattern="logs/app-%d{yyyy-MM-dd HH}-%i.log">-->
        <RollingFile name="RollingFile" fileName="logs/app.log"
                     filePattern="logs/app-%d{yyyy-MM-dd HH-mm-ss}.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <!--当经过了interval时间后,将根据filePattern对文件进行重命名,并生成新的文件用于日志写入-->
                <TimeBasedTriggeringPolicy interval="5"/>
                <!--当日志文件大小大于size时,将根据filepattern对文件进行重命名,并生成新的文件用于日志写入-->
                <!--<SizeBasedTriggeringPolicy size="30KB"/>-->
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

2.测试基于文件大小的触发

日志文件达到5KB,将触发rollover

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <!--<RollingFile name="RollingFile" fileName="logs/app.log"-->
                     <!--filePattern="logs/app-%d{yyyy-MM-dd HH}-%i.log">-->
        <RollingFile name="RollingFile" fileName="logs/app.log"
                     filePattern="logs/app-%d{yyyy-MM-dd HH-mm-ss}.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <!--当经过了interval时间后,将根据filePattern对文件进行重命名,并生成新的文件用于日志写入-->
                <!--<TimeBasedTriggeringPolicy interval="5"/>-->
                <!--当日志文件大小大于size时,将根据filepattern对文件进行重命名,并生成新的文件用于日志写入-->
                <SizeBasedTriggeringPolicy size="5KB"/>
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

3.测试DefaultRolloverStrategy的max参数和%i计数器的搭配使用

注意filePattern最小时间粒度为分钟,且含%i计数器

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <RollingFile name="RollingFile" fileName="logs/app.log"
                     filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log">
        <!--<RollingFile name="RollingFile" fileName="logs/app.log"-->
                     <!--filePattern="logs/app-%d{yyyy-MM-dd HH-mm-ss}.log">-->
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <!--当经过了interval时间后,将根据filePattern对文件进行重命名,并生成新的文件用于日志写入-->
                <!--<TimeBasedTriggeringPolicy interval="5"/>-->
                <!--当日志文件大小大于size时,将根据filepattern对文件进行重命名,并生成新的文件用于日志写入-->
                <SizeBasedTriggeringPolicy size="5KB"/>
            </Policies>
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

 

六、参考资料

http://logging.apache.org/log4j/2.x/manual/appenders.html  RollingFileAppender部分

说明

rollover 表示的是当日志文件大小满足指定大小后,就生成一个新的文件的过程。

RollingFileAppender

RollingFileAppender是一个OutputStreamAppender,它(会把日志)写入到filename参数命名的文件中,并且会根据TriggeringPolicyRolloverPolicyrollover(rolls the file over)。RollingFileAppender会使用RollingFileManager(继承OutputStreamManager)来实际执行文件的I/O和执行rollover。尽管不能共享来做不同配置的RolloverFileAppenders,但是如果Manager可以访问的话,那么RolloverFileAppenders可以(进行共享)。
例如:在一个servlet容器中的两个web应用程序,他们有自己的配置,如果他们Log4j是共用一个类加载器(ClassLoader)那么就可以安全的写入到一个文件中。

RollingFileAppender 需要TriggeringPolicyRolloverStrategytriggering policy决定是否应该执行rollover的操作,而RolloverStrategy定义了应该如何完成rollover。如果RolloverStrategy没有配置的话,RollingFileAppender将使用DefaultRolloverStrategy。从log4j2.5版本开始,在DefaultRolloverStrategy中配置的自定义删除操作在rollover时将被执行。
从2.8版本开始,如果在DirectWriteRolloverStrategy中没有配置文件名,将使用DefaultRolloverStrategy进行替换。

RollingFileAppender不支持文件锁的。

这里rollover的操作可以理解为:当日志文件大小满足指定大小后,就生成一个新的文件。


RollingFileAppender参数

参数名 类型 描述
append boolean 默认为true,记录追加到文件的最后,否则就先清除以前的记录再写入
bufferedIO boolean 默认true,记录将会写入到缓存区,当缓存区满的时候,就会写入磁盘。或者如果设置immediateFlush将会立即写入。文件锁定不能和bufferedIO一起使用。
bufferSize int 当bufferedIO设置为true是,默认是8192 bytes
createOnDemand boolean 默认为false,该appender按需创建文件,当日志事件通过所有的filters并且通过路由指向了该appender,该appender仅仅创建该文件
filter Filter 过滤器决定事件是否应该由这个Appender来处理。通过使用CompositeFilter来使用多个Filter
fileName String 要写入的文件的名称。如果文件或其父目录不存在,它们都将被创建出来
filePattern String 压缩日志文件的文件名的模式。该模式的格式取决于所使用的RolloverPolicyDefaultRolloverPolicy将接受兼容SimpleDateFormat的日期/时间模式和/或者%i(代表整数计数器)。这个模式也支持运行时插值,所以任何的查找( eg:DateLookup)都可以包含在模式中
immediateFlush boolean 默认为true,每次写入都会执行flush。这可以保证每次数据都被写入磁盘,但是会影响性能。在同步的loggers中每次写入执行flush,那就非常有用。异步loggers和appenders将会在一系列事件结束后自动执行flush,即使设置为false。这也保证了数据写入到磁盘而且很高效
layout Layout 这个Layout用于格式化LogEvent.如果没有提供默认的layout,默认为layout模式为%m%n
name String 该Appender名称
policy TriggeringPolicy 用于决定是否发生rollover的策略
strategy RolloverStrategy 用于决定压缩文件的名称和路径
ignoreExceptions boolean 默认为true,遇到异常时,会将事件追加到内部日志并忽略它。设置false时,异常会传递给调用者,当这个appender被FailoverAppender包裹时,必须设置为false

Triggering Policies

Composite Triggering Policy

Composite Triggering Policy组合了多个triggering policies,如果配置的策略中的任何一个返回true,则返回true。CompositeTriggeringPolicy简单的通过在policies元素包裹其他的policies来配置。
例如,以下XML片段定义了当JVM启动时,当日志大小达到二十兆字节以及当前日期与日志的开始日期不匹配时滚动日志的策略。

<Policies>
  <OnStartupTriggeringPolicy />
  <SizeBasedTriggeringPolicy size="20 MB" />
  <TimeBasedTriggeringPolicy />
</Policies>

Cron Triggering Policy

基于cron表达式的CronTriggeringPolicy触发rollover

CronTriggeringPolicy 参数

参数名称 类型 描述
schedule String cron表达式。该表达式和Quartz调度所允许的表达式相同,有关表达式的完整描述,请参阅CronExpression
evaluateOnStartup boolean 在启动时,将根据文件的最后修改时间戳评估cron表达式。如果cron表达式表示在该时间和当前时间之间应该发生rollover,则文件将立即rollover

OnStartup Triggering Policy

如果日志文件比当前jvm启动时间更早以及满足或者超过最小文件的大小就会触发rollover

OnStartupTriggeringPolicy 参数说明:

参数名称 类型 描述
minSize long 文件必定发生rollover操作的最小尺寸。要是大小为0的话,那么无论文件大小是多少都将引起rollover。默认值为1,这将阻止空文件发送rollover

SizeBased Triggering Policy

SizeBased Triggering Policy:一旦文件大小达到指定大小后,就会发送rollover
该策略接受一个interval属性和modulate布尔属性。其中interval属性表示的是,基于时间模式应该发送rollover的频率。

TimeBasedTriggeringPolicy参数说明:

参数名称 类型 描述
interval integer 根据日期格式中最具体的时间单位来决定应该多久发生一次rollover。例如,在日期模式中小时为具体的时间单位,那么每4小时会发生4次rollover,默认值为1
modulate boolean 表示是否调整时间间隔以使在时间间隔边界发生下一个rollover。例如:假设小时为具体的时间单元,当前时间为上午3点,时间间隔为4,第一次发送rollover是在上午4点,接下来是上午8点,接着是中午,接着是下午4点等发生。

Rollover Strategies

Default Rollover Strategy

默认的rollover strategy接受一个日期/时间模式和一个整数,其中这个整数,是RollingFileAppender本身指定的filePattern属性。如果date/time模式存在的话,它将会替换当前日期和时间的值。如果这个模式包含整数的话,它将会在每次发生rollover时,进行递增。如果模式同时包含date/time和整数,那么在模式中,整数会递增直到结果中的data/time模式发生改变。如果文件模式是以".gz", ".zip", ".bz2", ".deflate", ".pack200", or ".xz"结尾的,将会与后缀相匹配的压缩方案进行压缩文件。格式为:bzip2, Deflate, Pack200 和 XZ需要Apache Commons Compress,此外,XZ需要XZ for Java
该模式还可以包含可以在运行时解析的查找引用,如下面的示例所示:
默认的rollover策略支持三种增加计数的方式。第一种叫做:fixed window策略。为了说明它的工作原理,假设min属性设置为1,max属性设置为3,文件名为“foo.log”,文件名模式为:foo-%i.log。

rollover次数 输出的目标 压缩的日志文件 描述
0 foo.log - 所有的日志都输出到初始文件中
1 foo.log foo-1.log 在第一次rollover时,foo.log会被重命名为foo-1.log。同时会创建一个新的foo.log并开始写入。
2 foo.log foo-1.log, foo-2.log 在第二次发生rollover时,foo-1.log会重命名为foo-2.log并且foo.log会重命名为foo-1.log。同时会创建一个新的foo.log并开始写入。
3 foo.log foo-1.log, foo-2.log, foo-3.log 在第三次发生rollover时,foo-2.log会重命名为foo-3.log。foo-1.log重命名为foo-2.log,foo.log会重命名为foo-1.log。同时会创建一个新的foo.log并开始写入。
4 foo.log foo-1.log, foo-2.log, foo-3.log 在第四次和随后的rollover时,foo-3.log会被删除,foo-2.log重命名为foo-3.log。foo-1.log重命名为foo-2.log。foo.log重命名为foo-1.log。后面同理

相比之下,当 fileIndex属性设置了max,其他设置和上面相同,将执行以下操作:

rollover次数 输出的目标 压缩的日志文件 描述
0 foo.log - 所有的日志都输出到初始文件中
1 foo.log foo-1.log 在第一次rollover时,foo.log会被重命名为foo-1.log。同时会创建一个新的foo.log并开始写入。
2 foo.log foo-1.log foo-2.log 在第二次rollover时,foo.log重命名为foo-2.log。同时会创建一个新的foo.log并开始写入。
3 foo.log foo-1.log foo-2.log foo-3.log 在第三次发生rollover时,fool.log重命名为foo-3.log,同时会创建一个新的foo.log并开始写入。
4 foo.log foo-1.log foo-2.log foo-3.log 在第四次和随后的rollover时,foo-1.log会被删除,foo-2.log重命名为foo-1.log,foo-3.log重命名为foo-2.log,foo.log重命名为foo-3.log。同时会创建一个新的foo.log并开始写入。

最后,从2.8版本开始,如果fileIndex属性设置为nomax,那么最大和最小值,都将会被忽略掉,文件编号将从1开发增加,并且每次rollover时递增都从编码最大开始(项目于max效果),而且没有文件数的限制。

DefaultRolloverStrategy参数

参数名称 类型 描述
fileIndex String 如果设置了max(默认就是),文件索引(编号)高的比低的更 新些。如果设置min,文件重命名将遵循Fixed Window策略
min integer 计数器的最小值。默认值为1。
max integer 计数器的最大值。一旦达到这个值,旧的档案将在随后的rollover中被删除。
compressionLevel integer 设置压缩级别0-9,其中0=无,1=最佳速度,通过9=最佳压缩。只适用于ZIP文件。

DirectWrite Rollover Strategy

DirectWriteRolloverStrategy会将日志事件会直接写入文件模式表示的文件中去。使用这个策略不会执行文件重命名。如果基于大小的触发策略导致在指定的时间段内写入多个文件,则它们将从一个编号开始,并持续递增,直到发生基于时间的rollover
警告:如果文件模式里有压缩的后缀,那么当应用程序关闭时,当前文件将不被压缩。此外,如果时间变化使得文件模式不再匹配当前文件,则它也不会在启动时被压缩。

DirectWriteRolloverStrategy 参数

参数名称 类型 描述
maxFiles String 在与文件模式(file pattern)匹配的时间段内允许的最大文件数。如果这个数字被突破了,则最旧的文件将被删除。如果指定了,那么这个值必须大于1。如果值小于零或省略,则文件数不受限制。
compressionLevel integer 设置压缩级别0-9,其中0=无,1=最佳速度,通过9=最佳压缩。只适用于ZIP文件。

①下面是使用RollingFileAppender的一个示例配置,并且是基于时间和大小的触发策略。其将会根据当前年和月份在未来7天内创建7个压缩包,并且使用gzip进行压缩。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
       filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

②第二个例子显示一个rollover策略,最多保留20个文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

③下面是使用RollingFileAppender的一个示例配置,并且是基于时间和大小的触发策略。其将会根据当前年和月份在未来7天内创建7个压缩包,并且每6小时即被6整除时,会使用gzip进行压缩每个文件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <!--这行区别-->
        <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

④此示例配置使用基于cron和基于大小的触发策略的RollingFileAppender,并且是无限制文件数量的直接写入到归档文件中。cron触发器是每小时发生rollover并且文件大小不得超过250M。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <CronTriggeringPolicy schedule="0 0 * * * ?"/>
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

⑤此示例和上面④大致一样,但是每小时保存的文件数量限制了为10:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <CronTriggeringPolicy schedule="0 0 * * * ?"/>
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DirectWriteRolloverStrategy maxFiles="10"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

Log Archive Retention Policy(日志存档保留策略):Delete on Rollover

log4j-2.5开始引入了删除操作,使得用户更有效的的控制在rollover时间内删除文件,而不是使用DefaultRolloverStrategy max属性进行删除。删除操作允许用户配置一个或多个条件,选择要删除相对于基本目录的文件。
注意:删除任何文件这是允许的操作。不仅仅是rollover时的文件。所以使用这个操作时,一定要小心。使用testMode参数可以测试您的配置,而不会意外删除错误的文件。

Delete 参数:

参数名称 类型 描述
basePath String 必参。从哪里扫描要删除的文件的基本路径。
maxDepth int 要访问的目录的最大级别数。值为0表示仅访问起始文件(基本路径本身),除非被安全管理者拒绝。Integer.MAX_VALUE的值表示应该访问所有级别。默认为1,意思是指定基本目录中的文件。
followLinks boolean 是否遵循符号链接默认值为false。
testMode boolean 默认false。如果为true,文件将不会被删除,而是将信息打印到info级别的status logger,可以利用这个来测试,配置是否和我们预期的一样
pathSorter PathSorter 一个实现了PathSorter接口的插件在选择删除文件前进行排序。默认是最近修改的文件排在前面
pathConditions PathCondition[] 如果没有指定ScriptCondition,则为必需。一个或多个PathCondition元素。如果指定了多个条件,在删除之前,他们需要接受全部的路径。条件是可以嵌套的,在这种情况下,只有在外部路径被接受的情况下,才会去评估内部路径。如果条件没有嵌套,则可以按照任何顺序去评估。条件也可以通过IfAllIfAnyIfNot组合复合条件。他们对于的是AND、OR、NOT。用户可以创建自定义条件或使用内置条件:IfFileName:接受路径(相对于基本路径)与正则表达式或glob匹配的文件。IfLastModified:接受与指定持续时间相同或更早的文件。IfAccumulatedFileCount :在file tree walk期间超过一些计数阈值后接受路径。IfAccumulatedFileSize:在file tree walk期间超过累积文件大小阈值后接受路径。ifAll:如果所有嵌套条件都接受它(逻辑与),则接受路径。嵌套条件可以按任何顺序进行评估。IfAny:如果其中一个嵌套条件接受(OR或OR),则接受路径。嵌套条件可以按任何顺序进行评估。IfNot:如果嵌套条件不接受(逻辑NOT),则接受路径。
scriptCondition ScriptCondition 如果没有指定PathConditions,则为必需。指定脚本的ScriptCondition元素。ScriptCondition应包含一个Script,ScriptRef或ScriptFile元素,用于指定要执行的逻辑。有关配置ScriptFiles和ScriptRefs的更多示例,请参阅ScriptFilter文档。该脚本传递了许多参数,包括在基本路径下找到的路径列表(最多为maxDepth),并且必须返回具有要删除路径的列表。

IfFileName 条件参数:

参数名称 类型 描述
glob String 如果regex没有指定的话,则必须。使用类似于正则表达式但是又具有更简单的有限模式语言来匹配相对路径(相对于基本路径)
regex String 如果glob没有指定的话,则必须。使用由Pattern类定义的正则表达式来匹配相对路径(相对于基本路径)
nestedConditions PathCondition[] 一组可选的嵌套PathConditions如果存在任何嵌套条件,则在它们删除文件之前,必须都接受。仅当外部条件接受文件(如果路径名称匹配)时,才会评估嵌套条件。

IfLastModified条件参数:

参数名称 类型 描述
age String 必须。指定持续时间duration。该条件接受比指定持续时间更早或更旧的文件。
nestedConditions PathCondition[] 一组可选的嵌套PathConditions如果存在任何嵌套条件,则在它们删除文件之前,必须都接受。仅当外部条件接受文件(如果文件足够老)时,才会评估嵌套条件。

IfAccumulatedFileCount 条件参数

参数名称 类型 描述
exceeds int 必须。将要删除文件的计数阈值。也就是需要保留的文件数。
nestedConditions PathCondition[] 一组可选的嵌套PathConditions如果存在任何嵌套条件,则在它们删除文件之前,必须都接受。仅当外部条件接受文件(如果超过阈值计数)时,才会评估嵌套条件。

IfAccumulatedFileSize 条件参数

参数名称 类型 描述
exceeds String 必须。将删除文件累计阀值的大小。大小可以指定字节。后缀可以是KB, MB or GB例如:20MB。也就是要保留最接近该值大小的文件。
nestedConditions PathCondition[] 一组可选的嵌套PathConditions如果存在任何嵌套条件,则在它们删除文件之前,必须都接受。仅当外部条件接受文件(如果超过了阈值累积文件大小)时,才会评估嵌套条件。

下面这个示例配置使用的是RollingFileAppender,并且使用的是cron触发策略,是在每天的午夜触发。归档存储的目录是基于当前年和月。在rollover时间内匹配删除基本目录下所有满足参数glob等于*/app-*.log.gz和超过60天或更早的文件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Properties>
    <Property name="baseDir">logs</Property>
  </Properties>
  <Appenders>
    <RollingFile name="RollingFile" fileName="${baseDir}/app.log"
          filePattern="${baseDir}/$${date:yyyy-MM}/app-%d{yyyy-MM-dd}.log.gz">
      <PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
      <CronTriggeringPolicy schedule="0 0 0 * * ?"/>
      <DefaultRolloverStrategy>
        <Delete basePath="${baseDir}" maxDepth="2">
          <IfFileName glob="*/app-*.log.gz" />
          <IfLastModified age="60d" />
        </Delete>
      </DefaultRolloverStrategy>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

下面的示例配置使用的是RollingFileAppender并且触发策略是基于时间和文件大小。在未来100天内会创建100个归档,这些归档存储的目录是基于当前年和月的,并且会以gzip方式进行压缩每个归档,而且rollover是每小时发生一次。这个配置也将会删除匹配*/app-*.log.gz和超过30天或更早的文件。但是会保留大小进100G或者最近的10个文件,先到则为准(whichever comes first.)。我个人理解应该是以文件创建时间为准。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Properties>
    <Property name="baseDir">logs</Property>
  </Properties>
  <Appenders>
    <RollingFile name="RollingFile" fileName="${baseDir}/app.log"
          filePattern="${baseDir}/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
      <PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="100">
        <!--
        Nested conditions: the inner condition is only evaluated on files
        for which the outer conditions are true.
        -->
        <Delete basePath="${baseDir}" maxDepth="2">
          <IfFileName glob="*/app-*.log.gz">
            <IfLastModified age="30d">
              <IfAny>
                <IfAccumulatedFileSize exceeds="100 GB" />
                <IfAccumulatedFileCount exceeds="10" />
              </IfAny>
            </IfLastModified>
          </IfFileName>
        </Delete>
      </DefaultRolloverStrategy>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

ScriptCondition 参数:

参数名称 类型 描述
script Script, ScriptFile or ScriptRef Script元素是用于指定需要执行的逻辑。在基本路径下找到的路径列表,该脚本要是通过的,并且要以java.util.List<PathWithAttributes>返回删除的路径。 参考ScriptFilter文档,如何配置ScriptFilter和ScriptRefs 的示例。

Script 参数:

参数名称 类型 描述
basePath java.nio.file.Path 删除操作开始扫描要删除的文件的目录。可用于相对路径列表中的路径。
pathList java.util.List 在基本路径下找到路径列表直到指定的最大深度,优先排序最近修改的文件。该脚本可以自由修改并返回此列表。
statusLogger StatusLogger StatusLogger通常用于在脚本执行期间记录内部事件。
configuration Configuration 拥有此ScriptCondition的配置。
substitutor StrSubstitutor StrSubstitutor用于替换查找变量。
? String 在配置中声明的任何属性

以下示例配置使用RollingFileAppender并且是cron触发策略,触发时间为每天午夜。归档存储目录是基于当前年和月的。该脚本将返回在基本目录下,日期为13号并且是星期五的rollover文件列表。删除操作将删除脚本返回的所有文件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
  <Properties>
    <Property name="baseDir">logs</Property>
  </Properties>
  <Appenders>
    <RollingFile name="RollingFile" fileName="${baseDir}/app.log"
          filePattern="${baseDir}/$${date:yyyy-MM}/app-%d{yyyyMMdd}.log.gz">
      <PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
      <CronTriggeringPolicy schedule="0 0 0 * * ?"/>
      <DefaultRolloverStrategy>
        <Delete basePath="${baseDir}" maxDepth="2">
          <ScriptCondition>
            <Script name="superstitious" language="groovy"><![CDATA[
                import java.nio.file.*;

                def result = [];
                def pattern = ~/\d*\/app-(\d*)\.log\.gz/;

                pathList.each { pathWithAttributes ->
                  def relative = basePath.relativize pathWithAttributes.path
                  statusLogger.trace 'SCRIPT: relative path=' + relative + " (base=$basePath)";

                  // remove files dated Friday the 13th

                  def matcher = pattern.matcher(relative.toString());
                  if (matcher.find()) {
                    def dateString = matcher.group(1);
                    def calendar = Date.parse("yyyyMMdd", dateString).toCalendar();
                    def friday13th = calendar.get(Calendar.DAY_OF_MONTH) == 13 \
                                  && calendar.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY;
                    if (friday13th) {
                      result.add pathWithAttributes;
                      statusLogger.trace 'SCRIPT: deleting path ' + pathWithAttributes;
                    }
                  }
                }
                statusLogger.trace 'SCRIPT: returning ' + result;
                result;
              ]] >
            </Script>
          </ScriptCondition>
        </Delete>
      </DefaultRolloverStrategy>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

官网地址http://logging.apache.org/log4j/2.x/manual/appenders.html#RollingFileAppender

使用Log4j2记录日志

在Spring Boot中,可以使用注解的方式来记录日志。通过在类中添加注解@Log4j2,可以自动为该类生成一个Logger对象。

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

@Service
@Log4j2
public class UserService {

    public void addUser(String username, String password) {
        log.info("Add user, username: {}, password: {}", username, password);
    }

    public void updateUser(String username, String password) {
        log.info("Update user, username: {}, password: {}", username, password);
    }

    public void deleteUser(String username) {
        log.info("Delete user, username: {}", username);
    }
}

上述代码中,使用了Lombok提供的@Log4j2注解,自动生成了一个名为log的Logger对象。在类中的方法中通过log对象记录了相应的日志信息。

建议使用 log4j2-spring.xml 文件命名配置文件

注意配置文件中

 

${sys:LOG_PATH}  默认路径 是 yml配置文件logging.path 的值
${sys:LOG_FILE} 默认路径是 logging.path的值+ /spring.log
${sys:FILE_LOG_CHARSET} 路径是 yml配置文件中 logging.charset.file 的值
${sys:CONSOLE_LOG_CHARSET}路径是 yml配置文件中 logging.charset.console的值

如何在 Spring Boot 中集成日志框架 SLF4J、Log4j_boot继承log4j-CSDN博客

   如果 Log4j2 的日志配置文件名为 log4j2.xml 或 log4j2-spring.xml,且放在 resource 目录下,那就算是不在 Spring Boot 配置文件中引入此 Log4j2 的日志配置,Spring Boot 也会自动读取该 Log4j2 的日志配置。不过,最好还是显式地引入此配置。

 如果使用了 Maven 多模块,则此 Log4j2 的日志配置文件和 Spring Boot 配置文件只能放在 Spring Boot 入口模块中。不要在一种没有程序启动入口的 Maven 库模块中放置此配置文件

 

 采用yml配置,log4j2.yml

 采用yml配置,需要修改pom.xml,添加如下,否则控制台会报错,同时对应的application.yml需要将log4j2.xml改为log4j2.yml。

<!-- 加上这个才能辨认到log4j2.yml文件 -->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
        </dependency>
Configuration:
  status: OFF
  name: YAMLConfigTest
  properties:
    property:
      name: filename
      value: target/test-yaml.log
  thresholdFilter:
    level: debug
  appenders:
    #只接受程序中DEBUG级别的日志进行处理
    Console:
      name: Console
      target: SYSTEM_OUT
      ThresholdFilter:
        level: DEBUG
        onMatch: ACCEPT
        onMismatch: DENY
      PatternLayout:
        Pattern: "[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"
    #处理DEBUG级别的日志,并把该日志放到logs/debug.log文件中
    RollingFile:
      #打印出DEBUG级别日志,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档
      -
        name: RollingFileDebug
        fileName: ./logs/debug.log
        filePattern: "logs/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          Pattern: "[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"
        Filters:
          ThresholdFilter:
            -
              level: DEBUG
            -
              level: INFO
              onMatch: DENY
              onMismatch: NEUTRAL
        Policies:
          SizeBasedTriggeringPolicy:
            size: 512MB
      #处理INFO级别的日志,并把该日志放到logs/info.log文件中
      -
        name: RollingFileInfo
        fileName: ./logs/info.log
        filePattern: "logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          Pattern: "[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"
        Filters:
          #只接受INFO级别的日志,其余的全部拒绝处理
          ThresholdFilter:
            -
              level: INFO
            -
              level: WARN
              onMatch: DENY
              onMismatch: NEUTRAL
        Policies:
          SizeBasedTriggeringPolicy:
            size: 512MB
      #处理WARN级别的日志,并把该日志放到logs/warn.log文件中
      -
        name: RollingFileWarn
        fileName: ./logs/warn.log
        filePattern: "logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          Pattern: "[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"
        Filters:
          ThresholdFilter:
            -
              level: WARN
            -
              level: ERROR
              onMatch: DENY
              onMismatch: NEUTRAL
        Policies:
          SizeBasedTriggeringPolicy:
            size: 512MB
      #处理error级别的日志,并把该日志放到logs/error.log文件中
      -
        name: RollingFileError
        fileName: ./logs/error.log
        filePattern: "logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          Pattern: "[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"
        ThresholdFilter:
          level: ERROR
        Policies:
          SizeBasedTriggeringPolicy:
            size: 512MB
      #druid的日志记录追加器
      -
        name: druidSqlRollingFile
        fileName: ./logs/druid-sql.log
        filePattern: "logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz"
        PatternLayout:
          Pattern: "[%d{yyyy-MM-dd HH:mm:ss}] %-5level %L %M - %msg%xEx%n"
        Policies:
          SizeBasedTriggeringPolicy:
            size: 512MB
  loggers:
    logger:
      #记录druid-sql的记录
      name: druid.sql.Statement
      level: debug
      additivity: false
      appender-ref:
        ref: druidSqlRollingFile
    #log4j2 自带过滤日志
    Logger:
      -
        name: org.apache.catalina.startup.DigesterFactory
        level: error
      -
        name: org.apache.catalina.util.LifecycleBase
        level: error
      -
        name: org.apache.coyote.http11.Http11NioProtocol
        level: warn
      -
        name: org.apache.sshd.common.util.SecurityUtils
        level: warn
      -
        name: org.apache.tomcat.util.net.NioSelectorPool
        level: warn
      -
        name: org.crsh.plugin
        level: warn
      -
        name: org.crsh.ssh
        level: warn
      -
        name: org.eclipse.jetty.util.component.AbstractLifeCycle
        level: error
      -
        name: org.hibernate.validator.internal.util.Version
        level: warn
      -
        name: org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration
        level: warn
      -
        name: org.springframework.boot.actuate.endpoint.jmx
        level: warn
      -
        name: org.thymeleaf
        level: warn
    root:
      level: DEBUG
      appender-ref:
        -
          ref: Console
        -
          ref: RollingFileInfo
        -
          ref: RollingFileWarn
        -
          ref: RollingFileError
        -
          ref: RollingFileDebug
 

Log4j2 使用

 
 
日志使用规范手册
 
 
(一)日志规约

1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
 
2. 【强制】日志文件至少保存15天,因为有些异常具备以“周”为频次发生的特点。
 
3. 【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:
appName_logType_logName.log。
logType:日志类型,如 stats/monitor/access 等;logName:日志描述。这种命名的好处:
通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。
正例:mppserver 应用中单独监控时区转换异常,如:
mppserver_monitor_timeZoneConvert.log
说明:推荐对日志进行分类,如将错误日志和业务日志分开存放,便于开发人员查看,也便于
通过日志对系统进行及时监控。
 
4. 【强制】对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。
说明:logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
如果日志级别是 warn,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象,会执行 toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。
正例:(条件)建设采用如下方式
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
 
5. 【强制】避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。
正例:<logger name="com.taobao.dubbo.config" additivity="false">
 
6. 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。
正例:logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
 
7. 【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用warn来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志。
说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请
思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
 
8. 【推荐】可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。如非必要,请不要在此场景打出error级别,避免频繁报警。
说明:注意日志输出的级别,error级别只记录系统逻辑出错、异常或者重要的错误信息。
 
9. 【推荐】尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用中文描述即可,否则容易产生歧义。国际化团队或海外部署的服务器由于字符集问题,【强制】使用全英文来注释和描述日志错误信息。
 
 
(二)日志输出文件和日志行文规范

TIPS

以下规范均是在log4j.xml文件中配置,不需要手动创建文件夹,配置文件模板会在后面给出。
 
 
1. 运行时日志输出文件路径
日志文件统一存放在用户目录下的logs文件夹中,根据应用名称生成文件夹进行区分,如图2-1所示。单项目多应用情况下,可根据实际情况修改log4j.xml中的路径(依然存放在logs目录下)。
                        
image.png

图2-1  运行时日志输出文件路径
 
 
2. 存档日志输出文件路径
日志文件统一存放在用户目录下的logs文件夹中,根据应用名称生成文件夹进行区分,然后以年月“yyyy-MM”生成文件夹,以日志级别(如info / warn / error等)或日志类型(如stats / monitor / access等)生成文件夹,如图2-2和图2-3所示。
                        
image.png

图2-2  存档日志输出文件路径1
               
image.png

图2-3  存档日志输出文件路径2
 
 
 
3. 各日志级别文件命名规范
普通类型日志,以应用名称开头 + 日志级别 + 日期 + 序号,以“.log”结尾,如图2-3所示。
其它类型日志,以应用名称开头 + 日志类型 + 描述 + 日期 + 序号,以“.log”结尾,如图2-4所示。
                
image.png

图2-4  其它类型日志文件命名示例
 
 
 
4. 日志清理策略
我们可以利用log4j自带的日志清理功能,当同一文件夹下的文件数量大于设置的阈值时,log4j会清理最旧的一个或多个文件,来满足新文件的生成。
在文件路径规范章节中,我们以日志级别或日志类型为文件夹,这样我们就可以通过设置一个阈值,来根据不同日志级别或类型来定制清理策略。
对应的log4j配置为<DefaultRolloverStrategy max="20"/>,其中max属性值就是上述提到的阈值。详细说明会在后面章节的配置模板中阐述。
 
 
5. 日志行文输出规范
一条日志输出中会包含日期时间、日志级别、输出日志事件发生位置(类相关信息、发生线程、代码中所在行数)以及日志内容,如图2-5所示。
        
image.png

图2-5  日志行文输出规范示例
 
 
 
(三)项目中使用日志系统
 
 
1. SpringBoot框架下使用Log4j2
 
 
(1)Maven依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
因为SpringBoot默认使用的日志框架是Logback,为了避免冲突,需要将它从SpringBoot中移除。
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
         </exclusions>
</dependency>
 
 
(2)编写配置
配置文件命名和存放位置有多种方式,以“log4j2.xml”和“log4j2-spring.xml”命名时,可以直接将配置文件放到项目resources目录下,就不需要在application.yml配置对应的位置;如果想自定义文件名称和路径时,可以在application.yml中配置对应的位置,如图3-1所示。
image.png

图3-1  指定配置文件所在位置

为了统一,请以图3-1的方式为准。
在章节将给出log4j2的配置模板,里面有详细的注释,可以根据实际情况进行更改。
 
 
(3)代码中使用
我们使用Slf4j的api打印日志,如图3-2所示。
image.png

图3-2  使用Slf4j-api打印日志
 
有lombok使用经验的同学可以在项目中引入lombok依赖,然后以图3-3的方式打印日志。
image.png

图3-3  使用lombok打印日志
 
 
 
2. 其它Maven项目下使用Log4j2
除了maven依赖不同外,配置和使用方式与SpringBoot一样。
 
 
Maven依赖
<dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.12.1</version>
    </dependency>
 
 log4j2配置模板

为了让IDE中输出的日志有颜色,可以在输出日志格式中加“%highlight{[%p]}”修饰,IDEA下需要在VM options中添加“-Dlog4j.skipJansi=false”;Eclipse下安装“AnsiConsole”插件即可。
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN" :用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="30"  :间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="30">
    <Properties>
        <!--自定义常量,之后使用${变量名}引用-->
        <Property name="logFilePath">log</Property>
        <Property name="logFileName">test.log</Property>
        <!-- 应用名称 -->
        <Property name="appName">demo</Property>
        <!-- 运行时日志保存路径前缀 -->
        <Property name="fileNamePrefix">${sys:user.home}/logs/${appName}</Property>
        <!-- 存档日志保存路径前缀 -->
        <Property name="filePatternPrefix">${sys:user.home}/logs/${appName}/$${date:yyyy-MM}</Property>
        <!-- 日志输出格式 -->
        <Property name="patternLayout">%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] - %t - %l- :%m%n</Property>
    </Properties>
    <!--appenders:定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingFile]-->
    <appenders>
        <!--console :控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--PatternLayout :输出日志行文的格式 -->
            <PatternLayout pattern="${patternLayout}"/>
        </console>
        <!--File :同步输出日志到本地文件,开发时使用,生产环境下不需要使用 -->
        <!--append="false" :每次清空文件重新输入日志,可用于测试-->
        <File name="log" fileName="${logFilePath}/${logFileName}" append="false">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class %L %M - %msg%xEx%n"/>
        </File>
        <!-- ${sys:user.home} :项目路径 -->
        <RollingFile name="RollingFileInfo" fileName="${fileNamePrefix}/${appName}_info.log"
                     filePattern="${filePatternPrefix}/info/${appName}_info-%d{yyyy-MM-dd}-%i.log.gz">
            <!--ThresholdFilter :日志输出过滤-->
            <!--level="info" :日志级别,onMatch="ACCEPT" :级别在info之上则接受,onMismatch="DENY" :级别在info之下则拒绝-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${patternLayout}"/>
            <!-- Policies :日志滚动策略-->
            <Policies>
                <!-- TimeBasedTriggeringPolicy :时间滚动策略,自定义文件滚动时间间隔,每隔一段时间就产生新文件。
                interval="2":这个参数的含义由filePattern决定,当filePattern中的时间格式为{yyyy-MM-dd}时,代表以2天为间隔;
                              当filePattern中的时间格式为{yyyy-MM-dd HH:mm}时,代表以2分钟为间隔
                modulate="true": boolean型,说明是否对封存时间进行调制。
                               -->
                <TimeBasedTriggeringPolicy interval="2" modulate="true"/>
                <!-- SizeBasedTriggeringPolicy :文件大小滚动策略,大于这个阈值时,会将当前日志文件备份存档 -->
                <SizeBasedTriggeringPolicy size="300 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,超过这个阈值就会清理最旧的日志文件。
                    这里设置了20,理由:假设日志文件大小都小于阈值时,interval=2day,有1个RollingFile都在一个文件夹下,
                    文件夹名称为月份,即一个月理想状态下会产生1 x (30天 / 2) = 15个文件
             -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${fileNamePrefix}/${appName}_warn.log"
                     filePattern="${filePatternPrefix}/warn/${appName}_warn-%d{yyyy-MM-dd}-%i.log.gz">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${patternLayout}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="2" modulate="true"/>
                <SizeBasedTriggeringPolicy size="200 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${fileNamePrefix}/${appName}_error.log"
                     filePattern="${filePatternPrefix}/error/${appName}_error-%d{yyyy-MM-dd}-%i.log.gz">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${patternLayout}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="2" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <!-- 自定义appender -->
        <RollingFile name="MonitorTestController" fileName="${fileNamePrefix}/${appName}_monitor_testController.log"
                     filePattern="${filePatternPrefix}/monitor/${appName}_monitor_testController-%d{yyyy-MM-dd}-%i.log.gz">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="${patternLayout}" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="2" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB" />
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <!--Logger节点用来单独指定日志的形式,name为包路径,比如要为org.springframework包下所有日志指定为INFO级别等。 -->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <logger name="org.apache" level="INFO"></logger>
        <logger name="org.hibernate" level="INFO"></logger>
        <!-- 自定义logger
                避免重复打印日志,浪费磁盘空间,请务必设置additivity为false
         -->
        <logger name="com.fline.demo.log.controller.TestController" level="WARN" additivity="false">
            <appender-ref ref="MonitorTestController" />
        </logger>
        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <root level="all">
            <appender-ref ref="log"/>
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>
</configuration>
配置Log4j2与Logstash的连接
 
 
 
引入Logstash的Maven依赖

因为要用到 GelfAppender ,所以需要引入对应的依赖。

Log4j2与Logstash进行通信,还可以使用SocketAppender,具体的请自行查找相关资料。

GelfAppender 和 SocketAppender 相比,使用Gelf的好处在于,当Logstash重启后,Logstash会继续接收Log4j2发出的日志消息。
<dependency>
  <groupId>biz.paluch.logging</groupId>
  <artifactId>logstash-gelf</artifactId>
  <version>1.14.0</version>
</dependency>

配置Log4j2

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN" :用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="30"  :间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="30">
    <Properties>
        <!--自定义常量,之后使用${变量名}引用-->
        <!-- 应用名称,请务必小写(强制),大写会导致es索引建议失败,造成无法存到es中 -->
        <Property name="appName">demo</Property>
        <!-- 应用版本,请务必小写(强制),大写会导致es索引建议失败,造成无法存到es中 -->
        <Property name="version">1.0.0</Property>
        <!-- 日志输出格式 -->
        <Property name="patternLayout">%d{yyyy-MM-dd HH:mm:ss:SSS} [%p] - %l - :%m%n</Property>
    </Properties>
    <!--appenders:定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingFile]-->
    <appenders>
        <!--console :控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--PatternLayout :输出日志行文的格式-->
            <PatternLayout pattern="${patternLayout}"/>
        </console>
        <!-- 将日志输出到logstash,由logstash输出到es,然后在Kibana的Logs面板可以实时查看日志
                host:logstash配置文件中接收数据的地址
                ignoreExceptions="true"时,代表当前应用会忽略因为gelf产生的相关异常;否则会返回500异常
         -->
        <Gelf name="logstash-gelf" host="udp:172.16.12.11" port="4567" version="1.1" ignoreExceptions="true">
            <!-- 这里只接收error级别的异常 -->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 这里定义的字段,会被logstash接收使用 -->
            <Field name="timestamp" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}" />
          	<Field name="thread" pattern="%t" />
            <Field name="logger" pattern="%l" />
            <Field name="level" pattern="%level" />
            <!-- 当前应用所在服务器的系统节点名称 -->
            <Field name="server" pattern="%host" />
            <!-- 为当前应用指定 es索引名称,在logstash中被应用于创建索引 -->
            <Field name="index" pattern="log4j2-${appName}-v${version}" />
        </Gelf>
    </appenders>
    <loggers>
        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <root level="all">
            <appender-ref ref="Console" />
            <appender-ref ref="logstash-gelf" />
        </root>
    </loggers>
</configuration>

 安装及配置Logstash

 

官方下载地址:https://artifacts.elastic.co/downloads/logstash/logstash-7.6.2.tar.gz

下载下来,直接解压即可完成安装。

 
 

配置logstash.yml

如果你没有配置X-PACK, 可直接跳过这一小节

我们全局打开logstash.yml文件,全局查找 X-Pack Monitoring ,添加以下配置。

用户名必须是"logstash_system",密码是你在设置X-PACK时设置的密码,如果不记得了,可以在kibana中修改密码。

xpack.monitoring.enabled: true
xpack.monitoring.elasticsearch.username: "logstash_system"
xpack.monitoring.elasticsearch.password: "123456"
xpack.monitoring.elasticsearch.hosts: ["http://ip:9200"]
创建logstash配置文件

1、 在 logstash 的 config 目录下创建如下配置文件,名称随意,这里是 log4j2-es.conf 。

input 配置是定义日志输入地址,然后在 log4j2 中使用该地址

filter 模块可自行查找资料,这里不做介绍

output 配置是定义日志输出地址, stdout 是指输出到 logstash 控制台, elasticsearch 是指输出到 es 中

2、 在配置连接 es 的用户名密码前,请到 kibana 中创建角色和用户。

创建具有操作索引权限的角色,如下图所示,索引带有通配符。请注意,我们在log4j2中配置了创建的索引名称前缀为 log4j2 
image.png

创建拥有此角色的用户,不再演示。

创建的配置文件,如下所示。
input {
    gelf {
        host => "172.16.12.11"
        port => 4567
    }
}
filter {
  mutate {
    lowercase => [ "logger", "level" ]
  }
}
output {
    stdout {
        codec => rubydebug
    }
    elasticsearch {
        hosts => ["172.16.12.11:9200"]
        index => "%{[index]}"
        user => "log4j2"
        password => "log4j2"
    }
}

 

启动logstash

进入 logstash 安装目录,执行下面的命令。
 
可以通过修改 logstash.yml 或添加参数的方式来指定运行数据存放位置,如启动时遇到下面的问题,请指定数据存放位置
默认存放在${LOGSTASH_HOME}/data
 
 

为了可以自动检测配置文件的变动和自动重新加载配置文件,需要在启动的时候使用以下命令
 
默认,检测配置文件的间隔时间是3秒,可以通过以下命令改变
 

输出下方语句时代表启动成功。
 
 
 
测试

项目中产生 error 级别日志后,如果logstash配置了在控制台输出,我们会看到下面的输出信息
image.png


 
 
在Kibana中查看日志信息

打开 Kibana 控制台,在左侧菜单栏中找到 Logs 菜单,然后打开它,如下图所示。
image.png

然后设置要查看的索引,如下图所示。
image.png

往下拉,可以设置要展示的字段,这些字段就是在 Log4j2 的配置文件中设置的,如下图所示。设置完点击应用,进行保存。
image.png

然后点击 tab 菜单中的流式传输,即可看到日志信息,如下图所示。
image.png

因为数据是被保存到了 ES 中,所以我们可以使用 ES 的 API ,灵活的管理日志,实现日志中的价值。
 

支持其他结构化日志记录格式

Spring Boot 中的结构化日志记录支持是可扩展的,允许您定义自己的自定义格式。为此,请实现StructuredLogFormatter接口。使用 Logback 时,通用类型参数必须是ILoggingEvent ;使用 Log4j2 时,通用类型参数必须是LogEvent (这意味着您的实现与特定的日志记录系统相关)。然后使用日志事件调用您的实现并返回要记录的String ,如本示例所示:

import ch.qos.logback.classic.spi.ILoggingEvent;

import org.springframework.boot.logging.structured.StructuredLogFormatter;

class MyCustomFormat implements StructuredLogFormatter<ILoggingEvent> {

	@Override
	public String format(ILoggingEvent event) {
		return "time=" + event.getInstant() + " level=" + event.getLevel() + " message=" + event.getMessage() + "\n";
	}

}
posted @ 2025-01-15 15:54  CharyGao  阅读(389)  评论(0)    收藏  举报