系统日志:从入门到精通的完整指南

一、认识日志

1.1 为什么需要系统学习日志

日志是软件开发中不可或缺的组成部分。从初学编程开始,我们就接触到了最简单的日志输出方式------System.out.println()。这种方式虽然直观,但在实际项目开发中却显得力不从心。随着应用规模的扩大和业务复杂度的提升,我们需要更专业、更系统化的日志管理方案。

在项目开发过程中,日志不仅仅是调试工具,更是系统运行状态的"晴雨表"。它记录了程序执行的轨迹,帮助我们追踪问题根源、分析性能瓶颈、监控系统健康状态。特别是在分布式系统和微服务架构中,完善的日志体系更是保障系统稳定运行的关键。

1.2 日志的核心价值与应用场景

除了传统的问题排查功能外,现代日志系统在多个领域发挥着重要作用:

  1. 性能监控与告警:通过记录关键指标如响应时间、吞吐量、错误率等,可以实时监控系统运行状态。当指标超出预设阈值时自动触发告警,帮助运维团队快速响应潜在问题。
  2. 用户行为分析:日志记录了用户在系统中的操作轨迹,这些数据对于产品优化、用户体验改进具有重要价值。通过分析用户行为模式,可以优化产品功能设计。
  3. 业务决策支持:日志中蕴含的业务数据可以为决策提供依据。例如,通过分析访问日志可以了解用户偏好,指导内容推荐策略;通过交易日志可以分析销售趋势,优化库存管理。
  4. 安全合规审计:在金融、医疗等监管严格的行业,日志记录是满足合规要求的必要手段。完整的操作日志可以追溯每一个关键操作,确保业务流程的可审计性。

1.3 日志在安全审计中的重要性

在数字化时代,信息安全已成为企业运营的生命线。日志审计作为安全体系的重要组成部分,承担着多重职责:

首先,日志审计是发现安全威胁的重要手段。通过分析异常访问模式、频繁失败登录尝试等日志信息,可以及时发现潜在的攻击行为。其次,在发生安全事件时,完整的日志记录是调查取证的关键证据,能够还原攻击路径和影响范围。

从合规角度来看,国内外多项法规标准(如等保2.0、GDPR等)都对日志留存和审计提出了明确要求。企业需要建立完善的日志管理体系,确保日志的完整性、真实性和不可篡改性。

在实际应用中,日志审计不仅关注外部攻击,也关注内部风险。通过记录敏感数据的访问和操作,可以有效防止内部人员滥用权限或泄露信息。例如,在客户关系管理系统中,每一次客户信息的查询、修改都应该被详细记录,确保操作可追溯。

二、日志使用

大家可以先对比一下不同方法所打印的日志

通过上图,我们可以看到System.out.print打印的日志,比SpringBoot打印的日志缺少了很多信息。

因为SpringBoot内置了日志框架Slf4j,我们可以直接在程序中调用Slf4j来输出日志,具体操作请接着往下看

2.1 打印日志

Spring Boot 项目在启动的时候默认就有日志输出,如下图所示:

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

因为 Spring Boot 内置了日志框架 Slf4j,我们可以直接在程序中调用 Slf4j 来输出日志,具体操作请接着往下看。

打印日志的步骤:

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

在程序中获取日志对象需要使用日志工厂 LoggerFactory,如下代码所示:

private static Logger logger = LoggerFactory.getLogger(LoggerController.class);


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

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

2.1.2 使用日志对象打印日志

日志对象的打印方法有很多种,我们可以先使用 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 日志框架


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

2.2.1 门面模式(外观模式)

门面模式(Facade Pattern),也称为外观模式,是一种结构型设计模式。它为子系统中的一组接口提供了一个统一的高层接口,使得子系统更容易使用。

在日志框架的上下文中,SLF4J 就扮演了"门面"的角色。它定义了一套统一的日志接口(API),而具体的日志实现(如 Logback、Log4j2、JUL 等)则作为"子系统"。应用程序代码只需要面向 SLF4J 这一套接口编程,无需关心底层具体使用的是哪种日志实现库。

这种设计带来了以下核心优势:

  • 解耦:应用程序与具体的日志实现解耦。如果需要更换日志框架(例如从 Logback 切换到 Log4j2),只需更改依赖配置,业务代码无需任何修改。
  • 简化使用:开发者只需学习一套 SLF4J API,即可操作各种日志框架,降低了学习成本和使用复杂度。
  • 统一管理:通过一个统一的"门面"来管理所有日志操作,便于进行统一的配置、监控和扩展。

下图清晰地展示了门面模式在日志体系中的应用:

因此,SLF4J + 具体日志实现的组合,是 Java 日志领域践行门面模式的经典范例。

2.2.2 SLF4J框架介绍

