当前位置: 首页 > news >正文

【 SLF4J + Logback】日志使用方法+技巧介绍+项目示例(SpringBoot)

一、介绍

本文主要通过SLF4J + Logback(Spring Boot 默认日志组合)来介绍日志框架的使用和技巧,通过实战项目演示工作和学习中日志的使用,良好的打印日志习惯可以快速的帮助我们排查系统问题,减少解决bug的时间和工作量。

二、 项目整体目录结构

三、实现步骤

1.创建springboot项目,编辑pom文件,引入所需依赖

由于Spring Boot 父工程已包含 spring-boot-starter-logging,因此无需额外添加日志依赖,只需引入spring web依赖和spring aop,用于演示接口依赖和使用切面工程统一处理。

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

2.日志核心配置

在 src/main/resources 下创建 logback-spring.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{36} - %msg%n"/><property name="LOG_PATH" value="logs/springboot-log-demo"/> <!-- 日志文件存储路径 --><!-- 控制台输出 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- 业务日志文件输出(按天滚动、压缩、清理) --><appender name="BUSINESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/business.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/business.%d{yyyy-MM-dd}.log.gz</fileNamePattern><maxHistory>30</maxHistory> <!-- 保留30天日志 --></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 错误日志文件输出 --><appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/error.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 异步日志配置 --><appender name="ASYNC_BUSINESS" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold> <!-- 不丢弃日志 --><queueSize>512</queueSize> <!-- 队列大小 --><appender-ref ref="BUSINESS_FILE"/></appender><appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>512</queueSize><appender-ref ref="ERROR_FILE"/></appender><!-- 全局日志级别 --><root level="INFO"><appender-ref ref="CONSOLE"/><appender-ref ref="ASYNC_BUSINESS"/><appender-ref ref="ASYNC_ERROR"/></root><!-- 自定义包的日志级别(示例:降低 mapper 包日志级别) --><logger name="com.example.logdemo.mapper" level="DEBUG"/>
</configuration>

日志配置详细解析

1. 基础配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
  • scan="true"开启配置文件自动扫描(当配置文件修改时,无需重启应用即可生效)。
  • scanPeriod="30 seconds"扫描间隔为 30 秒。

2. 全局变量定义

<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<property name="LOG_PATH" value="logs/springboot-log-demo"/>
  • LOG_PATTERN日志格式模板,各占位符含义:
    • %d日志时间(精确到毫秒)。
    • [%thread]线程名。
    • %-5level日志级别(左对齐,占 5 个字符)。
    • %logger{36}日志所在类的全限定名(最长 36 个字符,超出会缩写)。
    • %msg%n日志消息 + 换行。
  • LOG_PATH日志文件存储根路径(相对路径,实际会生成在项目根目录下的 logs/springboot-log-demo 文件夹)。

3. 日志输出器(Appender)配置

Logback 通过 Appender 定义日志的输出目标(控制台、文件等),以下是配置的 5 种 Appender

3.1 控制台输出(CONSOLE)

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder>
</appender>
  • 作用:将日志输出到控制台。
  • ConsoleAppenderLogback 内置的控制台输出类。
  • encoder指定日志格式(复用 LOG_PATTERN 变量)。

3.2 业务日志文件输出(BUSINESS_FILE)

<appender name="BUSINESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/business.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/business.%d{yyyy-MM-dd}.log.gz</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter>
</appender>
  • 作用:将 INFO 级别的日志输出到文件,并按天滚动、压缩和清理。
  • 核心配置:
    • RollingFileAppender支持日志滚动(切割)的文件输出类。
    • <file>当前日志文件路径(business.log)。
    • TimeBasedRollingPolicy按时间滚动策略:
      • fileNamePattern滚动后文件名格式(例如 business.2025-11-05.log.gz),gz 表示自动压缩。
      • maxHistory保留最近 30 天的日志文件,超过则自动删除。
    • LevelFilter过滤日志级别,仅接受 INFO 级别,其他级别拒绝。

3.3 错误日志文件输出(ERROR_FILE)

<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/error.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter>
</appender>
  • 作用:将 ERROR 级别的日志输出到文件,配置与 BUSINESS_FILE 类似,但:
    • 日志文件名为 error.log
    • 仅接受 ERROR 级别日志。

