网站开发业务需求分析,百度建站多少钱,有哪些做室内设计好用的网站有哪些,江苏网站建设网络推广目录
Spring Boot 日志详解#xff1a;从入门到精通#xff08;新手版#xff09;
1. 日志概述#xff1a;为什么要学#xff1f;
1.1 从System.out.println到专业日志框架
2. 日志使用#xff1a;手把手教你写代码
2.1 打印日志#xff1a;第一个日志程序
知识点…目录Spring Boot 日志详解从入门到精通新手版1. 日志概述为什么要学1.1 从System.out.println到专业日志框架2. 日志使用手把手教你写代码2.1 打印日志第一个日志程序知识点如何获取日志对象知识点日志占位符避免字符串拼接2.2 日志框架介绍门面模式详解知识点什么是门面模式知识点SLF4J就是日志框架的门面2.3 日志格式说明2.4 日志级别代码演示与配置效果知识点六级别详解知识点配置日志级别如何影响代码行为2.5 日志配置详解知识点日志持久化存文件知识点日志分割配置3. Lombok简化日志更优雅的写法3.1 添加依赖3.2 使用Slf4j注解4. 日志最佳实践与扩展知识4.1 日志安全避免记录敏感信息4.2 高性能日志记录性能优化技巧技巧1始终使用占位符技巧2复杂日志使用条件判断4.3 集中式日志管理微服务场景4.4 日志标准化JSON结构化日志4.5 日志与追踪结合分布式TraceID5. 总结知识体系回顾5.1 核心知识点速查表5.2 新手学习路径建议5.3 代码 vs 结论如何建立联系附录完整项目结构示例Spring Boot 日志详解从入门到精通新手版1. 日志概述为什么要学1.1 从System.out.println到专业日志框架新手理解想象一下你做的程序就像一个黑盒子。当它出问题时你需要透过窗户看里面发生了什么。System.out.println就像在小盒子上戳个小洞只能看到一点点而专业日志框架就像安装了监控摄像头能全方位、分级别、可配置地观察系统状态。核心痛点System.out.println打印的信息只能看到控制台程序重启就消失无法区分信息的重要性是致命错误还是普通提示不能控制输出位置只能控制台不能存文件影响性能每次都要打印无法动态关闭2. 日志使用手把手教你写代码2.1 打印日志第一个日志程序知识点如何获取日志对象/* * 【完整代码示例1基础日志打印】 * 这段代码演示了如何在Spring Boot中获取并使用日志对象 * 对应结论Spring Boot内置SLF4J框架通过LoggerFactory获取日志对象 */ // import区域导入必要的类 import org.slf4j.Logger; // 【第1行】导入SLF4J的Logger接口这是所有日志操作的入口 import org.slf4j.LoggerFactory; // 【第2行】导入LoggerFactory工厂类用于创建Logger实例 import org.springframework.web.bind.annotation.RequestMapping; // 【第3行】导入Spring MVC的请求映射注解用于定义URL路径 import org.springframework.web.bind.annotation.RestController; // 【第4行】导入REST控制器注解表示这个类处理HTTP请求 // 类定义区域 RestController // 【第6行】标记这个类是一个RESTful控制器能处理HTTP请求并返回JSON或纯文本 public class LoggerController { // 【第7行】定义控制器类类名自定义 // 核心获取日志对象 // 【第9行】声明一个静态(final不可变)、线程安全的日志对象 // static所有实例共享同一个logger对象节省内存 // final确保logger引用不会被修改保证线程安全 // LoggerFactory.getLogger(LoggerController.class)创建与当前类绑定的logger // 参数作用LoggerController.class告诉框架这个日志来自哪个类日志中会显示类名 private static final Logger logger LoggerFactory.getLogger(LoggerController.class); // 请求处理方法 RequestMapping(/logger) // 【第13行】定义URL路径访问 http://localhost:8080/logger 会触发此方法 public String logger() { // 【第14行】定义处理请求的方法返回字符串 // 【第16行】使用logger对象打印INFO级别的日志日志内容自定义 // 这行代码执行后日志会显示在控制台默认配置 logger.info(--------------要输出日志的内容----------------); return 打印日志; // 【第18行】返回给浏览器的响应内容 } }代码与结论的联系结论通过LoggerFactory获取当前类的Logger实例代码体现第9行LoggerFactory.getLogger(LoggerController.class)就是具体的获取方式参数LoggerController.class保证了日志中能显示完整的类名路径方便定位问题知识点日志占位符避免字符串拼接// 演示代码片段 String userId user123; String action login; /* * 【重点】为什么用占位符{}而不是字符串拼接 * * 字符串拼接的问题 * logger.debug(用户 userId 执行了 action 操作); * 即使日志级别设置为WARN不输出DEBUGJVM仍会先执行字符串拼接浪费性能 * * 占位符的优势 * 下面这行代码当日志级别为WARN时不会执行字符串拼接直接跳过性能更好 */ logger.debug(用户 {} 执行了 {} 操作, userId, action);2.2 日志框架介绍门面模式详解知识点什么是门面模式现实生活中的例子你去餐厅吃饭不需要直接去后厨告诉厨师怎么炒菜、告诉服务员怎么摆盘。你只需要对服务员门面说我要一份宫保鸡丁服务员会协调后厨、配菜、上菜等所有子系统。代码层面的体现/* * 【完整代码示例2门面模式演示】 * 这段代码展示了如何通过门面模式统一控制多个灯 * 对应结论门面模式简化客户端与复杂子系统的交互 */ // 客户端代码使用门面 public class FacadePatternDemo { // 【第1行】演示类 public static void main(String[] args) { // 【第2行】主方法程序入口 // 【第4行】创建门面对象客户端只与门面交互不直接操作具体灯 LightFacade lightFacade new LightFacade(); // 【第5行】调用门面的统一方法一句代码开启所有灯 // 结论体现客户端无需知道有几个灯、灯怎么开只需调用门面方法 lightFacade.lightOn(); } } // 门面类统一接口 /* * 灯的门面提供统一的开关控制 * 门面角色隐藏了子系统的复杂性 */ class LightFacade { // 【第12行】门面对象 // 【第14-16行】子系统包含多个具体灯对象类的集合 // 这些是门面管理的子系统客户端不需要直接访问 private Light livingRoomLight new LivingRoomLight(); // 客厅灯 private Light hallLight new HallLight(); // 走廊灯 private Light diningLight new DiningLight(); // 餐厅灯 // 门面方法封装子系统操作 // 【第19行】统一开启所有灯遍历调用每个子系统的on方法 public void lightOn() { livingRoomLight.on(); // 调用客厅灯on hallLight.on(); // 调用走廊灯on diningLight.on(); // 调用餐厅灯on } // 【第24行】统一关闭所有灯 public void lightOff() { livingRoomLight.off(); hallLight.off(); diningLight.off(); } } // 子系统接口定义规范 interface Light { // 【第30行】灯的接口定义子系统必须实现的方法 void on(); // 开灯方法 void off(); // 关灯方法 } // 子系统实现具体灯类 // 【第35-47行】客厅灯实现实现Light接口的具体行为 class LivingRoomLight implements Light { Override public void on() { System.out.println(打开客厅灯); // 具体开灯逻辑 } Override public void off() { System.out.println(关闭客厅灯); } } // 【第49-61行】走廊灯实现 class HallLight implements Light { Override public void on() { System.out.println(打开走廊灯); } Override public void off() { System.out.println(关闭走廊灯); } } // 【第63-75行】餐厅灯实现 class DiningLight implements Light { Override public void on() { System.out.println(打开餐厅灯); } Override public void off() { System.out.println(关闭餐厅灯); } }代码与结论的联系结论减少系统相互依赖客户端只与门面对象交互代码体现FacadePatternDemo的main方法中客户端只创建了LightFacade对象调用了lightOn()方法完全没有直接引用LivingRoomLight、HallLight等子系统类实现了隔离知识点SLF4J就是日志框架的门面示意图解读【你的应用代码】 -- 【SLF4J API门面接口】 -- 【SLF4J绑定适配器】 -- 【具体实现Logback】为什么需要这个门面// 假设你直接依赖Log4j import org.apache.log4j.Logger; // 硬编码依赖Log4j Logger logger Logger.getLogger(MyClass.class); // 问题来了如果项目要换成Logback你需要修改所有类的import和代码 // 工作量巨大风险极高 // 使用SLF4J门面 import org.slf4j.Logger; // 接口不变 import org.slf4j.LoggerFactory; Logger logger LoggerFactory.getLogger(MyClass.class); // 好处切换实现只需改依赖和配置代码完全不用动 // 比如从Log4j换成Logback只需在pom.xml中替换依赖代码零改动2.3 日志格式说明默认日志格式2023-05-15 10:30:45.123 INFO 12345 --- [nio-8080-exec-1] com.example.demo.LoggerController : --------------要输出日志的内容----------------逐段解析2023-05-15 10:30:45.123时间戳精确到毫秒用于追踪事件发生顺序INFO日志级别告诉你这条信息的重要性12345进程ID多实例部署时区分是哪个进程打印的---分隔符视觉分隔提高可读性[nio-8080-exec-1]线程名并发场景下定位哪个线程执行的com.example.demo.LoggerControllerLogger名通常用类名精确定位代码位置:分隔符--------------要输出日志的内容----------------日志消息你自定义的内容结论与代码的联系结论这种格式设计使得日志信息丰富且结构化代码体现当你执行logger.info(要输出日志的内容)时Logback框架会自动在前面拼接时间、级别、线程等信息无需你手动添加2.4 日志级别代码演示与配置效果知识点六级别详解/* * 【完整代码示例3日志级别演示】 * 这段代码演示如何打印不同级别的日志 * 对应结论日志级别从低到高为 TRACE DEBUG INFO WARN ERROR * 对应结论Spring Boot默认只显示INFO及以上级别 */ import org.slf4j.Logger; // 导入日志接口 import org.slf4j.LoggerFactory; // 导入工厂类 import org.springframework.web.bind.annotation.RequestMapping; // 导入请求映射 import org.springframework.web.bind.annotation.RestController; // 导入REST控制器 RestController // 【第6行】标记为REST控制器 public class LoggerController { // 【第9行】获取与当前类绑定的logger对象 private static final Logger logger LoggerFactory.getLogger(LoggerController.class); /** * 打印不同级别的日志 * return 响应字符串 */ RequestMapping(/printLog) // 【第15行】定义URL路径 public String printLog() { // 【第16行】处理方法 // 【第18行】TRACE级别最详细用于追踪代码执行路径默认不显示 logger.trace( trace); // 【第20行】DEBUG级别调试信息开发环境使用默认不显示 logger.debug( debug); // 【第22行】INFO级别普通信息记录业务流程默认显示 logger.info( info); // 【第24行】WARN级别警告信息可能有问题默认显示 logger.warn( warn); // 【第26行】ERROR级别错误信息需要处理默认显示 logger.error( error); return 打印不同级别的日志; // 【第29行】返回响应 } }代码与结论的联系结论Spring Boot默认配置只输出INFO级别及以上的日志代码体现执行printLog()方法后控制台只会看到info、warn、error三行trace和debug不会显示。这是因为默认配置相当于logging.level.rootINFO低于INFO级别的被过滤掉了知识点配置日志级别如何影响代码行为配置文件# application.properties logging.level.rootdebug # 设置根日志级别为DEBUG # 也可以针对特定包设置 logging.level.com.example.demotrace # 这个包下所有类都输出TRACE级别配置效果示意图配置前默认INFO 代码中的5条日志 → [过滤器] → 只显示≥INFO的3条 配置后rootDEBUG 代码中的5条日志 → [过滤器] → 显示≥DEBUG的4条TRACE仍被过滤 配置后com.example.demoTRACE 代码中的5条日志 → [过滤器] → 全部5条都显示代码与配置的联系配置结论logging.level可以针对不同包设置级别代码体现如果你的项目结构是com.example.demo.controller.LoggerController那么logging.level.com.example.demo.controllertrace这个配置就会让LoggerController类中的logger.trace()生效2.5 日志配置详解知识点日志持久化存文件方式一指定文件名# application.yml logging: file: name: logger/springboot.log # 相对路径会在项目根目录下创建logger文件夹方式二指定目录# application.yml logging: file: path: D:/temp # 只指定目录文件名默认为spring.log代码与配置的联系结论如果同时配置name和path只有name生效实际操作yaml logging: file: name: myapp.log # 这个生效 path: D:/logs # 这个被忽略日志会保存在./myapp.log而不是D:/logs/spring.log知识点日志分割配置# application.yml logging: logback: rollingpolicy: max-file-size: 1KB # 单个文件超过1KB就分割 file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i # 分割后命名规则效果演示初始文件springboot.log 当大小1KB后 - springboot.log新文件 - springboot.log.2023-05-15.0旧文件 - springboot.log.2023-05-15.1更旧的文件时间格式占位符说明%d{yyyy-MM-dd}按天分割%d{yyyy-MM-dd_HH}按小时分割%i序号从0开始递增3. Lombok简化日志更优雅的写法3.1 添加依赖!-- pom.xml中添加Lombok依赖 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional !-- 可选依赖不传递给其他项目 -- /dependency3.2 使用Slf4j注解/* * 【完整代码示例4Lombok简化日志】 * 这段代码演示如何使用Lombok的Slf4j注解自动生成logger * 对应结论Lombok减少样板代码直接使用log变量 */ // import区域 import lombok.extern.slf4j.Slf4j; // 【第1行】导入Lombok的日志注解 import org.springframework.web.bind.annotation.RestController; // 【第2行】导入REST控制器 // 使用Slf4j注解 Slf4j // 【第4行】**核心注解**在编译期自动生成以下代码 // private static final org.slf4j.Logger log org.slf4j.LoggerFactory.getLogger(LogController.class); RestController // 【第5行】标记为REST控制器 public class LogController { // 【第6行】类定义 /** * 演示日志输出方法 */ public void log() { // 【第9行】普通方法 // 【第11行】直接使用自动生成的log对象无需手动创建 // 效果与logger.info完全相同 log.info(--------------要输出日志的内容----------------); // 高级用法1占位符避免字符串拼接 String userName 张三; int userId 123; // 【第16行】使用{}占位符效果与2.1.2节相同 // Lombok生成的log对象支持所有SLF4J方法 log.debug(用户 {} (ID: {}) 访问了系统, userName, userId); // 高级用法2条件日志提升性能 // 【第20行】判断是否开启DEBUG级别避免执行耗时操作 // 这是一个性能优化技巧对复杂日志尤其重要 if (log.isDebugEnabled()) { // 【第22行】只有在DEBUG开启时才执行复杂计算 // 如果DEBUG关闭generateExpensiveLogData()方法根本不会被执行 String expensiveData generateExpensiveLogData(); log.debug(详细信息: {}, expensiveData); } } /** * 模拟生成复杂日志数据的耗时操作 * return 复杂数据字符串 */ private String generateExpensiveLogData() { // 【第32行】模拟耗时操作睡眠100毫秒 try { Thread.sleep(100); // 模拟耗时100ms } catch (InterruptedException e) { // 【第34行】处理中断异常 // 【第35行】恢复线程中断状态这是最佳实践 Thread.currentThread().interrupt(); } return 复杂日志数据; // 【第37行】返回结果 } }代码与结论的联系结论Lombok减少样板代码无需手动创建Logger代码体现比较示例1和示例4示例1需要写第9行的LoggerFactory.getLogger(...)而示例4只需在类上加Slf4j注解直接使用log变量结论使用log.isDebugEnabled()避免不必要的计算代码体现第20行判断如果DEBUG级别未开启第22行的generateExpensiveLogData()方法耗时100ms根本不会执行节省性能4. 日志最佳实践与扩展知识4.1 日志安全避免记录敏感信息/* * 【完整代码示例5日志安全脱敏】 * 这段代码演示如何安全地记录日志 * 对应结论避免记录密码等敏感信息必要时脱敏处理 */ public class SecurityLogDemo { private static final Logger logger LoggerFactory.getLogger(SecurityLogDemo.class); public void login(String username, String password) { // 错误示范直接记录密码 // 【第9行】**极度危险**日志文件可能被多人访问密码明文存储违反安全规范 // 如果日志被黑客获取直接得到用户密码 logger.info(用户 {} 使用密码 {} 登录, username, password); // ❌ 不要这样做 // 正确示范只记录业务行为 // 【第13行】**安全做法**只记录谁尝试登录不记录密码 logger.info(用户 {} 尝试登录, username); // ✅ 推荐 // 正确示范调试时脱敏显示 // 【第17行】脱敏处理将密码替换为掩码 // 这样即使开了DEBUG级别也看不到真实密码 String maskedPassword password ! null ? *** : null; // 脱敏逻辑 logger.debug(用户 {} 使用密码 {} 登录, username, maskedPassword); // ✅ 安全 // 实际业务逻辑... boolean success validatePassword(password); if (success) { logger.info(用户 {} 登录成功, username); } else { logger.warn(用户 {} 登录失败, username); // 记录失败但不记录密码 } } private boolean validatePassword(String password) { // 模拟密码验证 return true; } }代码与结论的联系结论避免记录敏感信息必要时脱敏代码体现第9行错误示范直接记录password第17行正确示范使用***替代真实密码为什么这样做日志通常会被收集到集中系统可能被运维、开发人员查看明文密码是重大安全隐患4.2 高性能日志记录性能优化技巧技巧1始终使用占位符// ❌ 低效做法先拼接字符串再判断日志级别 // 即使DEBUG未开启也会执行getName()和getAction()方法并拼接字符串 logger.debug(当前用户: user.getName() , 操作: user.getAction()); // ✅ 高效做法使用占位符 // 如果DEBUG未开启getName()和getAction()都不会执行直接跳过 logger.debug(当前用户: {}, 操作: {}, user::getName, user::getAction); // Java 8方法引用更优技巧2复杂日志使用条件判断// 当需要构造复杂日志消息时 public void processLargeData(ListData dataList) { // 假设dataList有10000条数据 // ❌ 低效即使DEBUG关闭也会先格式化整个列表为字符串 logger.debug(处理数据: {}, dataList.toString()); // toString()耗时很长 // ✅ 高效先判断再执行耗时操作 if (logger.isDebugEnabled()) { // 【第7行】轻量级判断 // 只有DEBUG开启才执行toString() String dataSummary dataList.subList(0, 10).toString() ...共 dataList.size() 条; logger.debug(处理数据: {}, dataSummary); // 【第10行】实际记录 } // 处理数据... }代码与结论的联系结论使用占位符避免不必要的字符串拼接代码体现低效做法中操作符会立即执行拼接高效做法中{}占位符在日志级别判断后才处理性能差异在高并发场景下这种优化可减少大量CPU和内存开销4.3 集中式日志管理微服务场景问题场景你有10个微服务每个服务有3个实例日志分散在30台机器上。用户投诉下单失败你如何快速定位问题解决方案ELK Stack架构【各个服务】 → 【Filebeat收集】 → 【Logstash处理】 → 【Elasticsearch存储】 → 【Kibana展示】Spring Boot集成Filebeat# 在每个服务的application.yml中配置 logging: file: name: /opt/logs/${spring.application.name}.log # 统一日志路径 # Filebeat配置filebeat.yml filebeat.inputs: - type: log enabled: true paths: - /opt/logs/*.log # 收集所有服务日志 output.logstash: hosts: [logstash:5044]代码与结论的联系结论单机日志管理已不足够需要集中式解决方案代码体现通过配置logging.file.name将日志落盘到统一路径再由Filebeat收集。没有这些配置日志只在控制台无法集中管理4.4 日志标准化JSON结构化日志/* * 【完整代码示例6JSON结构化日志】 * 需要添加依赖net.logstash.logback:logstash-logback-encoder */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.logstash.logback.marker.Markers; // 导入JSON标记类 public class StructuredLogDemo { private static final Logger logger LoggerFactory.getLogger(StructuredLogDemo.class); public void userLogin(Long userId, String action, String status) { // 传统日志非结构化 // 缺点只能通过正则表达式解析效率低容易出错 logger.info(用户 {} 执行 {} 操作状态: {}, userId, action, status); // 输出2023-05-15...用户 123 执行 login 操作状态: success // 结构化日志JSON格式 // 优点可直接作为JSON解析每个字段独立可查询 logger.info( Markers.append(userId, userId) // 【第17行】添加userId字段 .and(Markers.append(action, action)) // 【第18行】添加action字段 .and(Markers.append(status, status)), // 【第19行】添加status字段 用户登录 // 【第20行】简洁的消息 ); // 输出示例 // { // timestamp: 2023-05-15T10:30:45.12308:00, // level: INFO, // logger: com.example.demo.StructuredLogDemo, // message: 用户登录, // userId: 123, // action: login, // status: success // } } }代码与结论的联系结论结构化日志便于机器解析可直接作为字段查询代码体现第17-19行的Markers.append()将额外数据作为独立字段添加到JSON中查询优势在Kibana中可以直接用userId:123查询而传统日志需要用正则匹配用户 1234.5 日志与追踪结合分布式TraceID/* * 【完整代码示例7分布式追踪】 * 需要添加依赖spring-cloud-starter-sleuth */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class OrderController { private static final Logger logger LoggerFactory.getLogger(OrderController.class); GetMapping(/createOrder) public String createOrder() { // Spring Cloud Sleuth自动注入TraceID和SpanID到MDC // MDCMapped Diagnostic Context是SLF4J的线程本地变量映射 // 在日志配置中配置logging.pattern.level%5p [${spring.application.name},%X{traceId},%X{spanId}] // 输出示例2023-05-15... INFO [order-service,5af7801cc2ab4ad4,5af7801cc2ab4ad4] ... 创建订单 logger.info(创建订单开始); // 【第18行】这行日志会自动带上TraceID // 调用其他服务RestTemplate或Feign // 这些调用会自动传递TraceID形成完整的调用链 logger.info(创建订单完成); // 【第22行】同一请求的TraceID相同 return 订单创建成功; } }跨服务调用示例用户请求 → [API Gateway] → [Order Service] → [Inventory Service] → [Payment Service] ↓ ↓ ↓ TraceID:5af78... TraceID:5af78... TraceID:5af78... SpanID:1 SpanID:2 SpanID:3 所有服务的日志都包含相同的TraceID: 5af7801cc2ab4ad4代码与结论的联系结论通过TraceID关联同一请求在不同服务的日志代码体现无侵入式只需添加依赖和配置第18行和第22行的日志会自动包含相同的TraceID排查流程用户反馈订单失败 → 从网关日志获取TraceID → 在ELK中查询该TraceID → 看到所有相关服务的完整调用链日志5. 总结知识体系回顾5.1 核心知识点速查表知识点代码体现配置关联最佳实践获取LoggerLoggerFactory.getLogger(Class)无需配置使用Lombok的Slf4j更简洁日志级别logger.trace/debug/info/warn/error()logging.level.*生产环境用INFO开发用DEBUG占位符log.info(msg {}, var)无需配置始终使用占位符避免字符串拼接日志文件logging.file.name/pathapplication.yml线上必须持久化日志日志分割logging.logback.rollingpolicy.*application.yml按天和大小分割防止磁盘满性能优化if(log.isDebugEnabled())无需配置复杂日志务必加判断敏感信息password - ***无需配置密码、密钥等绝不打印明文分布式追踪自动注入Spring Cloud Sleuth微服务架构必须启用5.2 新手学习路径建议第一步掌握基础2.1节代码能打印不同级别日志第二步理解配置2.5节能控制日志输出和文件存储第三步使用Lombok3.2节简化代码提升效率第四步应用最佳实践4.1-4.2节写出安全、高性能的日志代码第五步了解分布式日志4.3-4.5节为微服务架构做准备5.3 代码 vs 结论如何建立联系方法论看到结论问代码在哪例如结论占位符避免字符串拼接立即想到logger.debug(msg {}, var)这行代码看到代码问结论是什么例如看到logging.file.name配置思考结论日志持久化和这个配置的关系实践验证修改配置logging.level.rootTRACE运行示例3观察TRACE日志是否出现直观感受配置对代码行为的影响附录完整项目结构示例spring-boot-logging-demo/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/demo/ │ │ │ ├── LoggerController.java # 示例1、3代码 │ │ │ ├── LogController.java # 示例4代码 │ │ │ ├── SecurityLogDemo.java # 示例5代码 │ │ │ └── StructuredLogDemo.java # 示例6代码 │ │ └── resources/ │ │ ├── application.yml # 核心配置文件 │ │ └── logback-spring.xml # 高级日志配置可选 │ └── test/ │ └── java/ └── pom.xml # Maven依赖管理application.yml完整配置示例# 应用基本信息 spring: application: name: logging-demo # 日志配置 logging: # 级别配置 level: root: INFO com.example.demo: DEBUG # 本项目的包设为DEBUG # 文件配置 file: name: logs/${spring.application.name}.log # 日志文件名 # Logback滚动策略 logback: rollingpolicy: max-file-size: 10MB file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz # 压缩旧日志 # 控制台格式开发环境 pattern: console: %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n # 文件格式生产环境 pattern: file: %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n新手建议从最简单的LoggerController开始逐步添加配置观察日志输出变化再学习Lombok简化最后研究高级特性。每个知识点都动手写代码验证理解会更深刻