SpringBoot集成Logback日志框架全攻略

摘要: 在Java企业级应用开发中,日志记录是不可或缺的一部分。它不仅帮助开发者在开发过程中追踪和调试问题,还在应用程序部署后,用于监控性能和记录关键事件。Spring Boot作为当前最流行的Java框架之一,通过其自动配置特性,极大地简化了日志的集成。本文将深入探讨Spring Boot与Logback日志框架的集成过程,包括基本配置、高级特性以及最佳实践。

一、Logback简介

Logback是SLF4J(Simple Logging Facade for Java)的一个实现,由同一个作者开发。它被认为是Log4j的继任者,提供了更快的速度和更好的功能。Logback包括三个主要组件:Logger、Appender和Layout。Logger负责记录日志信息,Appender负责将日志信息输出到目的地(如控制台、文件等),而Layout则负责定义日志的格式。

Spring Boot在创建新项目时,如果未指定其他日志框架,则默认使用Logback作为日志实现。这意呀着,你不需要进行任何额外的配置即可开始使用Logback进行日志记录。Spring Boot通过spring-boot-starter-logging依赖来引入Logback,该依赖是spring-boot-starter的传递性依赖之一。

二、Logback配置文件

在Spring Boot项目中,Logback的配置文件通常是logback-spring.xml(推荐在Spring Boot项目中使用,因为它提供了对Spring环境的更好支持)或logback.xml,这些文件通常放在src/main/resources目录下。

示例配置: logback-spring.xml

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

    <!-- 定义日志文件的存储地址 -->
    <property name="LOG_HOME" value="logs"/>

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名 -->
            <FileNamePattern>${LOG_HOME}/springboot-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!-- 日志文件保留天数 -->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志级别设置 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

	<!-- 为特定包设置日志级别 -->  
    <logger name="com.example.myapp" level="debug" additivity="false">  
        <appender-ref ref="STDOUT" />  
    </logger> 

</configuration>

在这个配置文件中,我们定义了一个控制台输出的Appender以及日志文件的Appender,并设置了日志的格式。然后,我们设置了根日志级别为INFO,并指定了使用我们定义的STDOUT和FILE Appender。此外,我们还为com.example.myapp包下的类设置了DEBUG级别的日志。通过这些配置,将会把日志输出到控制台(STDOUT)和文件(FILE)。

三、在Spring Boot中使用Logback

在Spring Boot项目中,你可以通过注入Logger对象来记录日志。这通常在你的服务类或组件类中完成。

示例代码:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Service;  
  
@Service  
public class MyService {  
  
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);  
  
    public void doSomething() {  
        logger.info("Doing something...");  
        // 业务逻辑  
    }  
}

你也可以使用lombok提供的注解@Slf4j简化开发,减少日志对象的声明操作。

import lombok.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Slf4j
public class MyClass {
    
    public void doSomething() {
        // 使用@Slf4j注解生成的日志对象
        log.debug("This is a debug message.");
        log.info("This is an info message.");
        log.warn("This is a warning message.");
        log.error("This is an error message.");
    }
}

四、高级配置与最佳实践

1. 文件滚动存储

对于生产环境,你可能需要将日志输出到文件,并设置文件滚动和归档策略,以避免单个日志文件过大。Logback提供了RollingFileAppender来实现这一功能。

RollingFileAppender允许你根据时间、文件大小或其他条件来滚动日志文件,并将旧的日志文件归档。如基于时间的TimeBasedRollingPolicy、基于文件大小的SizeBasedTriggeringPolicy以及它们的组合SizeAndTimeBasedRollingPolicy等。

基于时间的滚动策略

<!-- 定义RollingFileAppender,指定日志文件的路径、名称以及滚动策略 -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
	 <!-- 日志存储文件路径 --> 
    <file>/path/to/yourlogfile.log</file>  
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
        <!-- 每天滚动一次,并保留30天的历史记录 -->  
        <fileNamePattern>/path/to/yourlogfile.%d{yyyy-MM-dd}.log</fileNamePattern> 
        <!--日志最长保留时间,单位:天--> 
        <maxHistory>30</maxHistory>  
    </rollingPolicy>  
    <encoder> 
    	<!--定义日志格式--> 
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>  
    </encoder>  
