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,观察控制台的输出

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

相关推荐
一只栖枝1 小时前
华为 HCIE 大数据认证中 Linux 命令行的运用及价值
大数据·linux·运维·华为·华为认证·hcie·it
uzong2 小时前
技术故障复盘模版
后端
GetcharZp3 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
wuicer3 小时前
ubuntu 20.04 安装anaconda以及安装spyder
linux·运维·ubuntu
桦说编程3 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研3 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
一只爱撸猫的程序猿4 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋4 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国5 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端