3.4 异步日志(ASYNC_BUSINESS 和 ASYNC_ERROR)

<appender name="ASYNC_BUSINESS" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>512</queueSize><appender-ref ref="BUSINESS_FILE"/>
</appender><appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>512</queueSize><appender-ref ref="ERROR_FILE"/>
</appender>
  • 作用:通过异步方式输出日志,避免同步写文件阻塞主线程,提升性能。
  • 核心配置:
    • AsyncAppender异步日志包装类,依赖一个同步 Appender(如 BUSINESS_FILE)。
    • discardingThreshold="0"不丢弃任何日志(即使队列满了也会阻塞等待,确保日志不丢失)。
    • queueSize="512"异步队列大小(最多缓存 512 条日志)。

4. 日志级别配置

4.1 全局日志级别(root)

<root level="INFO"><appender-ref ref="CONSOLE"/><appender-ref ref="ASYNC_BUSINESS"/><appender-ref ref="ASYNC_ERROR"/>
</root>
  • level="INFO"全局默认日志级别为 INFO(即输出 INFO 及以上级别日志:INFOWARNERROR)。
  • 关联的 Appender:日志会同时输出到控制台、异步业务日志、异步错误日志。

4.2 自定义包日志级别

<logger name="com.example.logdemo.mapper" level="DEBUG"/>
  • 作用:为特定包(com.example.logdemo.mapper)单独设置日志级别为 DEBUG,覆盖全局的 INFO 级别。
  • 场景:通常用于调试持久层(如 MyBatis 的 Mapper 接口)的 SQL 日志。

总结

该配置实现了以下功能:

  1. 日志同时输出到控制台和文件。

  2. 按级别分离日志:INFO 级日志到 business.logERROR 级日志到 error.log

  3. 日志文件按天滚动、自动压缩,保留 30 天历史。

  4. 通过异步日志提升应用性能,且不丢弃任何日志。

  5. 支持配置文件热更新,无需重启应用。

3.代码实现

