springboot 使用 log4j2 进行日志记录、并对文件日志以日期和大小进行拆分的 demo 示例

本文介绍了一个SpringBoot项目如何使用Log4j2进行日志记录,包括配置控制台日志输出、文件日志输出及日志文件按日期和大小进行归档的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 描述

一个springboot项目使用 log4j2 记录程序运行过程中的日志, 配置 log4j2 生成控制台日志和文件日志记录,以及对文件日志以日期和大小进行拆分的 demo示例。

环境:

IDE(idea)2021.3
JDK:1.8
maven:3.8.4
spring boot:2.5.6 
log4j-core: 2.14.1 (spring-boot-starter-log4j2: 2.56)

提示:

写这个demo的过程中遇到不少问题(写在最后)。 文档和源码都看得我有点懵,感觉比 logback 的上手难度大不少。

2. 结果

  • 控制台日志格式:
    在这里插入图片描述
  • 文件日志格式:
    在这里插入图片描述
  • 日志拆分归档:eg: 日志文件最大10KB,总文件大小20KB,最长时间30天, 当前策略目录下总文件格式19, 满足条件的总文件数20(Delete配置,不止针对当前策略)
    在这里插入图片描述
    在这里插入图片描述

3. demo

用了个定时任务在打印日志, 输出的日志文件用zip压缩格式进行了归档。文件大小设置的较小,以便于测试。

3.0 项目结构
在这里插入图片描述

3.1 pom.xml

  • 引入 log4j2 相关jar包(排除出web依赖中已有logging依赖)。
		<!--提供全栈的 web 开发特性,包括 Spring MVC 依赖和 Tomcat 容器-->
        <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>

        <!-- 引入log4j2依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!-- Required for AsyncLoggers -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.4</version>
        </dependency>

3.2 SpringBootApplication

  • 使用了EnableScheduling 注解,启用定时任务。
@Slf4j
@EnableScheduling // 启用定时任务
@SpringBootApplication
public class DemoLogLog4j2Application {
    public static void main(String[] args) {
        ConfigurableEnvironment environment = SpringApplication.run(DemoLogLog4j2Application.class, args).getEnvironment();
        String port = environment.getProperty("server.port");
        String contextPath = environment.getProperty("server.servlet.context-path");
        log.info("----- http://localhost:{}{} -----", port, contextPath);
    }
}

3.3 application.yml配置

server:
  port: 8880
  servlet:
    context-path: /demo

3.4 log4j2-spring.xml配置

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

<!--设置log4j2的自身log级别为warn-->
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<!--strict 默认false,xml使用strict风格时,需要配置strict="true" -->
<!--No need to set system property "log4j2.contextSelector" to any value when using <asyncLogger> or <asyncRoot>. -->
<configuration status="warn" monitorInterval="30" strict="false">

    <!-- 全局参数 -->
    <Properties>

        <!-- 控制台日志格式 -->
        <property name="CONSOLE_LOG_PATTERN"
                  value="%d %pid ${LOG_LEVEL_PATTERN:-%5p} --- [%15.15t] %-40.40logger{39} %line : %m%n"/>
        <!--    <property name="FILE_LOG_PATTERN"-->
        <!--              value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } -&#45;&#45; [%t] %-40.40logger{39} %line : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>-->
        <!--格式化输出:%d表示日期,%thread或%t表示线程名,%-5level:级别从左显示5个字符宽度, %line: 行号,%msg或%m:日志消息,%n是换行符-->
        <property name="FILE_LOG_PATTERN" value="%d %pid %-5level --- [%t] %-33.33logger{32} %line : %m%n"/>

        <!-- 全局参数 -->
        <property name="LOG_HOME" value="/var/logs"/>
        <property name="LOG_NAME" value="demo_log_log4j2"/>
        <property name="LOG_PATH">${LOG_HOME}/${LOG_NAME}</property>
        <!-- 和上面的写法一个意思 -->
        <!-- <property name="LOG_PATH" value="${LOG_HOME}/${LOG_NAME}"/> -->

    </Properties>

    <Appenders>
        <!-- 日志输出:ConsoleAppender-控制台输出。 -->
        <Console name="STDOUT" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${CONSOLE_LOG_PATTERN}"/>
        </Console>

        <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
        <!-- 日志输出:RollingFileAppender-日志文件输出:根据rollingPolicy(滚动策略)和TriggeringPolicy(触发策略)输出日志 -->
        <RollingFile name="FILE_INFO" fileName="${LOG_PATH}/${LOG_NAME}.log"
                     immediateFlush="true" append="true"
                     filePattern="${LOG_PATH}/info/${LOG_NAME}_%d{yyyy-MM-dd}.part_%i.log.zip">

            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${FILE_LOG_PATTERN}"/>

            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10KB"/>
            </Policies>
            <!--最多可以保存19个文件(max只对当前时间分片有效), 文件序号到达最大值后,会删除最早的文件,并对剩下的历史文件重命名-->
            <DefaultRolloverStrategy max="19">
                <!--可以删除任何文件,而不仅仅是滚动的日志文件!使用testMode参数,不删除,打印一条info级别日志信息-->
                <Delete basePath="${LOG_PATH}" maxDepth="2">
                    <IfFileName glob="*/*.log.zip">
                        <IfAny>
                            <!-- age的单位:D、H、M、S,分别表示天、小时、分钟、秒。 可以混用,eg: P1513H -->
                            <IfLastModified age="P30D" />
                            <!-- 文件总大小 -->
                            <IfAccumulatedFileSize exceeds="10 KB" />
                            <!-- 文件总个数 -->
                            <IfAccumulatedFileCount exceeds="20" />
                        </IfAny>
                    </IfFileName>
                </Delete>
            </DefaultRolloverStrategy>
            <!--最多可以保存10个文件, 不会对历史文件重命名, 序号会继续增加; 但是Appender不能指定fileName属性-->
            <!--            <DirectWriteRolloverStrategy maxFiles="10"/>-->
        </RollingFile>

        <!-- 日志输出:RollingRandomAccessFile-日志文件输出:根据rollingPolicy(滚动策略)和TriggeringPolicy(触发策略)输出日志 -->
        <!-- 始终处于缓冲状态(无法关闭),不支持文件锁定;-->
        <RollingRandomAccessFile name="FILE_ERROR" fileName="${LOG_PATH}/${LOG_NAME}_error.log"
                                 append="true"
                                 filePattern="${LOG_PATH}/error/${LOG_NAME}_%d{yyyy-MM-dd}.part_%i.log.zip">

            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${FILE_LOG_PATTERN}"/>

            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10KB"/>
            </Policies>
            <!-- max只对当前时间分片有效 -->
            <DefaultRolloverStrategy max="10">
