springboot框架项目应用实践四(日志)

日志这个事情,在项目中还真的是不能少,对于线上应用,如果出了什么问题,没有日志简直没有办法去排查!只能抓瞎了!

正好最近有项目组小伙伴,提了一个需求:如何实现日志的细粒度控制,即

  • 将不同包,甚至不同类的日志输出到不同的日志文件中,方便日志分析排查问题
  • 将不同日志级别的日志,输出到不同的日志文件中,方便日志分析排查问题

我们的应用都是基于springboot的应用,为了解决这个问题,简单整理了一个方案,并顺便分享出来。

1.日志组件介绍

java的世界,从来就不缺优秀的组件,在日志领域也是一样的。我们来数一数

  • JUL:最正宗的,jdk自带的日志系统,从jdk1.4版本就有了。但是是认知度最低的,几乎没有什么存在感!估计也没有几个小伙伴认识,不必关心它!
  • JCL:全称是Jakarta Commons Logging,是大名鼎鼎的Apache出品的通用日志API,小伙伴们肯定都很熟悉commons-logging依赖,指的就是它。需要注意的是JCL并不提供具体的实现,它只提供了接口,即提供了日志接口规范,方便我们在应用中按需切换具体的日志实现组件,比如log4j,jul等
  • Log4j:这个我们都很熟悉了,是Apache提供的开源日志组件实现,支持细粒度控制日志生成过程,有丰富的日志级别,以及支持将日志输出到控制台、文件、数据库等
  • Slf4j:简单日志门面,全称是The Simple Logging Facade For Java。Slf4j类似于JCL,只提供接口规范,不提供具体实现,都是为了方便在应用中按需切换具体日志组件而诞生的,简单理解就是为了解耦具体日志组件的
  • Logback:可靠、通用、灵活的Java日志框架,有很多优点,它是Slf4j-api的天然实现,与Slf4j使用不需要任何桥接包;它还是springboot应用默认支持的日志组件
  • Log4j2:Log4j2是Log4j 1.x与Logback的改进版本,采用了异步无锁化实现,吞吐量、性能都更好,是目前所有日志组件中的佼佼者!简直是降维打击啊!

介绍完日志组件,回到最初的需求场景,需要解决问题,你还记得上面的需求吗

  • 将不同包,甚至不同类的日志输出到不同的日志文件中,方便日志分析排查问题
  • 将不同日志级别的日志,输出到不同的日志文件中,方便日志分析排查问题

另外我们的应用都是基于springboot,案例环境就默认使用Slf4j + Logback的组合了。

2.案例搭建环境

2.1.引入依赖

需要引入spring-boot-starter-logging依赖

xml 复制代码
<!--日志依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

如果是springboot web应用,当我们引入spring-boot-starter-web依赖后,已经依赖了日志starter组件,就不需要在额外引入

为了一会在代码中,使用日志方便,再引入lombok依赖

xml 复制代码
<!--lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

2.2.编写配置文件

要细粒度控制日志输出,需要在resources目录下,增加控制日志行为的配置文件,根据logback的约定,配置文件名称规范是:logback-xxx.xml,通常项目中我们就叫做:logback-spring.xml

2.2.1.logback-spring.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!--上下文名称-->
    <contextName>follow-me-springboot-log</contextName>

    <!--日志输出目录,以及日志文件名称-->
    <property name="logback.logdir" value="D:\\nacos\\log"/>
    <property name="logback.appname" value="followMeLog"/>

    <!--自定义appender组件,将日志输出到文件:
        1.只记录INFO级别日志,通过LevelFilter过滤处理
    -->
    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高,
            所以我们使用下面的策略,可以避免输出 Error 的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--过滤 Error-->
            <level>ERROR</level>
            <!--匹配到就禁止-->
            <onMatch>DENY</onMatch>
            <!--没有匹配到就允许-->
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <!--日志名称-->
        <File>${logback.logdir}/info.${logback.appname}.log</File>
        <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径,定义了日志的切分方式------把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
            <FileNamePattern>${logback.logdir}/info.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--只保留最近90天的日志-->
            <maxHistory>90</maxHistory>
            <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
        <!--日志输出编码,以及日志格式-->
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </encoder>
    </appender>

    <!--自定义appender组件,将日志输出到文件:
       1.只记录ERROR级别日志,通过ThresholdFilter过滤处理
   -->
    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>Error</level>
        </filter>
        <!--日志名称-->
        <File>${logback.logdir}/error.${logback.appname}.log</File>
        <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径,定义了日志的切分方式------把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
            <FileNamePattern>${logback.logdir}/error.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--只保留最近90天的日志-->
            <maxHistory>90</maxHistory>
            <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
            <!--<totalSizeCap>1GB</totalSizeCap>-->
        </rollingPolicy>
        <!--日志输出编码,以及日志格式-->
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </encoder>
    </appender>

    <!--自定义appender组件,将日志输出到控制台 -->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <!--展示格式 layout-->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
        </layout>
        <!-- 筛选级别日志并输出到控制台,这里是INFO -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
             <level>INFO</level>
        </filter>
    </appender>

    <!--自定义日志logger组件,且支持环境profile:开发环境,测试环境-->
    <springProfile name="dev,test">
        <!-- 细粒度控制日志,属性说明:
           name:指定范围,可以到具体类名称,或者包名
           level:日志级别
           additivity:日志是否向上传递,true向上传递;false不向上传递
         -->
        <logger name="cn.edu.anan.controller" level="INFO" additivity="true">
            <appender-ref ref="fileInfoLog"/>
            <appender-ref ref="fileErrorLog"/>
        </logger>
    </springProfile>

    <!--自定义日志logger组件,且支持环境profile:生产环境-->
    <springProfile name="prod">
        <!-- 细粒度控制日志,属性说明:
           name:指定范围,可以到具体类名称,或者包名
           level:日志级别
           additivity:日志是否向上传递,true向上传递;false不向上传递
         -->
        <logger name="cn.edu.anan.controller" level="ERROR" additivity="false">
            <appender-ref ref="fileInfoLog"/>
            <appender-ref ref="fileErrorLog"/>
        </logger>
    </springProfile>


    <!--配置根日志root组件,它引用输出到控制台appender组件-->
    <root level="INFO">
        <!--appender将会添加到这个logger-->
        <appender-ref ref="consoleLog"/>
    </root>

