【SpringBoot 日志】日志级别与配置:分类、使用及持久化全攻略

文章目录

    • [一、 日志概述](#一、 日志概述)
    • [二、 日志使用](#二、 日志使用)
      • [2.1 打印日志](#2.1 打印日志)
      • [2.2 日志框架介绍](#2.2 日志框架介绍)
        • [2.2.1 门面模式(外观模式)](#2.2.1 门面模式(外观模式))
        • [2.2.2 SLF4J 框架介绍](#2.2.2 SLF4J 框架介绍)
      • [2.3 日志格式的说明](#2.3 日志格式的说明)
      • [2.4 日志级别](#2.4 日志级别)
        • [2.4.1 日志级别的分类](#2.4.1 日志级别的分类)
        • [2.4.2 日志级别的使用](#2.4.2 日志级别的使用)
      • [2.5 日志配置](#2.5 日志配置)
        • [2.5.1 配置日志级别](#2.5.1 配置日志级别)
        • [2.5.2 日志持久化](#2.5.2 日志持久化)
        • [2.5.3 配置日志文件分割](#2.5.3 配置日志文件分割)
    • [3. @Slfj 注解日志输出](#3. @Slfj 注解日志输出)
    • [4. 总结](#4. 总结)

一、 日志概述

日志的用途:

日志主要是为了发现问题、分析问题、定位问题的,但除此之外,日志还有很多用途:

  1. 系统监控:监控现在几乎是一个成熟系统的标配,我们可以通过日志记录这个系统的运行状态,每一个方法的响应时间、响应状态等,对数据进行分析,设置不同的规则,超过阈值时进行报警。比如统计日志中关键字的数量,并在关键字数量达到一定条件时报警,这也是日志的常见需求之一。
  2. 数据采集:数据采集是一个比较大的范围,采集的数据可以作用在很多方面,比如数据统计、推荐排序等。
  3. 日志审计:随着互联网的发展,众多企业的关键业务越来越多的运行于网络之上。通过系统日志分析,可以判断一些非法攻击、非法调用,以及系统处理过程中的安全隐患。

二、 日志使用

Spring Boot 项目在启动的时候默认就有日志输出。

与 System.out.print 的区别

java 复制代码
@RestController
public class LoggerController {
    @RequestMapping("/logger")
    public String logger() {
        System.out.println("打印日志");
        return "打印日志";
    }
}

观察日志输出:

可以看到,通过 System.out.print 打印的日志,比 Spring Boot 打印的日志缺少了很多信息。

Spring Boot 内置了日志框架 Slf4j,我们可以直接在程序中调用 Slf4j 来输出日志。

2.1 打印日志

打印日志的步骤:

  1. 在程序中得到日志对象。
  2. 使用日志对象输出要打印的内容。

示例:

  1. 在程序中得到日志对象
    在程序中获取日志对象需要使用日志工厂 LoggerFactory,如下代码所示:
java 复制代码
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);

LoggerFactory.getLogger 需要传递一个参数,标识这个日志的名称。这样可以更清晰的知道是哪个类输出的日志。当有问题时,可以更方便直观的定位到问题类。

注意 :Logger 对象是属于 org.slf4j 包下的,不要导入错包。

完整示例:

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoggerController {
    private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
}
  1. 使用日志对象打印日志
    日志对象的打印方法有很多种,我们可以先使用 info() 方法来输出日志,如下代码所示:
java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoggerController {
    private static Logger logger = LoggerFactory.getLogger(LoggerController.class);

    @RequestMapping("/logger")
    public String logger() {
        logger.info("打印日志");
        return "打印日志";
    }
}

打印日志效果展示:

2.2 日志框架介绍

分类 框架
日志门面(Logging Facade) commons-logging、SLF4J
日志实现 log4j 1/2、JUL、logback

SLF4J 不同于其他日志框架,它不是一个真正的日志实现,而是一个抽象层,对日志框架制定的一种规范、标准、接口。所有 SLF4J 并不能独立使用,需要和具体的日志框架配合使用。

2.2.1 门面模式(外观模式)

SLF4J 是门面模式的典型应用(但不仅仅使用了门面模式)。

门面模式定义:提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用。

门面模式主要包含 2 种角色

  1. 外观角色(Facade):也称门面角色,系统对外的统一接口。
  2. 子系统角色(SubSystem):可以同时有一个或多个 SubSystem。每个 SubSystem 都不是一个单独的类,而是一个类的集合。SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)。

门面模式的实现

场景:回家,我们会开各个屋的灯。离开家时,会关闭各个屋的灯。如果家里设置一个总开关,来控制整个屋的灯就会很方便。

java 复制代码
public class FacadePatternDemo {
    public static void main(String[] args) {
        LightFacade lightFacade = new LightFacade();
        lightFacade.lightOn();
        lightFacade.lightOff();
    }
}

/**
 * 灯的门面
 */
class LightFacade {
    private Light livingRoomLight = new LivingRoomLight();
    private Light hallLight = new HallLight();
    private Light diningLight = new DiningLight();

    public void lightOn() {
        livingRoomLight.on();
        hallLight.on();
        diningLight.on();
    }

    public void lightOff() {
        livingRoomLight.off();
        hallLight.off();
        diningLight.off();
    }
}

interface Light {
    void on();
    void off();
}

/**
 * 客厅灯
 */
class LivingRoomLight implements Light {
    @Override
    public void on() {
        System.out.println("打开客厅灯");
    }

    @Override
    public void off() {
        System.out.println("关闭客厅灯");
    }
}

/**
 * 走廊灯
 */
class HallLight implements Light {
    @Override
    public void on() {
        System.out.println("打开走廊灯");
    }

    @Override
    public void off() {
        System.out.println("关闭走廊灯");
    }
}

/**
 * 餐厅灯
 */
class DiningLight implements Light {
    @Override
    public void on() {
        System.out.println("打开餐厅灯");
    }

    @Override
    public void off() {
        System.out.println("关闭餐厅灯");
    }
}

门面模式的优点

  1. 减少了系统的相互依赖。实现了客户端与子系统的耦合关系,这使得子系统的变化不会影响到调用它的客户端。
  2. 提高了灵活性,简化了客户端对子系统的使用难度,客户端无需关心子系统的具体实现方式,而只需要和门面对象交互即可。
  3. 提高了安全性。可以灵活设定访问权限,不在门面对象中开通方法,就无法访问。
2.2.2 SLF4J 框架介绍

SLF4J 就是其他日志框架的门面。SLF4J 可以理解为是提供日志服务的统一 API 接口,并不涉及到具体的日志逻辑实现。

引入日志门面的优势

引入门面日志框架之后,应用程序和日志框架(框架的具体实现)之间有了统一的 API 接口(门面日志框架实现),此时应用程序只需要维护一套日志文件配置,且当底层实现框架改变时,也不需要更改应用程序代码。

2.3 日志格式的说明

打印的日志内容元素具体如下:

  1. 时间日期:精确到毫秒
  2. 日志级别:ERROR、WARN、INFO、DEBUG 或 TRACE
  3. 进程 ID
  4. 线程名
  5. Logger 名(通常使用源代码的类名)
  6. 日志内容

示例日志解析:

2.4 日志级别

日志级别代表着日志信息对应问题的严重性,为了更快的筛选符合目标的日志信息。

2.4.1 日志级别的分类

日志的级别从高到低依次为:FATAL、ERROR、WARN、INFO、DEBUG、TRACE

  • FATAL:致命信息,表示需要立即被处理的系统级错误。
  • ERROR:错误信息,级别较高的错误日志信息,但仍然不影响系统的继续运行。
  • WARN:警告信息,不影响使用,但需要注意的问题。
  • INFO:普通信息,用于记录应用程序正常运行时的一些信息(例如系统启动完成、请求处理完成等)。
  • DEBUG:调试信息,需要调试时候的关键信息打印。
  • TRACE:追踪信息,比 DEBUG 更细粒度的信息事件(除非有特殊用意,否则请使用 DEBUG 级别替代)。

日志级别顺序(从高到低):

级别越高,收到的消息越少。

2.4.2 日志级别的使用

针对这些级别,Logger 对象分别提供了对应的方法,来输出日志。

java 复制代码
@RestController
@RequestMapping("/logger")
public class LoggerController {
    private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);

    @RequestMapping("/print")
    public String print(){
        logger.error("打印 error 日志");
        logger.warn("打印 warn 日志");
        logger.info("打印 info 日志");
        logger.debug("打印 debug 日志");
        logger.trace("打印 trace 日志");
        return "日志打印成功";
    }
}

默认日志级别输出结果

结果发现,只打印了 info、warn 和 error 级别的日志。这与日志级别的配置有关,日志的输出级别默认是 info 级别,所以只会打印大于等于此级别的日志,也就是 info、warn 和 error。

2.5 日志配置

日志框架支持我们更灵活的输出日志,包括内容、格式等。

2.5.1 配置日志级别

日志级别配置只需要在配置文件中设置 logging.level 配置项即可。

Properties 配置

properties 复制代码
logging.level.root=debug

yml 配置

yaml 复制代码
logging:
  level:
    root: trace

重新运行代码,观察结果:

2.5.2 日志持久化

以上的日志都是输出在控制台上的,然而在线上环境中,我们需要把日志保存下来,以便出现问题之后追溯问题。把日志保存下来就叫持久化。

日志持久化有两种方式:

  1. 配置日志文件名
  2. 配置日志的存储目录
配置项 说明
logging.file.name 日志文件名(例如 'myapp.log')。名称可以是确切位置或相对于当前目录。
logging.file.path 日志文件的位置。例如 /var/log

配置日志文件的路径和文件名

  • Properties 配置:
properties 复制代码
logging.file.name=logger/springboot.log
  • yml 配置:
yaml 复制代码
logging:
  file:
    name: logger/springboot.log

后面可以跟绝对路径或者相对路径。

运行结果显示,日志内容保存在了对应的目录下。

配置日志文件的保存路径

  • Properties 配置:
properties 复制代码
logging.file.path=D:/temp
  • yml 配置:
yaml 复制代码
logging:
  file:
    path: D:/temp

这种方式只能设置日志的路径,文件名为固定的 spring.log

注意logging.file.namelogging.file.path 两个都配置的情况下,只生效其一,以 logging.file.name 为准。

2.5.3 配置日志文件分割

如果我们的日志都放在一个文件中,随着项目的运行,日志文件会越来越大,需要对日志文件进行分割。当然,日志框架考虑到了这一点,所以如果不进行配置,就走自动配置。默认日志文件超过 10M 就进行分割。

配置项 说明 默认值
logging.logback.rollingpolicy.file-name-pattern 日志分割后的文件名格式 ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
logging.logback.rollingpolicy.max-file-size 日志文件超过这个大小就自动分割 10MB

配置日志文件分割

  • Properties 配置:
properties 复制代码
logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i
logging.logback.rollingpolicy.max-file-size=1KB
  • yml 配置:
yaml 复制代码
logging:
  logback:
    rollingpolicy:
      max-file-size: 1KB
      file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i

3. @Slfj 注解日志输出

每次都使用 LoggerFactory.getLogger(xxx.class) 很繁琐,且每个类都添加一遍,lombok 给我们提供了一种更简单的方式。

lombok 提供的 @Slf4j 注解会帮我们提供一个日志对象 log,我们直接使用就可以。

  • 添加 lombok 依赖
xml 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
  • 输出日志
java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class LogController {
    public void log() {
        log.info("要输出日志的内容");
    }
}

4. 总结

  1. 日志是程序中的重要组成部分,使用日志可以快速的发现和定位问题。Spring Boot 内置了日志框架,默认情况下使用的是 info 日志级别将日志输出到控制台,我们可以通过 lombok 提供的 @Slf4j 注解和 log 对象快速的打印自定义日志。
  2. 日志包含 6 个级别,日志级别越高,收到的日志信息也就越少,可以通过配置日志的保存名称或保存目录来将日志持久化。
相关推荐
SimonKing17 小时前
基于Netty的TCP协议的Socket服务端
java·后端·程序员
予枫的编程笔记17 小时前
Elasticsearch深度搜索与查询DSL实战:精准定位数据的核心技法
java·大数据·人工智能·elasticsearch·搜索引擎·全文检索
while(1){yan}17 小时前
拦截器(详解)
数据库·spring boot·spring·java-ee·拦截器
荒诞硬汉17 小时前
面向对象(三)
java·开发语言
柒.梧.17 小时前
Spring Boot集成JWT Token实现认证授权完整实践
java·spring boot·后端
白露与泡影17 小时前
放弃 IntelliJ IDEA,转 VS Code 了。。
java·ide·intellij-idea
迷雾骑士17 小时前
IDEA中将项目提交到Gitee仓库
java·gitee·intellij-idea
菜鸟233号17 小时前
力扣416 分割等和子串 java实现
java·数据结构·算法·leetcode
奔波霸的伶俐虫17 小时前
redisTemplate.opsForList()里面方法怎么用
java·开发语言·数据库·python·sql