SpringBoot+Lombok+Logback实现日志

Lombok+Logback实现日志功能

一、Lombok 详解

Lombok 是一款 Java 开发工具库,核心目标是通过注解简化 Java 类的模板代码(如 getter/setter、构造方法、日志对象等),减少冗余代码,提升开发效率。它的原理是在编译阶段(而非运行时)动态生成字节码,因此不影响程序运行性能。

1. 核心优势

  • 消除模板代码:无需手动编写 getXxx()setXxx()toString() 等重复代码;
  • 代码更简洁:专注业务逻辑,类结构更清晰;
  • 低侵入性:仅在编译期生效,运行时无额外依赖(scope=provided)。

2. 前置配置(必做)

(1)引入依赖(Maven)
xml 复制代码
        <!-- Lombok 核心依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
(2)IDE 安装插件
  • IntelliJ IDEA:Settings → Plugins → 搜索 Lombok → 安装并重启 IDE;
  • Eclipse:Marketplace → 搜索 Lombok → 安装,需确保 eclipse.ini 配置 Lombok 路径。

插件作用:让 IDE 识别 Lombok 注解生成的代码,避免 "找不到方法 / 变量" 的语法提示(不装插件不影响编译,但开发体验差)。

安装成功如图

3. Lombok 常用注解

注解 作用
@Data 组合注解:包含 @Getter+@Setter+@ToString+@EqualsAndHashCode+@RequiredArgsConstructor
@Getter/@Setter 为字段生成 getter/setter 方法(可加在类上(所有字段)或单个字段上)
@ToString 生成 toString() 方法,可通过 exclude 排除指定字段
@EqualsAndHashCode 生成 equals() 和 hashCode() 方法
@NoArgsConstructor 生成无参构造方法
@AllArgsConstructor 生成全参构造方法
@RequiredArgsConstructor 为 final 或 @NonNull 字段生成构造方法
@Builder 实现建造者模式,支持链式调用创建对象
@NonNull 字段非空校验,赋值为 null 时抛出 NullPointerException
@Cleanup 自动关闭资源(如 IO 流、连接),替代 try-finally
@Value 不可变对象,相当于final类+@Data
@SneakyThrows 隐藏异常捕获(简化try-catch)
@Synchronized 安全的同步锁(替代 synchronized 关键字)
@Slf4j 生成 SLF4J 日志对象 log(本文主要内容)

4. 示例

1. 基础核心注解(实体类高频)
(1)@Getter/@Setter:生成 getter/setter 方法
java 复制代码
import lombok.Getter;
import lombok.Setter;

// 类级别:为所有字段生成 getter/setter
@Getter
@Setter
public class GetterSetterDemo {
    private Long id;
    private String name;
    // 字段级别:仅为该字段生成(优先级高于类级别)
    @Getter
    private Integer age;

    public static void main(String[] args) {
        GetterSetterDemo demo = new GetterSetterDemo();
        demo.setId(1L);    // @Setter 生成
        demo.setName("张三");// @Setter 生成
        System.out.println(demo.getId()); // @Getter 生成
        System.out.println(demo.getAge()); // 字段级别 @Getter 生成
    }
}
(2)@ToString:生成 toString () 方法
java 复制代码
import lombok.ToString;

@ToString(
        exclude = "age",        // 排除指定字段,多个字段使用{"age","name"}
        includeFieldNames = true // 是否显示字段名(默认true)
)
public class ToStringDemo {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        ToStringDemo demo = new ToStringDemo();
        demo.id = 1L;
        demo.name = "李四";
        demo.age = 20;
        // 输出:ToStringDemo(id=1, name=李四)(排除了age)
        System.out.println(demo.toString());
    }
}
(3)@EqualsAndHashCode:生成 equals ()/hashCode ()
java 复制代码
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(
    of = {"id", "name"}, // 仅基于指定字段生成(避免父类字段干扰)
    callSuper = false    // 是否包含父类字段(默认false)
)
public class EqualsHashCodeDemo {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        EqualsHashCodeDemo d1 = new EqualsHashCodeDemo();
        d1.id = 1L;
        d1.name = "王五";
        d1.age = 20;

        EqualsHashCodeDemo d2 = new EqualsHashCodeDemo();
        d2.id = 1L;
        d2.name = "王五";
        d2.age = 30;

        // 输出 true(仅比较id和name)
        System.out.println(d1.equals(d2));
        System.out.println(d1.hashCode() == d2.hashCode()); // true
    }
}
(4)@Data:组合注解(Getter+Setter+ToString+EqualsAndHashCode+RequiredArgsConstructor)
java 复制代码
import lombok.Data;

@Data // 一站式生成核心方法,无需单独加其他注解
public class DataDemo {
    private Long id;
    private String name;
    private final Integer type = 1; // final字段,会被@RequiredArgsConstructor包含