</configuration>

配置文件解读

  • 配置了三个appender组件,分别将日志输出到控制台、文件
    • fileInfoLog:输出INFO级别日志到文件
    • fileErrorLog:输出ERROR级别日志到文件
    • consoleLog:将日志输出到控制台
  • 配置了两个SpringProfile组件,控制不同的环境,使用不同的profile特性
    • springProfile name="dev,test":开发环境、测试环境,需要对应spring.profiles.active属性
    • springProfile name="prod":生产环境,需要对应spring.profiles.active属性
  • 配置了两个logger组件,用于组织appender组件,且控制是否要将日志向上传递到root组件
  • 配置了root根日志组件,应用consoleLog组件,将日志输出到控制台

以上配置,在配置文件中有详细的注释,如果你不熟悉,可以仔细看一下

2.2.2.application.yml

在application.yml文件中,重点需要关注spring.profiles.active属性,它与logback-spring.xml文件中的SpringProfile组件对应

yaml 复制代码
server:
  port: 8080
spring:
  application:
    name: follow-me-springboot-log
  profiles:
    active: dev

2.3.编写应用

编写两个controller,分别开放不同的端点,测试日志效果

2.3.1.LogController

java 复制代码
@RestController
@RequestMapping("log")
@Slf4j
public class LogController {

    @RequestMapping("test")
    public String test(){
        // 1.记录INFO级别日志
        log.info("LogController.日志级别:{}.日志内容:{}.", "INFO","记录INFO级别日志");

        // 2.记录ERROR级别日志
        log.error("LogController.日志级别:{}.日志内容:{}.", "ERROR","记录ERROR级别日志");

        return "LogController.";
    }

}

2.3.2.Log2Controller

java 复制代码
@RestController
@RequestMapping("log")
@Slf4j
public class Log2Controller {

    @RequestMapping("test2")
    public String test2(){
        // 1.记录INFO级别日志
        log.info("Log2Controller.日志级别:{}.日志内容:{}.", "INFO","记录INFO级别日志");

        // 2.记录ERROR级别日志
        log.error("Log2Controller.日志级别:{}.日志内容:{}.", "ERROR","记录ERROR级别日志");

        return "Log2Controller.";
    }

}

3.实现效果

启动应用,分别访问端点,观察日志结果

端点一:http://127.0.0.1:8080/log/test

端点二:http://127.0.0.1:8080/log/test2

控制台

shell 复制代码

2021-08-23 15:56:29 [http-nio-8080-exec-1] INFO cn.edu.anan.controller.LogController -LogController.日志级别:INFO.日志内容:记录INFO级别日志.2021-08-23 15:56:29 [http-nio-8080-exec-1] ERROR cn.edu.anan.controller.LogController -LogController.日志级别:ERROR.日志内容:记录ERROR级别日志.2021-08-23 15:56:36 [http-nio-8080-exec-3] INFO cn.edu.anan.controller.Log2Controller -Log2Controller.日志级别:INFO.日志内容:记录INFO级别日志.2021-08-23 15:56:36 [http-nio-8080-exec-3] ERROR cn.edu.anan.controller.Log2Controller -Log2Controller.日志级别:ERROR.日志内容:记录ERROR级别日志.

复制代码

INFO日志文件

ERROR日志文件

通过以上执行效果,我们看到已经成功将INFO、ERROR日志实现分离,记录到不同的日志文件中

  • error.followMeLog.log:错误日志文件
  • info.followMeLog.log:info日志文件

这样一来,会非常方便进行不同的日志收集、分析事项。nice!对吧。

另外这里还有三个特性,我就留给你去实践了

  • 将application.yml文件中spring.profiles.active属性,设置为prod看看效果
  • 通过Logger组件的name属性,实现将LogController、Log2Controller日志更细粒度的分离
  • 通过Logger组件的additivity属性,分别设置为true、false,观察控制台的输出

实践完成后,相信你一定会有收获!

相关推荐
sd21315125 分钟前
springboot3 spring security+jwt实现接口权限验证实现
java·后端·spring
m0_7482480210 分钟前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
qq_4476630510 分钟前
《Spring日志整合与注入技术:从入门到精通》
java·开发语言·后端·spring
源码姑娘12 分钟前
基于SpringBoot的智慧停车场小程序(源码+论文+部署教程)
spring boot·后端·小程序
Seven9720 分钟前
【设计模式】使用中介者模式实现松耦合设计
java·后端·设计模式
Seven9726 分钟前
【设计模式】探索状态模式在现代软件开发中的应用
java·后端·设计模式
Seven9727 分钟前
【设计模式】从事件驱动到即时更新:掌握观察者模式的核心技巧
java·后端·设计模式
Trae首席推荐官1 小时前
Trae 功能上新:支持 Remote-SSH 和自定义模型配置
前端·后端·trae
andrew_12191 小时前
暑期第一面oωo, TME一面面经
java·后端·sql·mysql·面试