3.1 启动类(LogDemoApplication.java
@SpringBootApplication
public class LogDemoApplication {private static final Logger logger = LoggerFactory.getLogger(LogDemoApplication.class);public static void main(String[] args) {logger.info("应用启动中...");SpringApplication.run(LogDemoApplication.class, args);logger.info("应用启动完成!");}}
3.2 控制器(LogController.java

演示接口入参、出参、异常的日志记录


@RestController
public class LogController {private static final Logger logger = LoggerFactory.getLogger(LogController.class);@Resourceprivate LogService logService;@GetMapping("/demo")public String demo(@RequestParam String name) {// 记录接口入参logger.info("接收到请求,name = {}", name);try {String result = logService.process(name);// 记录接口出参logger.info("接口返回结果,result = {}", result);return result;} catch (Exception e) {// 记录异常(含堆栈)logger.error("接口处理异常", e);throw e;}}@GetMapping("/error")public void error() {logger.error("触发模拟异常");throw new RuntimeException("模拟运行时异常");}
}
3.3 服务类(LogService.java
@Service
public class LogService {private static final Logger logger = LoggerFactory.getLogger(LogService.class);public String process(String name) {// 记录业务步骤logger.info("开始处理业务,name = {}", name);// 循环场景:用 StringBuilder 优化日志输出(避免循环内频繁打日志)StringBuilder sb = new StringBuilder();for (int i = 0; i < 5; i++) {sb.append("步骤").append(i).append("完成,");// 批量记录(每5次记录一次,或循环结束后记录)if (i % 5 == 0 || i == 4) {logger.info("循环执行进度:{}", sb);sb.setLength(0); // 清空,复用 StringBuilder}}// 敏感信息过滤:日志中不直接记录密码等敏感信息String password = "123456"; // 模拟敏感信息logger.info("密码已加密存储,请勿泄露"); // 避免直接打印 passwordreturn "处理结果:" + name;}
}
3.4 AOP 切面(LogAspect.java
@Aspect
@Component
public class LogAspect {private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);// 定义切点:匹配 com.breeze.log_demo 包下所有类的所有方法@Pointcut("execution(* com.breeze.log_demo..*.*(..))")public void logPointcut() {}// 环绕通知:记录方法入参、出参、耗时@Around("logPointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();// 记录方法入参logger.info("方法 {} 入参:{}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));try {Object result = joinPoint.proceed();// 记录方法出参和耗时long cost = System.currentTimeMillis() - startTime;logger.info("方法 {} 出参:{},耗时:{}ms",joinPoint.getSignature().getName(), result, cost);return result;} catch (Throwable e) {// 记录方法异常logger.error("方法 {} 执行异常", joinPoint.getSignature().getName(), e);throw e;}}
}
3.5 测试类(LogDemoApplicationTests.java

演示测试环境日志验证

@SpringBootTest
class LogDemoApplicationTests {private static final Logger logger = LoggerFactory.getLogger(LogDemoApplicationTests.class);@Testvoid contextLoads() {logger.debug("Debug 级别日志(测试环境可见)");logger.info("Info 级别日志(测试环境可见)");logger.error("Error 级别日志(测试环境可见)");}
}

四、运行及效果展示

1. 启动项目:运行 LogDemoApplication,观察控制台和 logs 目录生成的日志文件。

日志文件里内容准确:

2. 调用接口:

  • 访问 http://localhost:8080/demo?name=test,查看接口日志的入参、出参、业务步骤记录。




    error.log 日志文件 成功记录到错误日志:

五、异常的相关知识总结

1. 异常体系结构

Throwable(顶层类)
├─ Error(错误:JVM层面的严重问题,程序无法处理)
│  ├─ VirtualMachineError(虚拟机错误)
│  │  ├─ OutOfMemoryError(内存溢出)
│  │  ├─ StackOverflowError(栈溢出)
│  │  └─ InternalError(内部错误)
│  ├─ AWTError(AWT组件错误)
│  └─ LinkageError(类链接错误)
│     ├─ NoClassDefFoundError(类定义未找到)
│     └─ UnsatisfiedLinkError(本地方法链接失败)
│
└─ Exception(异常:程序可处理的问题)├─ RuntimeException(运行时异常:非检查型,编译不强制处理)│  ├─ NullPointerException(空指针)│  ├─ IndexOutOfBoundsException(索引越界)│  ├─ ClassCastException(类型转换)│  ├─ IllegalArgumentException(非法参数)│  ├─ ArithmeticException(算术错误)│  └─ ConcurrentModificationException(并发修改)│└─ 受检异常(Checked Exception:编译强制处理)├─ IOException(IO操作异常)│  ├─ FileNotFoundException(文件未找到)│  └─ IOException(通用IO错误)├─ SQLException(数据库操作异常)├─ ClassNotFoundException(类未找到)└─ InterruptedException(线程中断)

2. Error(错误):不可处理的严重问题

错误类型含义常见场景解决方式
OutOfMemoryError内存溢出(JVM 堆 / 方法区不足)创建大量对象未释放、大文件加载1. 调大 JVM 内存参数(-Xms/-Xmx)2. 优化对象生命周期,避免内存泄漏
StackOverflowError栈溢出(方法调用栈深度超过限制)递归调用无终止条件、多层嵌套方法1. 修复递归终止条件2. 减少方法嵌套层级
NoClassDefFoundError运行时找不到类定义(编译存在,运行缺失)类路径(classpath)配置错误、jar 包缺失1. 检查类路径是否包含目标类2. 确保依赖 jar 包存在
UnsatisfiedLinkError本地方法(JNI)链接失败调用的本地库(.dll/.so)不存在或版本不匹配1. 检查本地库路径是否正确2. 确保库文件与系统兼容

3. Exception(异常):可处理的问题

3.1 运行时异常(RuntimeException):非检查型异常

特点:编译时不强制处理,通常由程序逻辑错误导致,需通过代码优化避免。

异常类型含义常见场景解决方式
NullPointerException调用 null 对象的方法 / 属性String s = null; s.length();1. 调用前判空(if (obj != null))2. 使用Optional类避免空指针
IndexOutOfBoundsException数组 / 集合索引越界List<String> list = new ArrayList<>(); list.get(0);1. 访问前检查索引范围(index < size)2. 使用循环时避免硬编码索引
ClassCastException类型强制转换失败Object obj = "str"; Integer i = (Integer) obj;1. 转换前用instanceof判断类型2. 避免无意义的强制转换
IllegalArgumentException方法接收非法参数传入负数到要求正数的方法(如new ArrayList(-1)1. 方法内校验参数合法性2. 调用前确保参数符合要求
ArithmeticException算术错误(如除以 0)int a = 1 / 0;1. 避免除数为 02. 计算前校验除数合法性
ConcurrentModificationException迭代集合时并发修改(如边遍历边删除)for (String s : list) { list.remove(s); }1. 使用迭代器的remove()方法2. 使用CopyOnWriteArrayList等线程安全集合
3.2 受检异常(Checked Exception):编译时强制处理

特点:编译器要求必须捕获或声明抛出,通常由外部环境导致(如 IO、网络问题)。

异常类型含义常见场景解决方式
IOExceptionIO 操作失败(读 / 写文件、网络流)读取不存在的文件、网络中断时写数据1. 捕获异常并处理(如提示 "文件不存在")2. 确保资源路径正确,网络通畅
FileNotFoundException找不到指定文件 / 目录(IOException子类)new FileInputStream("不存在的文件.txt")1. 检查文件路径是否正确2. 操作前判断文件是否存在(file.exists()
SQLException数据库操作失败连接参数错误、SQL 语法错误、表不存在1. 检查数据库连接配置和 SQL 语句2. 打印异常信息定位具体错误(如e.getMessage()
ClassNotFoundException加载类时找不到类定义(与NoClassDefFoundError的区别:前者是主动加载失败,后者是被动引用失败)Class.forName("com.mysql.Driver")1. 检查类全限定名是否正确2. 确保依赖的 jar 包已引入
InterruptedException线程在等待 / 休眠时被中断Thread.sleep(1000)时调用thread.interrupt()1. 捕获后可恢复中断状态(Thread.currentThread().interrupt())2. 根据业务决定是否终止线程
http://www.dtcms.com/a/572875.html

相关文章:

  • 重构可见性:IT资产管理的下一次觉醒
  • mermaid install for free docker
  • 0 基础学前端:100 天拿 offer 实战课(第 6 天)—— JavaScript 入门:给网页加 “动态交互” 的 3 个核心案例
  • 宝塔nginx http转https代理
  • 建设企业网站登录901如何修改wordpress主题模板
  • 系统架构设计师论文-论软件体系结构的演化
  • 【大模型学习】第一章:自然语言处理(NLP)核心概念
  • 软件测试之压力测试知识总结
  • 高级系统架构师笔记——系统架构设计基础知识(3)软件架构风格
  • 备案网站负责人必须为法人吗网站建设需要客户提供什么内容
  • QML学习笔记(五十一)QML与C++交互:数据转换——基本数据类型
  • 机载电脑部署安装px4环境详细教程
  • Android APP 的压力测试与优化
  • 网站建设需要多久seo综合查询怎么关闭
  • 前端需要掌握多少Node.js?
  • Node.js 实现企业内部消息通知系统(钉钉/企业微信机器人)
  • 赤峰建设局网站wordpress主题制作工具
  • 告别混乱文本:基于深度学习的 PDF 与复杂版式文档信息抽取
  • 嵌入式Linux C语言程序设计五
  • 笔记:现代操作系统:原理与实现(8)
  • HashiCorp Vault 镜像拉取与 Docker 部署全指南
  • Oracle数据库常用视图:dba_datapump_jobs
  • WordPress wpForo Forum插件漏洞CVE-2025-11740复现
  • JAVA115回顾:Leecode 两数之和、无重复字符的最长字串、翻转二叉树、 最长公共前缀
  • 机器学习 大数据情境下blending-示例
  • 企业网站制作模板深圳坪山最新消息
  • Java进阶之多线程
  • idea2025版本设置springboot加载热部署
  • 合肥电子商务开发网站建设手机html5网站开发
  • 高速摄像机、科学相机赋能燃烧与多相流研究