</appender>

基于文件大小和时间的滚动策略

<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">  
    <file>/path/to/yourlogfile.log</file>  
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  
        <!-- 每天滚动一次,每个文件最大10MB,并保留30天的历史记录 -->  
        <fileNamePattern>/path/to/yourlogfile.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>  
        <maxFileSize>10MB</maxFileSize>  
        <maxHistory>30</maxHistory>  
        <totalSizeCap>3GB</totalSizeCap> <!-- 可选,限制所有归档文件的总大小 -->  
    </rollingPolicy>  
    <encoder>  
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>  
    </encoder>  
</appender>

在上面的示例中,%d{yyyy-MM-dd}用于指定日期格式,%i是一个递增的索引,用于在同一天内区分多个日志文件(当单个文件大小超过maxFileSize时)。.gz表示归档文件将被压缩。

提醒,你需要在 <root> 标签或特定的 <logger>标签中引用你定义的RollingFileAppender,以便将日志输出到指定的文件,并根据配置的滚动策略进行滚动和归档。

2. 异步日志

在高并发场景下,日志记录可能会成为性能瓶颈。Logback提供了AsyncAppender,允许你以异步方式记录日志,从而提高应用程序的性能。AsyncAppender可以将日志事件异步地写入到另一个Appender中,从而减少对主应用程序性能的影响。

使用步骤:

  1. 定义一个正常的Appender:首先,你需要定义一个正常的Appender,用于处理日志事件,比如RollingFileAppender或ConsoleAppender。
  2. 配置AsyncAppender:然后,你需要配置一个AsyncAppender,并将其appender-ref指向你之前定义的Appender。这样,AsyncAppender就会将日志事件异步地写入到你指定的Appender中。
  3. 配置Logger或Root Logger使用AsyncAppender:最后,你需要在Logger或Root Logger的配置中引用AsyncAppender,以便将日志事件发送到它。

配置示例:

<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
  
    <!-- 定义一个正常的RollingFileAppender -->  
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">  
        <file>logs/app.log</file>  
        <encoder>  
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>  
        </encoder>  
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>  
            <maxHistory>30</maxHistory>  
        </rollingPolicy>  
    </appender>  
  
    <!-- 配置AsyncAppender,并指向FILE Appender -->  
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">  
        <!-- 队列大小,默认为256,可以根据需要调整 -->  
        <queueSize>512</queueSize>  
        <!-- 当队列剩余容量小于discardingThreshold时,会丢弃INFO及以下级别的日志;默认为-1(queueSize的20%),设置为0表示不丢弃 -->  
        <discardingThreshold>0</discardingThreshold>  
        <!-- 队列满后是否阻塞,默认为false(不阻塞,丢弃日志);如果设置为true,则队列满后阻塞 -->  
        <neverBlock>false</neverBlock>  
        <!-- 附加的Appender -->  
        <appender-ref ref="FILE"/>  
    </appender>  
  
    <!-- 配置Root Logger使用AsyncAppender -->  
    <root level="DEBUG">  
        <appender-ref ref="ASYNC_FILE"/>  
    </root>  
  
</configuration>

参数说明:

  • queueSize:队列的大小,默认为256。这个值会影响性能,建议根据业务需求和服务器配置进行调整。
  • discardingThreshold:当队列剩余容量小于这个阈值时,会丢弃INFO及以下级别的日志。默认为-1,表示队列容量的20%;设置为0表示不丢弃任何日志。
  • neverBlock:当队列满后,是否阻塞等待队列有空闲空间。默认为false,表示不阻塞,直接丢弃日志;如果设置为true,则表示阻塞等待。

3. 日志级别

在Logback配置文件中,设置日志级别的作用是决定哪些级别的日志消息将被记录。Logback支持多种日志级别,从低到高依次为:

  • TRACE:最详细的日志级别,记录运行堆栈信息。
  • DEBUG:详细日志级别,记录比INFO更多的调试信息。
  • INFO:普通日志级别,用于记录重要的事件。
  • WARN:警告日志级别,用于记录可能需要关注的异常或事件。
  • ERROR:错误日志级别,用于记录错误事件或条件。
  • FATAL:致命错误日志级别,用于记录非常严重的问题。合并计入ERROR