<!--                <Delete basePath="${LOG_PATH}" maxDepth="2" testMode="true">-->
<!--                    <IfFileName glob="*/*.log.zip" />-->
<!--                    <IfLastModified age="10D" />-->
<!--                    <IfAccumulatedFileCount  exceeds="15" />-->
<!--                    <IfAccumulatedFileSize exceeds="20KB" />-->
<!--                </Delete>-->
            </DefaultRolloverStrategy>
        </RollingRandomAccessFile>
    </Appenders>

    <Loggers>
        <AsyncLogger name="com.demo" level="DEBUG" includeLocation="true">
            <AppenderRef ref="FILE_INFO"/>
            <AppenderRef ref="FILE_ERROR"/>
        </AsyncLogger>

        <!--        <logger name="com.demo" level="ERROR" includeLocation="true">-->
        <!--            <AppenderRef ref="FILE_ERROR"/>-->
        <!--        </logger>-->

        <Root level="INFO">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FILE_INFO"/>
            <AppenderRef ref="FILE_ERROR"/>
        </Root>
    </Loggers>

</configuration>

3.5 其他代码

  • SchedulingConfig.java
/**
 * 定时任务配置
 *
 * @author byrc
 * @date 2022/3/14
 */
@Configuration
public class SchedulingConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //设定一个长度10的定时任务线程池,  多线程定时任务
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(9));
    }
}
  • DemoTask.java
@Slf4j
@Component
public class DemoTask {

    @Scheduled(cron = "0/30 * * * * ?")  //@Scheduled来创建定时任务 这个注解用来标注一个定时任务方法
    public void action1() {
        log.info("----- Drink water ------------------------");
    }

    @Scheduled(cron = "33 */1 * * * ?")
    public void action2() {
        log.debug("----- debug -------------");
        log.info("----- info -----------");
        log.warn("----- warn -------------");
        log.error("----- error -------------");
    }

    @Scheduled(cron = "0/3 * * * * ?")
    public void action3() {
        log.debug("---action3-- debug ---1----------");
        log.info("---action3-- info -----1------");
        log.warn("---action3-- warn -----1--------");
        log.error("--action3--- error ----1---------");
        log.info("---action3-- info ----2-------");
        log.warn("---action3-- warn -----2--------");
        log.error("--action3--- error --2-----------");
        log.error("--action3--- error -----ADSL飞洒发的---");
    }

4. 资料

5. 注

  • spirng boot 已在父项目引入(父项目pom.xml配置);
  • 部分jar包版本已在父项目管理(如果对应不上,一定、肯定、决定是某些修改,没同步更新文档)。

6. 问题记录

仅列举遇到的费劲一点的问题。

  1. AsyncLogger错误 NoClassDefFoundError https://blog.csdn.net/besto229/article/details/123582303
  2. log4j2 的xml配置有两种风格(concise and strict:简洁和严格)https://blog.csdn.net/besto229/article/details/123641670
  3. DefaultRolloverStrategy的Delete元素下面的条件要同时生效,需要IfAny(任何一个条件满足就行) 。
  4. 还有遇到很多问题,参考log4j2-spring.xml文件中的注释信息。

写在最后:

写了logback的demo再写的这个demo。 感觉要难好多,功能也要多不少,转来转去的看官方文档(English is a sad story)。 --------- 仅个人感受

  • 照例,附上一张log4j2的类图。
    在这里插入图片描述
  • 感受一下 appender 类
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值