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 实现「按日期 + 大小」拆分日志,防止日志文件过大。

相关推荐
廋到被风吹走2 小时前
【LangChain4j】特点功能及使用场景
后端·python·flask
孫治AllenSun2 小时前
【线程池】优化等待队列和拒绝策略
java·spring boot·spring cloud
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于Spring Boot的体育场地预约管理系统为例,包含答辩的问题和答案
java·spring boot·后端
青槿吖2 小时前
第二篇:告别XML臃肿配置!Spring注解式IOC/DI保姆级教程,从入门到真香
xml·java·开发语言·数据库·后端·sql·spring
摇滚侠3 小时前
讲一讲 SpringMVC,线程变量 ThreadLocal 的使用
java·spring boot·intellij-idea
小哇6663 小时前
第2篇:Spring Boot + WebSocket + 消息队列STOMP协议(Rabbitmq) 架构原理
后端·websocket
Victor3564 小时前
MongoDB(39)如何使用聚合进行过滤?
后端
Victor3564 小时前
MongoDB(38)如何使用聚合进行投影?
后端
AI_56785 小时前
基于智优达平台的Python教学实践:从环境搭建到自动评测
开发语言·前端·人工智能·后端·python