这些级别之间存在包含关系,即一个级别包含另一个级别的所有日志消息(低级别包含高级别)。通过设置日志级别,你可以控制哪些日志信息会被记录到日志文件或输出到控制台。例如,如果你只设置INFO级别的日志,那么DEBUG、TRACE级别的日志就不会被记录。

4. 日志格式

在Logback的配置文件中,<encoder> 标签用于定义日志输出的格式。<pattern> 标签用于指定日志输出的具体格式。

例如日志格式

<encoder> 
  	<!--定义日志格式--> 
    <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - [%method,%line] - %msg%n</pattern>  
</encoder>  

解析结果:

  1. %d{yyyy-MM-dd HH:mm:ss}:日期格式

    • %d:日期和时间格式。
    • {yyyy-MM-dd HH:mm:ss}:指定日期和时间的格式,这里表示日期为 yyyy-MM-dd,时间为 HH:mm:ss。
    • 输出格式为:2024-01-01 12:34:56。
  2. [%thread]:线程

    • %thread:线程名称,或者简写为%t。
    • 输出格式为 [main],如果当前线程是主线程,则为 [main]。
  3. %-5level:

    • %-5level:日志级别,其中 - 表示左对齐,5 表示宽度为5。可以简写为%p
    • 输出格式为 INFO,如果日志级别是INFO,则显示为 INFO,如果日志级别是DEBUG,则显示为 DEBUG。
  4. %logger{36}:

    • %logger:日志记录器名称。
    • {36}:指定输出日志记录器名称的长度,这里为36个字符。
    • 输出格式为 com.example.package.MyClass,如果日志记录器名称超过36个字符,则截断。
  5. [%method,%line]:

    • %method:方法名
    • %line:代码行数
    • 输出格式为 [addListener,93]
  6. %msg:

    • %msg:日志消息,可以简写为%m。
  7. %n:

    • %n:换行符,用于在日志消息后添加一个新行。

综上所述,这个日志格式将会输出类似以下格式的日志:

2024-08-11 16:11:49.676 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - [registerRepositoriesIn,127] - Bootstrapping Spring Data Redis repositories in DEFAULT mode.

我们也可以设置日志格式是彩色日志格式,需要使用<conversionRule>定义彩色日志的转换规则,然后在日志格式中使用相应的转换词%clr(),设置颜色。

%clr用法:%clr(内容){颜色},把需要设置颜色的内容括起来,如果要设置颜色用{颜色}。如%clr(%logger{20}){yellow},来输出黄色文本。

配置示例:

<!-- 定义日志消息转换规则 -->
<!-- 彩色日志依赖的渲染类,用于渲染彩色文本 -->
<conversionRule conversionWord="clr"
                converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<!-- 彩色日志格式 -->
<!-- 
faint:淡色
blue:蓝色
magenta:红色
orange:橙色
cyan:青色
yellow:黄色
 -->
<property name="CONSOLE_LOG_PATTERN"
          value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr(${PID}){magenta} %clr([%thread]){orange} %clr(%logger){cyan} %m%n"/>


<!-- 控制台日志输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}</pattern>
    </encoder>
</appender>

在这里插入图片描述

5. yml配置参数

在Spring Boot项目中,我们也可以使用application.yml或application.properties文件来配置Logback参数。Spring Boot支持YAML格式的配置文件,因此你可以在application.yml文件中配置Logback的相关参数。

springboot集成了logback日志系统,默认读取名叫logback-spring.xml的配置文件,如果想自定义配置文件的名称,需要在spring boot配置文件中配置指定

logging:
  config: classpath:logback.xml

配置示例:

logging:
  # 设置日志级别
  level:
    root: INFO
    com.example: DEBUG
  # 设置日志文件存储
  file:
    name: ${spring.application.name}.log
    path: /logs/
  logback:
    rollingpolicy:
      max-file-size: 100MB
      file-name-pattern: ${spring.application.name}.%d{yyyy-MM-dd}.%i.log
  # 控制台日志输出
  console:
    enabled: true

6. MDC上下文配置

在Logback中,Mapped Diagnostic Context(MDC)是一种用于在日志记录过程中传递上下文信息的机制。它允许你将应用程序的上下文信息(如事务ID、用户ID、环境变量等)与日志记录关联起来,以便于日志分析和问题追踪。

