1. 描述

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

环境:

IDE(idea):2021.3
JDK:1.8
maven:3.8.4
spring boot:2.5.6 
logback-classic: 1.2.6 (spring-boot-starter-web --- spring-boot-starter --- spring-boot-starter-logging)

提示:

当前版本SizeAndTimeBasedFNATP 已弃用(SizeAndTimeBasedFNATP is deprecated.Use SizeAndTimeBasedRollingPolicy)

2. 结果

  • 控制台日志格式:
  • 文件日志格式:
  • 日志拆分归档:eg: 日志文件最大10KB,总文件大小20KB,最长时间30天

3. demo

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

3.0 项目结构

springboot linx部署日志文件写入 springboot 日志文件位置_sed

3.1 pom.xml

  • 引入 logback 相关jar包(web依赖中已有)。
<!-- web 包括了spring-boot-starter-logging, 默认使用logback -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

3.2 SpringBootApplication

  • 使用了EnableScheduling 注解,启用定时任务。
@Slf4j
@EnableScheduling // 启用定时任务
@SpringBootApplication
public class DemoLogLogbackApplication {
    public static void main(String[] args) {
        ConfigurableEnvironment environment = SpringApplication.run(DemoLogLogbackApplication.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 logback-spring.xml配置

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

<!--
scan: 当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为 true.
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒.当 scan 为 true 时,此属性生效.默认的时间间隔为 1 分钟.
debug: 当此属性设置为 true 时,将console上打印出 logback 内部日志信息,实时查看 logback 运行状态, 但这些logback内部日志不会记录到log文件中. 默认值为 false.
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>

    <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>

    <!-- 全局参数 -->
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${PID:- }){magenta} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %line %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <!--    <property name="FILE_LOG_PATTERN"-->
    <!--              value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%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_logback"/>
    <property name="LOG_PATH" value="${LOG_HOME}/${LOG_NAME}"/>

    <!-- 日志输出:ConsoleAppender-控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoder的class属性默认值为PatternLayoutEncoder -->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 日志输出:RollingFileAppender-根据rollingPolicy(滚动策略)和TriggeringPolicy(触发策略)输出日志 -->
    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--ThresholdFilter 过滤掉低于配置级别的-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!--活动日志文件(当前写入日志的文件,没配置时,使用下面的归档日志文件做活动日志文件)-->
        <file>${LOG_PATH}/${LOG_NAME}.log</file>
        <!-- 滚动策略: TimeBasedRollingPolicy按时间滚动;SizeAndTimeBasedRollingPolicy按日期归档文件,但同时限制每个日志文件的大小-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径&日志文件输出的文件名-->
            <FileNamePattern>${LOG_PATH}/info/${LOG_NAME}_%d{yyyy-MM-dd}.part_%i.log.zip</FileNamePattern>
            <!--日志文件保留天数(maxHistory属性优先级高于totalSizeCap)-->
            <maxHistory>15</maxHistory>
            <!-- 总文件超出大小删除老文件(需要设置maxHistory后生效) -->
            <!-- <totalSizeCap>1GB</totalSizeCap>-->
            <totalSizeCap>20KB</totalSizeCap>
            <!-- appender启动时,进行一次日志文件清理(作用:有些存活很短时间的应用,没机会进行文件清理) -->
            <!-- <cleanHistoryOnStart>true</cleanHistoryOnStart>-->

            <!-- SizeAndTimeBasedFNATP 被弃用. 参考下一个appender配置,Use SizeAndTimeBasedRollingPolicy instead. -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
        </encoder>
    </appender>

    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--LevelFilter 不等于配置级别的日志做过滤或不过略处理-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <!--匹配到(允许-ACCEPT,禁止-DENY)-->
            <onMatch>ACCEPT</onMatch>
            <!--没有匹配到-->
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${LOG_NAME}_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/error/${LOG_NAME}_error.%d{yyyy-MM-dd}.part_%i.log.zip</FileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>20KB</totalSizeCap>
            <maxFileSize>10KB</maxFileSize>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
        </encoder>
    </appender>

    <logger name="com.demo" level="DEBUG"/>
    <root>
        <level value="INFO"/>
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE_INFO"/>
        <appender-ref ref="FILE_ERROR"/>
    </root>

</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包版本已在父项目管理(如果对应不上,一定、肯定、决定是某些修改,没同步更新文档)。

写在最后:

相比log4j2来看,logback 配置上手难度要低好多l,代码更少,更清晰一些。而log4j2 代码更多,功能扩展更多,更强大,也更细化(仅是Appender的可实例化类就是logback倍数)。 --------- 仅个人感受

  • 附上一张logback的appender的类图,对比配置感觉很清晰,很好理解。