    public static void main(String[] args) {
        DataDemo demo = new DataDemo(1); // @RequiredArgsConstructor 生成(仅final字段)
        demo.setId(100L);
        demo.setName("赵六");
        // 输出:DataDemo(id=100, name=赵六, type=1)
        System.out.println(demo);
    }
}
(5)构造方法注解(@NoArgsConstructor/@AllArgsConstructor/@RequiredArgsConstructor)
java 复制代码
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@NoArgsConstructor // 无参构造
@AllArgsConstructor // 全参构造
@RequiredArgsConstructor // 为final/@NonNull字段生成构造
public class ConstructorDemo {
    private Long id;
    @NonNull private String name; // 非空字段
    private final Integer status = 0; // final字段

    public static void main(String[] args) {
        ConstructorDemo d1 = new ConstructorDemo(); // @NoArgsConstructor
        ConstructorDemo d2 = new ConstructorDemo(1L, "钱七", 1); // @AllArgsConstructor
        ConstructorDemo d3 = new ConstructorDemo("孙八"); // @RequiredArgsConstructor(仅name)
    }
}
2. 进阶注解
(1)@Builder:建造者模式(链式创建对象)
java 复制代码
import lombok.Builder;
import lombok.Data;

@Data
@Builder // 生成建造者方法
public class BuilderDemo {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        // 链式调用创建对象,无需手动set
        BuilderDemo demo = BuilderDemo.builder()
                .id(1L)
                .name("周九")
                .age(25)
                .build();
        System.out.println(demo); // BuilderDemo(id=1, name=周九, age=25)
    }
}
(2)@NonNull:非空校验
java 复制代码
import lombok.NonNull;

public class NonNullDemo {
    private String name;

    public void setName(@NonNull String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        NonNullDemo demo = new NonNullDemo();
        // 传入null会抛出NullPointerException(Lombok自动生成校验)
        demo.setName(null);
    }
}
(3)@Cleanup:自动关闭资源
java 复制代码
import lombok.Cleanup;
import java.io.FileInputStream;
import java.io.IOException;

public class CleanupDemo {
    public static void main(String[] args) {
        try {
            @Cleanup FileInputStream is = new FileInputStream("test.txt");
            System.out.println("文件字节数:" + is.available());
        } catch (IOException e) {
            e.printStackTrace();
        } // 自动关闭is
    }
}
(4)@Value:不可变对象(替代 final + @Data)
java 复制代码
import lombok.Value;

@Value // 等价于:final类 + 所有字段final + @Getter + @ToString + @EqualsAndHashCode + @AllArgsConstructor
public class ValueDemo {
    Long id;
    String name;
    Integer age;

    public static void main(String[] args) {
        ValueDemo demo = new ValueDemo(1L, "吴十", 30);
        // demo.setId(2L); // 编译报错:无setter方法(不可变)
        System.out.println(demo.getId()); // 仅能读取
    }
}
(5)@SneakyThrows:隐藏异常捕获(简化 try-catch)
java 复制代码
import lombok.SneakyThrows;
import java.io.FileInputStream;

public class SneakyThrowsDemo {
    // 自动捕获Checked异常,无需手动try-catch或throws声明
    @SneakyThrows
    public static void readFile() {
        FileInputStream is = new FileInputStream("test.txt");
        System.out.println(is.available());
    }

    public static void main(String[] args) {
        readFile(); // 无需处理IOException
    }
}
(5)@Synchronized:安全的同步锁(替代 synchronized 关键字)
java 复制代码
import lombok.Synchronized;

public class SynchronizedDemo {
    private final Object lock = new Object();

    // 等价于 synchronized(this)
    @Synchronized
    public void method1() {
        System.out.println("方法1同步执行");
    }

    // 指定锁对象,等价于 synchronized(lock)
    @Synchronized("lock")
    public void method2() {
        System.out.println("方法2同步执行");
    }

    public static void main(String[] args) {
        SynchronizedDemo demo = new SynchronizedDemo();
        new Thread(demo::method1).start();
        new Thread(demo::method2).start();
    }
}

是不是发现少了个@Slf4j ,这里留到最后单独讲解,使用因为这是本文的核心内容

@Slf4j 注解详解

@Slf4j 是 Lombok 框架提供的一个注解,核心作用是自动为类生成日志对象,无需手动编写 private static final Logger log = LoggerFactory.getLogger(XXX.class); 这类重复代码,简化日志开发。

(1)前置条件

1、Slf4j 是 Lombok 框架提供的一个注解,所以需要导入Lombok依赖以及安装插件(已满足)

2、SLF4J + Logback 为例(主流组合),Logback为SpringBoot默认依赖的日志框架所以需要引入SpringBoot依赖,在引入后会自动引入对应版本的依赖

(2)基础使用
java 复制代码
import lombok.extern.slf4j.Slf4j;

// 注解加在类上,自动生成 log 日志对象
@Slf4j
public class UserService {

