
博客主页小谢同学的小破站✍️本文由小谢同学的小破站原创首发于 CSDN ☕JavaSE专栏JavaSEJavaEE初阶专栏JavaEE初阶JavaEE进阶专栏JavaEE进阶数据结构专栏数据结构⚙️算法专栏算法MySQL初阶专栏MySQL初阶MySQL进阶专栏MySQL进阶计算机网络专栏计算机网络C语言专栏C语言欢迎点赞 收藏⭐ 留言发现错误欢迎指正✨脚踏实地持续深耕奔赴自己的目标✨----- 分割线 -------[JavaEE进阶]Spring Boot日志1. 日志到底解决什么问题1.1 System.out.print 的四个原罪1.2 日志的三份正经工作2. 在 Spring Boot 里打印第一条日志2.1 先拿到日志对象2.2 用日志对象输出的效果2.3 一条日志长什么样六件套3. 日志级别给消息分级上报3.1 六个级别3.2 默认只打印 info 及以上4. 日志框架与门面模式4.1 SLF4J 是什么4.2 门面模式一家奶茶店就讲明白了4.3 没有门面的日子有多惨5. 日志配置级别、持久化、分割、格式5.1 改日志级别5.2 日志持久化5.3 日志文件分割5.4 自定义日志格式6. 更省事的写法Slf4j7. 总结与速查表前言这里是小谢同学的 Spring Boot 日志学习记录整理日志的基本用法、级别、门面模式、配置与持久化。笔记用于自我复盘巩固有错误欢迎大家指出专栏还有 Java、网络、C 语言等系列笔记欢迎翻阅。同时也希望这篇文章能够帮助到你~1. 日志到底解决什么问题1.1 System.out.print 的四个原罪日志其实我们一点都并不陌生,从 JavaSE 开始我们就在用System.out.print打印东西了,就好比我们在写算法题目的时候,有时候我们并不好观察,我们就可以使用IDEA的sout进行打印出来看看对不对?是不是符合我们的预期?但是如果项目开始复杂起来了呢?System.out.print就明显不够用了。举个一个例子之前我写一个简单的借书接口所有信息全靠 println在测试的时候出现了问题控制台里密密麻麻的文字我根本分不清哪一行是几点几秒打印的、是哪个类的、是哪一次请求的。就像我们使用Spring Boot框架一样,Spring Boot 本身给我们设置好了一些日志的功能,我们在启动的时候能够观察到时间,位置,进程ID,等等信息:四条硬伤总结一下对比项System.out.print专业日志框架时间信息没有只能靠自己手写默认带精确到毫秒的时间日志级别没有所有输出一样重有 ERROR/WARN/INFO/DEBUG 等级别来源信息没有不知道哪个类打的自动带类名、线程名、进程号开关控制只能删代码改一行重新打包改个配置项就能压掉某些日志持久化只能重定向格式乱七八糟一行配置写文件还能自动切分性能同步阻塞量大时拖慢接口支持异步量大了也不至于卡死所以说白了,“专业的人干专业的事情”,此时我们就需要一个专门的日志框架;↩ 返回目录1.2 日志的三份正经工作除了最常见的排查 bug日志其实还在干三件大事① 系统监控一个成熟的系统基本上都离不开监控;把每一个接口的响应时间,返回状态记录到日志中,然后根据统计分析设计阈值,当超过了阈值就会报警;比如统计日志中库存不足这个关键字出现了多少次一分钟超过 100 次就发告警这就是很常见的需求。也可以换一个说法: 一天晚上,公司的服务器出现异常,假设出现的问题只有以下这几种;第一种:可能出现的情况: P3低优先级、日志统计、非核心小报错通知纯邮箱、日报汇总不弹任何强提醒定期复盘第二种:P2预警、慢查询、磁盘慢慢要满、证书快到期)通知群消息 邮箱不用半夜打扰上班之后排查就行第三种:P1高危但业务没全崩错误率飙升、内存打满、单实例宕机)通知短信 钉钉 / 企微机器人不用电话吵醒看到消息尽快处理适合晚上非致命故障第四种:P0核心炸了最高级支付、下单、首页挂了大量用户受影响通知电话值班 on-call 工程师 短信 群告警先打值班开发 / 运维不是直接打领导工程师处理超时没确认才升级给组长、领导打电话目标立刻接电话5~10 分钟响应止损② 数据采集采集来的数据能用在很多地方数据统计页面的浏览量PV、访客量UV、点击量用来看运营效果推荐排序把用户浏览过什么、停留多久记进日志算法同学拿去训练模型做推荐。购物、广告、新闻这些领域基本都在这么干。③ 日志审计这个在正经公司里特别重要。运营在后台删了一条数据、改了一条数据如果没有操作日志你根本不知道是谁干的记了日志一查就清楚。还有些内部违规、信息泄漏的情况比如客户信息被偷偷导出去卖没有日志留存事后根本没法追查。所以很多公司连频繁查询客户信息都会记录并报警。就好比生活中的例子:别人污蔑你进去他房间偷东西,但是没有摄像头,刚好你又进去过,东西又丢失了,没有任何的证明来证明自己没有偷就像数据库设计中,一般使用的都是逻辑删除,而不是正真意义上的删除,目的就是不要去麻烦人家,欠别人的人情,即使自己不小心删错了,也能进行找回来,不会造成很大的损失一句话日志不只是给开发看的它还是运维的眼睛、算法的粮食、合规的证据。↩ 返回目录2. 在 Spring Boot 里打印第一条日志2.1 先拿到日志对象Spring Boot 项目一启动控制台就自带一堆日志这些可不是System.out打的是内置的日志框架干的活。Spring Boot 内置的是SLF4J Logback我们直接调 SLF4J 就能输出日志。打印日志就两步拿到日志对象 → 用它输出内容。拿对象要用日志工厂LoggerFactory注意是工厂不是直接 newprivatestaticfinalLoggerloggerLoggerFactory.getLogger(TestController.class);getLogger要传一个参数用来标记这个日志是谁打的。一般就传当前类的class这样出问题时看日志里的类名就能直接定位到代码非常直观。⚠️ 踩坑提醒Logger有几个同名类一定要导org.slf4j.Logger别导成org.apache.logging.log4j.Logger或者java.util.logging.Logger不然要么编译报错要么日志打不出来。↩ 返回目录2.2 用日志对象输出的效果我简单写一个借阅的接口,例如:RestControllerpublicclassTestController{privatestaticfinalLoggerloggerLoggerFactory.getLogger(TestController.class);RequestMapping(/borrow)publicStringborrow(){logger.info(用户借阅《Java 编程思想》编号 B1024);System.out.println(用户借阅《Java 编程思想》编号 B1024);return借阅成功;}}我们可以简单来看看日志情况:访使用postman或者浏览器访问一下http://localhost:8080/book/borrow控制台输出的长这样对比一下System.out打出来的那行干巴巴的文字信息量差得不是一点半点。↩ 返回目录2.3 一条日志长什么样六件套上面那行其实是六块内容拼起来的从左往右看序号元素示例说明1时间日期2026-10-21T17:28:08.04008:00精确到毫秒排查偶发问题时很关键2日志级别INFOERROR、WARN、INFO、DEBUG、TRACE3进程 ID18632多实例部署时用来区分是哪个进程4线程名[nio-8080-exec-1]能看出是哪个线程处理的并发问题全靠它5Logger 名c.e.b.c.BookController通常就是类名超长会缩写6日志内容用户借阅…我们自己写的那句话看到第 5 项没这就是为什么getLogger里要传当前类的 class否则这里只会显示一个你自己起的名字定位起来全靠猜。↩ 返回目录3. 日志级别给消息分级上报打个比方。假设你是一家两万人公司的老板如果每个员工的鸡毛蒜皮都直接汇报给你你早就干不下去了。于是有了组织架构组员报组长组长报研发一组研发一组报 Java 研发……层层上报重要的才往上走。日志级别就是这个道理。给消息分好级出问题时只看 ERROR日常看 INFO就不用在一堆输出里翻半天了。3.1 六个级别日志从高到低分一共6个日志等级Spring Boot 用的 Logback 里没有 FATAL它被并到了 ERROR级别含义借书场景里的例子FATAL致命系统级错误必须立刻处理数据库连不上了整个服务不可用ERROR错误但仍不影响系统继续运行扣库存失败这一单借阅失败返回 500WARN警告不影响使用但值得注意用户积分不够跳过积分抵扣INFO普通信息记录正常运行轨迹借阅成功、项目启动完成DEBUG调试信息开发期用进入 borrow 方法bookId1024TRACE追踪比 DEBUG 更细循环里每一轮迭代的中间值注意: Bug 级别没有半毛钱关系我们需要知道的是,日志级别是自己开发设置的,给开发人员自己看的;Bug 级别是测试根据现象和影响范围定的。还有个口诀级别越高能收到的消息越少。↩ 返回目录3.2 默认只打印 info 及以上Logger 对每个级别都有对应的方法我一次性全打一遍RequestMapping(/levels)publicStringlevels(){logger.trace( trace );logger.debug( debug );logger.info( info );logger.warn( warn );logger.error( error );return打印不同级别的日志;}使用postman访问: http://127.0.0.1:8080/levels结果只出来三行这下面两行根本就没有打印出来:logger.trace( trace );logger.debug( debug );踩坑记录第一次见这个现象我还以为是trace和debug方法没生效。其实 Spring Boot 默认日志级别就是info只有大于等于当前级别的日志才会输出所以 info、warn、error 出来了debug 和 trace 被过滤掉了。想让它们出现改配置就行下一节说: 5.1 改日志级别。 再补一个小技巧SLF4J 支持{}占位符不用自己拼字符串性能也更好logger.info(用户 {} 借阅了《{}》编号 {},userId,bookName,bookNo);↩ 返回目录4. 日志框架与门面模式4.1 SLF4J 是什么市面上日志框架一大堆Log4j、Logback、Log4j2、JUL……那为什么我们代码里只导org.slf4j的包因为 SLF4J 本身不是一个具体的日志实现它只是一层抽象一套规范、一套接口。它不能单独干活必须搭配 Logback 这类真正的实现来用。这就引出它背后的设计思想——门面模式。↩ 返回目录4.2 门面模式一家奶茶店就讲明白了门面模式Facade Pattern也叫外观模式的定义是为子系统中的一组接口提供一个统一的入口让子系统更容易使用。生活中的例子也比较多,例如去医院挂号,门诊,化验,取药,等流程比较繁琐,患者需要自己跑一圈,这可比较累了;但是如果有一个导诊台帮你把这些事情穿起来,你只需要跟导诊台打交道就行;我用奶茶店来写这段代码——顾客只跟收银台说话后面的泡茶、加料、封口各干各的publicclassFacadeDemo{publicstaticvoidmain(String[]args){MilkTeaShopshopnewMilkTeaShop();shop.order(珍珠奶茶);}}/** * 奶茶店门店门面角色对外的统一入口 */classMilkTeaShop{privateTeaMakerteaMakernewTeaMaker();privateToppingAddertoppingAddernewToppingAdder();privateCupSealercupSealernewCupSealer();publicvoidorder(Stringname){System.out.println(收到订单name);teaMaker.brew();toppingAdder.add();cupSealer.seal();System.out.println(出餐请取餐);}}classTeaMaker{publicvoidbrew(){System.out.println(① 泡茶底焖 8 分钟);}}classToppingAdder{publicvoidadd(){System.out.println(② 加珍珠两勺);}}classCupSealer{publicvoidseal(){System.out.println(③ 封口贴标);}}顾客客户端压根不知道后面有几个工位、谁先谁后他只知道点单这一个动作。这就是门面的价值优点说明降低耦合客户端只依赖门面子系统内部怎么改都不影响调用方使用更简单客户端不用关心执行顺序和内部细节安全性更好门面里不开放的方法外面就调不到对于顾客来说: 我不关心你后面有几道工序,我只关系我需要一杯奶茶~门面模式里面之一两个角色:外观角色Facade对外的统一接口上面的MilkTeaShop和子系统角色SubSystem可以有很多个而且子系统本身并不知道门面的存在对它来说门面也只是个普通调用方。用我们现在的话来说就是: 我要去买奶茶,我付钱,你给我奶茶~↩ 返回目录4.3 没有门面的日子有多惨把奶茶换成日志框架就懂了。假设你的项目用的是 Log4j结果依赖的一个第三方组件比如某个消息中间件用的是 Logback那你就得把 Logback 也引进来。麻烦随之而来两套框架的 API 和配置文件都不一样你得维护两份配置哪天想换框架业务代码里的日志调用全得改改的过程还容易冲突依赖一多可能同时存在三四套日志配置越堆越乱。引入门面模式之后就清爽了: 应用程序只需要面向SLF4J这一套的接口代码,地城换成Logback,Log4j2都行.,业务层代码一行都不需要动,同理配置文件也只有一份;所以那句话说得挺对SLF4J 让你的代码独立于任何特定的日志 API。↩ 返回目录5. 日志配置级别、持久化、分割、格式Spring Boot 的日志配置全写在application.properties或application.yml里两个文件配一个就行。两者可以互换规律很简单properties 里的点.在 yml 里就是换行。5.1 改日志级别修改日志的级别,我们只需要配置相关内容:logging.level例如:logging.level.roottrace或者logging:level:root:traceroot表示根 logger也就是全局生效。改完重启刚才那五条日志就全出来了——debug 和 trace 都在。实际开发里一般不用全局开 debug日志会爆炸通常是给某个包单独开比如只盯自己写的 Controller但是当你修改完默认的日志级别之后,你去启动项目,你会发现,下面的日志滚动的非常快~↩ 返回目录5.2 日志持久化控制台日志一关窗口就没了线上出了问题想回溯根本没戏。把日志存到文件里就叫持久化。有两种配法。方式一指定文件名logging.file.namelog/book.log或者:logging:file:name:log/book.log路径可以写相对路径也可以写绝对路径比如D:/logs/book.log。配完跑一下项目根目录下就多出log/book.log内容就是控制台打印的那些。方式二只指定目录logging.file.pathD:/logs或者:logging:file:path:D:/logs这种方式只能指定目录文件名是固定的spring.log不能自己起名。⚠️ 踩坑记录两个都配了会怎样只生效一个以logging.file.name为准。我第一次配的时候两个都写了还纳闷为什么文件名不是我想要的那个后来才发现是优先级问题。所以别贪心选一个用。↩ 返回目录5.3 日志文件分割所有日志都堆在一个文件里跑上几天就是几个 G打开都能卡死编辑器的。日志框架其实早就想到了默认超过10MB就自动切分。想自己定规则配这两个项配置项说明默认值logging.logback.rollingpolicy.file-name-pattern分割后的文件名格式${LOG_FILE}.%d{yyyy-MM-dd}.%i.gzlogging.logback.rollingpolicy.max-file-size单个文件超过多大就切10MB为了看得清楚我把阈值调成 1KB 做个演示logging.logback.rollingpolicy.file-name-pattern${LOG_FILE}.%d{yyyy-MM-dd}.%i logging.logback.rollingpolicy.max-file-size1KB或者:logging:logback:rollingpolicy:max-file-size:1KBfile-name-pattern:${LOG_FILE}.%d{yyyy-MM-dd}.%i多点几次接口多打些日志目录里就出现了一堆文件命名规律就是日志名.日期.索引。1KB 只是为了演示企业里一般设 200MB 或 500MB这个没有硬性标准看磁盘和日志量自己定。↩ 返回目录5.4 自定义日志格式默认格式看习惯了其实挺好但有时候需要精简或者想把自己的方法名、行号加上去。控制台和文件可以分开配配置项作用logging.pattern.console控制台日志格式logging.pattern.file日志文件格式常用占位符先收好占位符含义%d{yyyy-MM-dd HH:mm:ss.SSS}日期时间精确到毫秒%5p日志级别不足 5 位右边补空格%t/%thread线程名%c类的全限定名%M方法名%L行号%m/%msg日志内容%n换行%clr(表达式){颜色}给日志上色支持 blue、cyan、faint、green、magenta、red、yellow举个精简版配置时间 类名 方法 行号 线程 内容logging.pattern.console%d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n或者:logging:pattern:console:%d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%nfile:%d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n这样看起来就清爽多了:格式里那几个数字规则也顺手记一下%5是不够 5 位时右边补空格%-5是左边补空格%.15是超过 15 位就截断%15.15是两个都干。⚠️ 踩坑记录想用%clr上色结果控制台一点颜色都没有不是配置写错了是 IDEA 默认不给控制台 ANSI 颜色。要在启动配置的 VM options 里加一句-Dspring.output.ansi.enabledALWAYS重启就有了。不过说实话实际开发里默认格式就够用除非有特殊要求不太建议折腾。只不过我这是专业版,自动给日志渲染上了颜色~↩ 返回目录6. 更省事的写法Slf4j每个类都写一遍LoggerFactory.getLogger(xxx.class)说实话挺烦的尤其是 Controller 一多粘贴都粘到手软。Lombok 给了个偷懒方案。第一步加依赖Spring Boot 项目一般创建时就自带了dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency第二步类上打Slf4j然后直接用logimportlombok.extern.slf4j.Slf4j;importorg.springframework.web.bind.annotation.RestController;Slf4jRestControllerRequestMapping(/book)publicclassLogController{publicvoidlog(){log.info(用户借阅《Java 编程思想》编号 B1024);}}就这么简单log这个对象是注解帮我们生成的不用自己声明。⚠️ 踩坑提醒如果 IDEA 里log报红基本都是 Lombok 没配好——要么没开注解处理器Enable annotation processing要么插件没装。不是代码写错了。再给大叫推荐几个工具:这几个工具还是非常的好用的,具体用法这里就不过多解释了;我们就能打开对一些配置文件进行快速添加:↩ 返回目录7. 总结与速查表这一节内容不多但都是天天要用的东西。三句话概括日志是项目的基础设施能帮我们发现问题、分析问题、定位问题还能做监控、数据采集和审计Spring Boot 内置 SLF4J Logback默认以 info 级别输出到控制台用Slf4jlog就能快速打日志。日志有 6 个级别FATAL/ERROR/WARN/INFO/DEBUG/TRACE级别越高收到的消息越少通过配置文件名或目录可以把日志持久化到磁盘。想改行为就改配置级别、存哪儿、怎么切、长什么样四个维度全都能配。其实一些配置文件我们不需去记: 我们可以提前存储起来,需要使用的时候能想起来,直接使用复制粘贴的方式~配置速查表需求配置项备注改全局日志级别logging.level.rootdebug也可给指定包单独设指定日志文件名logging.file.namelog/book.log支持绝对/相对路径只指定日志目录logging.file.pathD:/logs文件名固定为spring.log两者同时配—以logging.file.name为准切分阈值logging.logback.rollingpolicy.max-file-size默认 10MB切分文件名logging.logback.rollingpolicy.file-name-pattern默认含日期和索引控制台格式logging.pattern.console占位符见上表文件格式logging.pattern.file与控制台可分开配控制台颜色VM options 加-Dspring.output.ansi.enabledALWAYSIDEA 需要易错点速记坑正解Logger 导错包必须是org.slf4j.Loggerdebug 日志不打印默认级别是 info配置里放开两个持久化配置都写了只有logging.file.name生效只配 path 想改文件名不行是固定的spring.log颜色配置不生效IDEA 要开 ANSI 输出用字符串拼接打日志改用{}占位符好了日志这块就先整理到这里。下一篇继续 Spring Boot 的其他内容感兴趣的可以蹲一下如果这篇笔记对你有帮助欢迎点赞 收藏 ⭐ 留言 有写错的地方也请帮忙指正一起进步↩ 返回目录