likes
comments
collection
share

整合日志框架log4j2

作者站长头像
站长
· 阅读数 19

其实我们之前就一直在用日志框架了,只不过spring boot为我们整合好了默认的日志框架的实现。我们通过Lombok框架提供的日志相关的注解再配合编辑器的插件,直接用的内置对象log进行日志api的调用。然后日志默认在控制台打印输出。

现在我们将对日志框架做进一步的定制,我们希望日志可以输出到文件中保存起来,方便我们日后排查问题。废话不多说,开干!

引入依赖

因为spring boot默认会包含spring-boot-starter-logging依赖,其中使用logback的日志实现,现在我们要用log4j2,则需要在它所有的依赖中,都将这个模块排除,一个最简单的排除方式:

configurations {
    all*.exclude module: 'spring-boot-starter-logging'
    ...
}

同时还要引入如下依赖:

dependencies {
    ...
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'
}

日志配置

接下来我们需要在resources资源包下新建一个log4j2.xml文件,在其中配置日志:

整合日志框架log4j2

日志级别

日志的级别优先级从高到低:

  • OFF

  • FATAL(导致应用退出的重大错误)

  • ERROR(不影响系统继续运行的错误)

  • WARN

  • INFO

  • DEBUG

    调试程序输出的信息

  • TRACE

    输出调用栈中每一步的详细的跟踪日志

  • ALL

    打印所有的日志

配置结构

现在我们写一个日志配置的基本结构:

<?xml version="1.0" encoding="UTF-8"?>
<!-- status记录的是log4j在抓取和输出日志过程中自身输出的日志级别,这里输出fatal以及以上优先级的级别的日志 -->
<configuration status="fatal">
    <Properties>
        <!-- 这里定义被下面配置引用的常量 -->
    </Properties>

    <Appenders>
        <!-- 用于向控制台输出日志 -->
        <Console name="consoleLog" target="SYSTEM_OUT">

        </Console>

        <!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
        <RollingFile name="fileInfoLog">

        </RollingFile>

        <!-- error级别日志文件输出 -->
        <RollingFile name="fileErrorLog">

        </RollingFile>

        <!-- 异步日志记录器,在写文件时实现异步写入,提高程序执行性能 -->
        <Async name="myAsync">
            <AppenderRef ref="fileInfoLog"/>
            <AppenderRef ref="fileErrorLog"/>
        </Async>

    </Appenders>

    <loggers>
        <!-- 配置程序各个模块(包)所使用的日志记录器以及配置日志输出的级别 -->
        <!-- 所有模块继承的默认设置,子模块可以覆盖 -->
        <Root level="warn">
            <AppenderRef ref="consoleLog"/>
        </Root>
        <!-- 对我们的应用指定日志输出级别为info,并且从根设置继承,支持控制台输出,默认additivity为true -->
        <logger name="com.juan" level="info" additivity="true">
            <AppenderRef ref="myAsync"/>
        </logger>
    </loggers>
</configuration>

日志文件输出路径

这里我们把要输出的日志文件的路径设置为一个常量,值表示的是相对于项目根路径的路径,这里我们设置为本地开发运行、测试时和项目同一层级的logs目录下:

<Properties>
    <!-- 这里定义被下面配置引用的常量 -->
    <Property name="LOG_HOME" value="../logs"/>
</Properties>

日志Layout

对于输出到文件中的日志格式,我们也定义在常量中:

<Properties>
    ...
    <Property name="File_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %6p %5pid --- [%15.15t] %-40.40logger{39}: %m%n"/>
</Properties>

关于这里的格式可以参考logback的官方文档layout章节,因为格式方面日志框架基本相通的,且logback这方面文档写的更好。

控制台日志

现在我们div控制台日志的输出风格,这里下卷直接给出一个仿spring boot控制台默认的输出风格的布局:

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="fatal">

    <Appenders>
        <!-- 用于向控制台输出日志 -->
        <Console name="consoleLog" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t] %style{%-40.40logger{39}}{bright,cyan}: %highlight{%m%n}{INFO=BLACK, DEBUG=BLACK}" disableAnsi="false"/>
        </Console>

    </Appenders>

    <loggers>
        <Root level="info">
            <AppenderRef ref="consoleLog"/>
        </Root>
    </loggers>
</configuration>

按照以上配置后,小伙伴们可以自行运行一个单元测试来看看控制台的输出效果。

文件日志配置

接下来我们再来完善下文件的日志配置,这里我们将输出到文件中的日志分为两类:info日志和error日志。我们主要来看下info日志文件的配置,error的则类似。

<!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
<RollingFile name="fileInfoLog" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/info_%i.%d{yyyy-MM-dd-HH}.log" append="true">
    <!-- 过滤器 -->
    <Filters>
        <!--只接受info级别的日志,其它全部拒绝掉-->
        <ThresholdFilter level="INFO"/>
        <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
    </Filters>
    <!-- 日志格式 -->
    <PatternLayout pattern="${File_LOG_PATTERN}"/>
    <!-- 策略 -->
    <Policies>
        <!-- 每隔一天转存 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 文件大小 -->
        <SizeBasedTriggeringPolicy size="10 MB"/>
    </Policies>
    <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始自动清理-->
    <DefaultRolloverStrategy max="10">
        <Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
            <IfFileName glob="*/*.log" />
            <!--7天-->
            <IfLastModified age="168H" />
        </Delete>
    </DefaultRolloverStrategy>