例如,我们可以通过请求头拦截器,设置需要的上下文信息

import org.slf4j.MDC;

public class HeaderInterceptor implements AsyncHandlerInterceptor
{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (!(handler instanceof HandlerMethod))
        {
            return true;
        }

		# 设置线程信息
        SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID));
        SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME));
        SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY));
        SecurityContextHolder.setAuth(ServletUtils.getHeader(request, SecurityConstants.AUTHORIZATION_HEADER));
        SecurityContextHolder.setNickName(ServletUtils.getHeader(request, SecurityConstants.NICK_NAME));
        SecurityContextHolder.setLogId(ServletUtils.getHeader(request, SecurityConstants.LOG_ID));

		# 设置MDC上下文信息
        MDC.put("log_id",SecurityContextHolder.getLogId());
        MDC.put("user_id", String.valueOf(SecurityContextHolder.getUserId()));
        MDC.put("nick_name",SecurityContextHolder.getNickName());

        String token = SecurityUtils.getToken();
        if (StringUtils.isNotEmpty(token))
        {
            LoginUser loginUser = AuthUtil.getLoginUser(token);
            if (StringUtils.isNotNull(loginUser))
            {
                AuthUtil.verifyLoginUserExpire(loginUser);
                SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
            }
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception
    {
        SecurityContextHolder.remove();
    }
}

在这个例子中,我们使用MDC.put()方法将log_id和user_id设置为上下文信息。在日志记录器中,我们可以使用%X{log_id}和%X{user_id}来引用这些上下文信息。

<!-- 日志输出格式 -->
<property name="log.pattern"
          value="%d{HH:mm:ss.SSS} production_service [%X{log_id}] [%X{user_id}] [%X{nick_name}]  [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>

7. 实战配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<!-- 设置日志上下文名称 -->
    <contextName>${APP_NAME}</contextName>

	<!-- 从Spring属性中读取配置 -->
    <springProperty name="APP_NAME" scope="context" source="spring.application.name"/>
    <springProperty name="LOG_FILE" scope="context" source="logging.file" defaultValue="./logs/${APP_NAME}"/>
    <springProperty name="LOG_POINT_FILE" scope="context" source="logging.file" defaultValue="./logs/point"/>
    <springProperty name="LOG_MAXFILESIZE" scope="context" source="logback.filesize" defaultValue="50MB"/>
    <springProperty name="LOG_FILEMAXDAY" scope="context" source="logback.filemaxday" defaultValue="7"/>
    <springProperty name="ServerIP" scope="context" source="spring.cloud.client.ip-address" defaultValue="0.0.0.0"/>
    <springProperty name="ServerPort" scope="context" source="server.port" defaultValue="0000"/>

    <!--定义日志消息转换规则-->
    <!-- 彩色日志依赖的渲染类,用于渲染彩色文本 -->
    <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="[${APP_NAME}:${ServerIP}:${ServerPort}] [%clr(%X{traceid}){yellow},%clr(%X{X-B3-TraceId}){yellow}] %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr(${PID}){magenta} %clr([%thread]){orange} %clr(%logger){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <property name="CONSOLE_LOG_PATTERN_NO_COLOR"
              value="[${APP_NAME}:${ServerIP}:${ServerPort}] [%X{traceid},%X{X-B3-TraceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %level ${PID} [%thread] %logger %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <!--简易日志格式-->
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %logger{20} - [%method,%line] - %msg%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" />
    
    <!-- 控制台日志配置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <withJansi>true</withJansi>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成错误日志文件 -->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}/${APP_NAME}-error.log</file>
        <!-- 基于时间的分包策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <!--保留时间,单位:天-->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN_NO_COLOR}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
        <!-- 定义日志事件的过滤规则 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
        	<!-- 只打印错误日志 -->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 按照每天生成常规日志文件 -->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}/${APP_NAME}-info.log</file>
        <!-- 基于时间和文件大小的分包策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}/${APP_NAME}-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <!--保留时间,单位:天-->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN_NO_COLOR}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 定义日志事件的过滤规则 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
    </appender>
    
	<!-- 根日志配置 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="ERROR"/>
        <appender-ref ref="INFO"/>
    </root>

</configuration>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值