SLF4J(Simple Logging Facade for Java,Java 简单日志门面)是 Java 社区中最广泛使用的日志门面框架。它由 Ceki Gülcü 创建,旨在为 Java 应用程序提供统一的日志 API,让开发者能够在不修改业务代码的情况下,灵活切换底层日志实现。

SLF4J 的核心设计理念可以概括为以下几点:

  • 接口与实现分离:SLF4J 只提供一套标准的日志 API 接口,不包含具体的日志实现。这种设计使得应用程序代码与具体的日志框架(如 Logback、Log4j2、JUL 等)完全解耦。
  • 向后兼容:SLF4J 在设计时充分考虑了与现有日志框架的兼容性。通过提供各种适配器(Adapter)和桥接器(Bridge),SLF4J 能够无缝集成几乎所有的 Java 日志框架,包括 Log4j、JUL(java.util.logging)、Jakarta Commons Logging 等。
  • 性能优化 :SLF4J API 在设计上注重性能。例如,它提供了参数化日志输出方法(如 logger.debug("User {} logged in at {}", username, timestamp)),只有在日志级别启用时才会进行字符串拼接,避免了不必要的性能开销。

SLF4J 的组成模块主要包括:

  1. slf4j-api:核心 API 模块,定义了所有日志接口。这是应用程序唯一需要直接依赖的 SLF4J 模块。
  2. 具体实现绑定 :将 SLF4J API 绑定到具体日志实现的模块,例如:
    • slf4j-log4j12:绑定到 Log4j 1.2.x
    • slf4j-jdk14:绑定到 JDK 1.4+ 的 java.util.logging
    • slf4j-simple:SLF4J 自带的简单实现(主要用于测试)
  3. 桥接器 :将其他日志框架的调用重定向到 SLF4J,例如:
    • jcl-over-slf4j:将 Jakarta Commons Logging 调用重定向到 SLF4J
    • log4j-over-slf4j:将 Log4j 调用重定向到 SLF4J
    • jul-to-slf4j:将 java.util.logging 调用重定向到 SLF4J

典型依赖配置示例(Maven)

xml 复制代码
<!-- SLF4J API -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>
<!-- 绑定到 Logback(推荐组合) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>

为什么选择 SLF4J + Logback 组合?

在 Spring Boot 的默认配置中,SLF4J 与 Logback 是黄金搭档,原因如下:

  • 原生集成:Logback 是 SLF4J 的原生实现,两者由同一作者开发,配合最为紧密,性能最优。
  • 零配置启动:Spring Boot 提供了合理的默认配置,开箱即用。
  • 丰富的特性:Logback 支持异步日志、条件化配置、自动重载配置文件、多种输出格式等高级功能。
  • 向后兼容:通过桥接器,可以统一项目中可能存在的其他日志框架调用。

2.3 日志格式说明

2.4 日志级别分类

日志级别(Log Level)用于标识日志信息的重要性或严重程度。通过为日志消息分配不同的级别,我们可以控制哪些信息需要被记录、输出到何处(如控制台、文件),以及在不同环境(开发、测试、生产)下输出哪些级别的日志,从而实现灵活的日志管理和问题排查。

SLF4J 定义了以下几种常见的日志级别,从低到高依次为:

  1. TRACE:最详细的跟踪信息,通常用于记录程序运行过程中的每一步细节,如方法的进入退出、变量的值等。在开发阶段用于深度调试,生产环境通常关闭。
  2. DEBUG:调试信息,用于记录程序运行的关键节点、变量状态等,帮助开发者理解程序流程和定位问题。开发环境常用,生产环境可根据需要开启。
  3. INFO:信息性消息,用于记录程序正常的运行状态、重要的业务事件或系统启动信息等。这是默认的、推荐在生产环境使用的级别,用于监控系统健康状态。
  4. WARN:警告信息,表明可能有问题发生,但程序仍能继续运行。例如,磁盘空间不足、使用了过时的 API、非关键的第三方服务调用超时等。需要关注但非紧急。
  5. ERROR:错误信息,表明发生了错误事件,影响了某个操作的正常完成,但应用程序可能仍能继续运行。例如,数据库连接失败、文件读取异常、业务逻辑错误等。需要立即调查处理。
  6. FATAL (在某些框架中):致命错误,表明发生了非常严重的错误事件,可能导致应用程序崩溃或中止。SLF4J 规范本身未定义此级别,但一些底层实现(如 Logback)支持。

级别规则 :日志框架通常遵循"级别阈值"规则。例如,如果将日志级别设置为 INFO,那么只有级别等于或高于 INFO(即 INFOWARNERROR)的日志才会被输出,而 TRACEDEBUG 级别的日志将被忽略。级别越高收到的信息越少。

2.5 日志级别的使用

日志的级别是开发人员自己设置的,开发人员根据自己的理解来判断信息的重要程度

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

代码实现:

java 复制代码
@RestController
@RequestMapping("/logger")
public class LoggerController {

    private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);

    @RequestMapping("/logger1")
    public String printLog() {
        logger.trace("====trace====");
        logger.debug("====debug====");
        logger.info("====info====");
        logger.warn("====warn====");
        logger.error("====error====");
        return "打印不同的级别";
    }
}

运行结果:

我们发现,结果只打印了info,warn和error级别的日志

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

2.6 日志配置

上面分享的都是日志地使用,日志框架支持我们更灵活的输出日志,包括内容,格式等

2.6.1 配置日志级别

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

properties配置:

java 复制代码
logging.level.root=debuge

yml配置:

java 复制代码
logging:
  level:
    root: debug

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

2.6.2 日志持久化

日志持久化有两种方式:

  1. 配置日志文件名
  2. 配置日志的存储目录

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

java 复制代码
//properties配置:
    logging.file.name=logger/springboot.log

//yml配置:
    logging:
      file:
        name: logger/springboot.log

运行展示:


配置日志文件的保存路径:

java 复制代码
//properties配置
    logging.file.path=D:/temp

//yml配置
    logging:
      file:
        path: D:/temp

运行展示:


当logging.file.name和logging.file.path两种配置都存在时,以logging.file.name为准

2.6.3 配置日志文件分割

随着项目的运行,日志数量会不断增加,这时就需要对日志进行分割

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

代码实现:

java 复制代码
//properties配置:

    logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i
    logging.logback.rollingpolicy.max-file-size=2KB


//yml配置:
    logging:
      logback:
        rollingpolicy:file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i
        max-file-size: 2KB
2.6.4 配置日志格式

日志格式决定了每条日志输出的样式和包含的信息。Spring Boot 默认使用 Logback 作为日志实现,我们可以通过配置文件自定义日志的输出格式。

常见的日志格式占位符:

  • %d{yyyy-MM-dd HH:mm:ss}:日期时间
  • %-5level:日志级别(左对齐,宽度5)
  • %logger{36}:日志记录器名称(最大长度36)
  • %msg:日志消息
  • %n:换行符
  • %thread:线程名
  • %M:方法名
  • %L:行号

配置示例(application.properties):

properties 复制代码
# 控制台日志格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
文件日志格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n

配置示例(application.yml):

yaml 复制代码
logging:
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

格式说明:

  • %d{yyyy-MM-dd HH:mm:ss}:显示完整日期时间
  • [%thread]:显示线程名,便于多线程调试
  • %-5level:日志级别,左对齐占5个字符宽度
  • %logger{36}:记录器名称,最多显示36个字符
  • %msg:实际的日志消息内容
  • %n:换行符,确保每条日志单独一行

通过合理配置日志格式,可以让日志信息更加清晰、规范,便于后续的日志分析和问题排查。

三、更简单的日志输出

每次使用都调用LoggerFactory.getLogger(xxx.class)很繁琐,所以lombok给我们提供了更为简单的方式:

  1. 添加lombok框架支持
  2. 使用@slf4j注解输出日志

3.1 添加lombok依赖

java 复制代码
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

3.2 输出日志

代码编写:

java 复制代码
@Slf4j
@RestController
public class Logger {
    @RequestMapping("/1")
    public void log(){
        log.info("==输出日志内容==");
        return ;
    }
}

运行展示:

四、小结

好饿好饿好饿,感觉每次写博客都要花挺久的,有点点不想写了,想赶紧往后赶进度。因为今天突然发现很多算法题都没掌握,愁哇愁哇。现在才理解那句出来混早晚要还的,今天晚上在更一期牛客算法,其实已经写了很多了,就是一直没往博客写,哈哈哈。接下来一个星期我将更新最少五篇,请格外观众老爷给我点点赞,支持下我吧。

相关推荐
_oP_i1 小时前
mysql统计数据库使用存储大小
数据库
会编程的土豆2 小时前
MySQL 入门:库、表、行、主键是什么
linux·数据库·网络协议·http
写代码的强哥2 小时前
云原生数据库与传统数据库相比“新”在哪里
数据库·云原生·架构
MartinYeung52 小时前
[论文学习]InjecAgent:工具集成大语言模型智能体的间接提示注入基准测试
网络·学习·语言模型
paopaokaka_luck2 小时前
基于Springboot3+Vue3的高校选课系统(AI选课助手、协同过滤算法、分享到微博、扣扣、Echarts图形化分析)
网络·spring boot·网络协议·echarts
吴声子夜歌3 小时前
MongoDB 4.2——查询
数据库·mongodb
三川6983 小时前
深入浅出SSD 10:UFS介绍
网络
BullSmall4 小时前
AFL++ HTTP Mode(网络 / HTTP 服务模糊测试)完整安装教程
网络·网络协议·http