    public void addUser(String username) {
        // 直接使用自动生成的 log 对象打印日志
        log.info("开始添加用户,用户名:{}", username); // 信息级日志,支持占位符
        try {
            // 业务逻辑
            log.debug("用户添加中,用户名:{}", username); // 调试级日志(开发环境用)
        } catch (Exception e) {
            // 错误级日志,传入异常对象,自动打印堆栈信息
            log.error("添加用户失败,用户名:{}", username, e);
        }
    }

    public static void main(String[] args) {
        new UserService().addUser("张三");
    }
}
(3)核心逻辑说明

@Slf4j 会在编译阶段为类生成如下代码(无需手动写):

java 复制代码
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserService.class);

日志级别(从低到高):trace < debug < info < warn < error,可通过配置文件(如 logback.xml)控制输出级别。

占位符 {} 是 SLF4J 的特性,避免手动拼接字符串(如 log.info("用户名:" + username)),提升性能和可读性。

(4)日志输入格式

Spring Boot 默认使用 Logback 作为日志框架,因此配置主要围绕 Logback 展开。日志格式的核心要素包括:

时间戳(%d)

日志级别(%level)

线程名(%thread)

类 / 方法名(%logger/%M)

日志内容(%msg)

换行(%n)

方案 1:通过 application.yml/properties 快速修改(简单场景)

适合只需要微调默认格式的场景,无需编写 XML 配置,直接在 Spring Boot 核心配置文件中设置。

示例:application.yml 配置

yml 复制代码
# application.yml
logging:
  # 全局日志级别(可针对特定包单独设置)
  level:
    root: INFO  # 根日志级别
    com.example.demo: DEBUG  # 自定义包的日志级别
  # 控制台输出格式
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
    # 文件输出格式(如果开启了文件日志)
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
  # 可选:开启日志文件输出(指定路径和大小)
  file:
    name: ./logs/demo.log  # 日志文件路径
  logback:
    rollingpolicy:
      max-file-size: 10MB  # 单个日志文件最大大小
      max-history: 7       # 日志文件保留天数

格式参数解释

参数 含义
%d{yyyy-MM-dd HH:mm:ss.SSS} 时间戳,精确到毫秒
%thread 线程名(方括号是自定义格式,可去掉)
%-5level 日志级别(-5 表示左对齐,占 5 个字符,如 INFO、DEBUG)
%logger{50} 类名 / Logger 名称({50} 表示最长显示 50 个字符,超出会截断)
%msg 日志内容(业务代码中 log.info("xxx") 里的内容)
%n 换行符(保证每条日志单独一行)
方案 2:通过 logback-spring.xml 自定义(复杂场景)

如果需要更精细的控制(比如不同日志级别用不同颜色、分模块输出、自定义过滤器),推荐使用 Logback 专属配置文件。

步骤 1:创建配置文件

在 src/main/resources 下新建 logback-spring.xml(Spring Boot 会自动识别这个文件名)。

步骤 2:完整配置示例(含彩色输出 + 多文件拆分)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <!-- 基础配置:定义变量(方便复用) -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <property name="LOG_PATH" value="./logs"/> <!-- 日志文件根路径 -->

    <!-- 1. 控制台输出 Appender(支持彩色) -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 开启彩色输出(仅控制台生效) -->
            <withJansi>true</withJansi>
            <!-- 日志格式(复用上面定义的变量) -->
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 2. 文件输出 Appender(滚动日志,避免单个文件过大) -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件路径 -->
        <file>${LOG_PATH}/demo.log</file>
        <!-- 滚动策略:按大小+日期拆分 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 拆分后的日志文件名:demo-2024-03-13.log -->
            <fileNamePattern>${LOG_PATH}/demo-%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 单个文件最大大小 -->
            <maxFileSize>10MB</maxFileSize>
            <!-- 日志保留天数 -->
            <maxHistory>7</maxHistory>
            <!-- 总日志大小上限(超出会删除旧文件) -->
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 3. 针对特定包的日志配置(比如DAO层单独输出) -->
    <logger name="com.example.demo.mapper" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </logger>

    <!-- 4. 根日志配置 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

关键配置解释

彩色输出:true 开启后,控制台日志会按级别显示不同颜色(INFO 绿色、ERROR 红色、WARN 黄色),需确保 IDE 控制台支持 ANSI 颜色(IDEA 默认支持)。

additivity="false":避免特定包的日志被重复输出到根日志(比如 mapper 层的日志只输出一次)。

滚动策略:TimeBasedRollingPolicy 实现「按日期 + 大小」拆分日志,防止日志文件过大。

相关推荐
止语Lab4 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活4 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困4 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn4 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
dkbnull4 小时前
Spring Boot请求处理组件对比详解
java·spring boot
用户298698530144 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
不能只会打代码4 小时前
Day 011 — Spring 全家桶深度拆解
spring boot·mybatis·spring aop·spring mvc·spring ioc
用户77283104908404 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金4 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛5 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构