</RollingFile>

这里的fileName指定的是当前日志的输出路径,filePattern则是被转储后的文件路径,这里我们会以日期来划分子目录,并且文件的写入形式为追加,而不是覆盖(append="true"控制,默认为true),当一个文件写满了(达到<SizeBasedTriggeringPolicy>配置的大小)后,再按照命名中的%i以索引自增的方式,生成新的文件,也就是我们说的日志滚动,这里我们指定了日志滚动的大小为10 MB。最后我们还指定了日志文件的清理策略。

error日志的配置类似,处理文件命名,还有过滤的规则不一样:

<Filters>
    <!-- 只考虑error级别 -->
    <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>

然后,我们为文件日志配置异步记录器:

<Async name="myAsync">
    <AppenderRef ref="fileInfoLog"/>
    <AppenderRef ref="fileErrorLog"/>
</Async>

并为我们的小卷生鲜应用模块(包)指定下要记录的日志级别和记录器:

<logger name="com.xiaojuan" level="info">
    <AppenderRef ref="myAsync"/>
</logger>

示例配置

最后为了方便小伙伴们练习,小卷给出完整的日志配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!-- status记录的是log4j在抓取和输出日志过程中自身输出的日志级别,这里输出fatal以及以上优先级的级别的日志 -->
<configuration status="fatal">
    <Properties>
        <!-- 这里定义被下面配置引用的常量 -->
        <Property name="LOG_HOME" value="../logs"/>
        <Property name="File_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %6p %5pid --- [%15.15t] %-40.40logger{39}: %m%n"/>
    </Properties>

    <Appenders>
        <!-- 用于向控制台输出日志 -->
        <Console name="consoleLog" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t] %style{%-40.40logger{39}}{bright,cyan}: %highlight{%m%n}{INFO=BLACK, DEBUG=BLACK}" disableAnsi="false"/>
        </Console>

        <!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
        <RollingFile name="fileInfoLog" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/info_%i.%d{yyyy-MM-dd-HH}.log" append="true">
            <!-- 过滤器 -->
            <Filters>
                <!--只接受info级别的日志,其它全部拒绝掉-->
                <ThresholdFilter level="INFO"/>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <!-- 日志格式 -->
            <PatternLayout pattern="${File_LOG_PATTERN}"/>
            <!-- 策略 -->
            <Policies>
                <!-- 每隔一天转存 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <!-- 文件大小 -->
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始自动清理-->
            <DefaultRolloverStrategy max="10">
                <Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
                    <IfFileName glob="*/*.log" />
                    <!--7天-->
                    <IfLastModified age="168H" />
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <!-- error级别日志文件输出 -->
        <RollingFile name="fileErrorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/error_%i.%d{yyyy-MM-dd-HH}.log" append="true">
            <!-- 过滤器 -->
            <Filters>
                <!-- 只考虑error级别 -->
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <!-- 日志格式 -->
            <PatternLayout pattern="${File_LOG_PATTERN}"/>
            <Policies>
                <!-- 每隔一天转存 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <!-- 文件大小 -->
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10">
                <Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
                    <IfFileName glob="*/*.log" />
                    <!--7天-->
                    <IfLastModified age="168H" />
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <!-- 异步日志记录器,在写文件时实现异步写入,提高程序执行性能 -->
        <Async name="myAsync">
            <AppenderRef ref="fileInfoLog"/>
            <AppenderRef ref="fileErrorLog"/>
        </Async>

    </Appenders>

    <loggers>
        <!-- 配置程序各个模块(包)所使用的日志记录器以及配置日志输出的级别 -->
        <!-- 所有模块继承的默认设置,子模块可以覆盖 -->
        <Root level="debug">
            <AppenderRef ref="consoleLog"/>
        </Root>
        <logger name="org.springframework" level="info">

        </logger>
        <logger name="org.mybatis" level="info">

        </logger>
        <!-- 对我们的应用指定日志输出级别为info,并且从根设置继承,支持控制台输出,默认additivity为true -->
<!--        <logger name="com.xiaojuan" level="info" additivity="true">-->
        <logger name="com.xiaojuan" level="info">
            <AppenderRef ref="myAsync"/>
        </logger>
    </loggers>
</configuration>

注意

最后为了查看spring boot整合的框架启动的debug信息,我们可以将Root级别设置为debug,这样很多框架都会打印debug信息。我们可以将不要打印debug信息的框架(包)的日志级别设置为info

一般来说,我们开发不关心debug日志输出,推荐设置为下面的配置:

<loggers>
    <Root level="info">
        <AppenderRef ref="consoleLog"/>
    </Root>
    <logger name="com.xiaojuan">
        <AppenderRef ref="myAsync"/>
    </logger>
</loggers>