尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

logback配置全解析:滚动策略、异步与生产实践

logback配置全解析:滚动策略、异步与生产实践 我先把话说在前面做Java后端这几年日志配置是我见过被低估最严重的一环。很多项目代码写得规规矩矩一上线排查问题就抓瞎最后十有八九是logback配置没搞明白——要么日志文件不滚动、磁盘被打满要么关键请求的日志根本没打出来要么所有日志糊成一团、想查一次接口调用链得用grep翻半天。这篇文章就把logback配置信息这块彻底拆开讲清楚从文件结构、核心标签、滚动策略到生产环境的实战配置最后再附上我踩过的坑。不管你是刚入门的新手还是写了好几年Java的老手只要你还在用Spring Boot或者纯Java工程这份配置经验都能直接抄作业。1. 为什么logback配置值得花半小时研究1.1 logback在Java日志体系里的位置先搞清楚logback是什么。它是Java生态里一个老牌日志框架作者是Ceki Gülcü这哥们儿之前还写了log4j 1.x后来觉得log4j 1.x的架构不够灵活干脆另起炉灶写了logback。logback天然实现了SLF4J的日志门面接口而SLF4J又是目前Java项目里最主流的日志抽象层所以Spring Boot默认就把logback作为日志实现你用spring-boot-starter-web的时候里面已经自带了logback依赖不需要额外引入。接下来说说它和现在常见的几个日志框架的关系。log4j2是Apache基金会的产品性能上用了无锁异步之后确实很猛但它需要额外引入依赖而且配置格式和logback不完全兼容。logback的优势在于Spring Boot默认集成、文档详细、配置文件语法直观对于绝大多数业务系统来说性能完全够用。所以只要你的项目不是那种每秒几十万条日志的极端场景logback是最省事的选择。logback官网上有完整文档如果你要查一些冷门标签的用法直接去看官方manual比我在这里说的更全。这套配置的学习成本其实不高核心就一句话日志框架解决的问题是把日志事件从代码里捕获出来经过格式化写到它该去的地方。你要做的就是通过配置文件告诉它三个事哪些日志要记录、以什么格式记录、记录到哪里。1.2 配置没写好的典型症状我在帮别人排查问题的时候见过太多典型的配置事故现场。第一类是日志全堆在一个文件里而且永远不切分。有个同事的项目线上跑了两三个月一个app.log膨胀到了二十多个GB服务器磁盘直接告警。他用vim打开文件想看日志结果编辑器直接卡死最后只能写脚本按行切割场面极其狼狈。这就是没配滚动策略的后果。第二类是日志级别混乱。有些团队为了省事把root logger的级别调成DEBUG结果线上每秒钟产生几百MB的日志业务日志和框架日志混在一起真正想查的订单号反而被淹没。等到想改回INFO级别又发现某些模块的DEBUG日志其实是有用的删了可惜——最后只能在那改配置、重启、观察来回折腾。第三类是日志格式缺失关键信息。比如没有线程名没有logger名称没有耗时出了性能问题想定位是哪个方法慢翻日志根本对不上。更头疼的是没有traceId或者requestId一个请求经过多个服务你根本没法把日志串起来看。这些问题都不是代码问题纯粹是logback配置信息没整明白。花半小时把配置理顺后面省下来的排查时间可能是几天。2. logback配置文件的骨架logger、appender、layout三者怎么配合2.1 配置文件的查找顺序与命名陷阱logback启动的时候会按照一套固定顺序去找配置文件。这个顺序在官方文档里写得很清楚但我发现很多人不清楚导致配置改了却没生效。查找顺序是这样的检查系统属性logback.configurationFile如果通过-Dlogback.configurationFile/path/to/config.xml指定了路径就直接用这个不再往下找。在classpath里找logback-test.xml。这个文件是给测试环境用的优先级比正式配置高所以如果你在test/resources下放了一个logback-test.xml它会把你要用的配置顶掉。我见过一个项目就是测试环境日志正常一到生产环境日志格式全变了最后发现是测试配置文件classpath打进了生产包。在classpath里找logback.xml。如果上面都没有logback会用自带的BasicConfigurator简单输出到控制台级别是DEBUG。这里有个Spring Boot的特殊情况。Spring Boot还支持logback-spring.xml这个文件由Spring Boot自己解析而不是logback直接加载。它的好处是能用springProfile、springProperty这两个Spring标签实现多环境配置切换。但是要注意如果你在logback.xml里写了springProfile标签logback解析的时候会报错因为这不是logback原生认识的标签。所以Spring Boot项目我建议直接用logback-spring.xml两边都兼顾。另外logback.configurationFile系统属性的优先级最高这个既是便利也是坑。之前有个线上问题运维同学为了调试在启动脚本里加了这个参数指向一个旧配置文件结果开发改了classpath里的配置完全不起作用查了一下午才定位到。如果你发现配置文件怎么改都没反应第一时间去查启动命令或者环境变量里有没有这个属性。2.2 logger的树形结构与继承机制理解logback的配置最关键的是理解logger的组织方式。它和Java的包名结构一样是一个树形结构。根节点叫root logger下面可以挂各种子logger子logger的name通常就是包名或者类名。举个例子com.example.order.service.OrderService这个类它的logger name可以是完整的类名也可以是包名com.example.order.service。当你在配置里写logger namecom.example.order.service levelDEBUG/那么com.example.order.service包下所有类的日志都按DEBUG级别处理包括它下面的子包。这里有个核心机制叫继承。如果一个logger自己没有设置级别它会往上找最近的设置了级别的父logger一直找到root。root的默认级别是DEBUG如果root也没设置那就用默认的DEBUG。这个继承机制对很多新人来说是个坑最常见的现象是你给某个包设置了INFO级别结果它下面有个子包还是输出了DEBUG日志因为那个子包可能自己配置了级别或者additivity导致了appender重复输出。再说additivity属性。它控制的是当前logger的日志事件要不要继续向上传递。默认是true也就是说com.example.order.service下的日志既会输出到它自己绑定的appender也会往上传给root由root绑定的appender再输出一次。如果你不想让子logger的日志被root再打印一遍就要设置additivityfalse。这个属性在日志拆分场景下特别有用后面我会给一个实际例子。2.3 appender与encoder的关系appender可以理解成一个日志的输出管道。logback内置了很多种appender常用的有ConsoleAppender输出到控制台本地开发调试最常用。FileAppender固定输出到一个文件不滚动。RollingFileAppender根据滚动策略切分文件生产环境最常用。AsyncAppender异步输出先把日志写进队列再由后台线程刷到真正的appender。SocketAppender、SMTPAppender等把日志发到远程或发邮件用的场景相对少。appender本身不负责格式化日志格式化是encoder干的事。从logback 1.x开始PatternLayoutEncoder是标配你可以在里面配pattern控制日志的格式。有的老教程会让你用layout标签那个是老API新项目不要用。一个实用的配置习惯是给每个appender起一个表意明确的名字比如控制台叫CONSOLE文件叫FILE异步文件叫ASYNC_FILE。名字没有硬性规定但团队协作时别人看到配置能立马知道日志流向了哪里。2.4 pattern日志格式的实操要点日志格式是配置里最容易被忽略、又最影响排查效率的部分。推荐一套我一直在用的patternpattern%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n/pattern拆开解释一下%d{yyyy-MM-dd HH:mm:ss.SSS}输出时间精确到毫秒。%-5level日志级别左对齐固定占5个字符这样日志里级别列是整齐的。[%thread]线程名排查并发问题必备。%logger{36}输出logger名称最长36个字符超过会从左边缩写这个是为了让长包名不会把日志撑得很难看。-是分隔符。%msg%n日志消息加换行。如果你要排查性能可以加上%method和%line分别输出方法名和行号。但要提醒一句这两个东西是通过生成调用堆栈来获取的在高并发场景下开销比较大生产环境尽量别加。真的需要定位代码位置时可以临时在某个环境改配置观察不要全局开。另外有个实用技巧是给控制台格式加上颜色方便本地调试pattern%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n/pattern%highlight和%cyan都是控制台专用的着色指令日志文件里不要用文件里带颜色转义字符反而会增加解析难度。还有一个格式技巧就是通过MDC输出请求ID。这个在分布式系统里非常关键你可以在代码里往MDC里塞一个traceId然后在pattern里用%X{traceId}输出。比如pattern%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} traceId%X{traceId} - %msg%n/pattern这样每行日志都有traceId排查一次请求的完整链路只需要用traceId过滤就行。后面我会专门演示怎么把MDC集成到配置和代码里。3. 一套能上生产的logback配置从这里抄3.1 基础款控制台加文件先给一个最小可用的配置模版这个模版既能在本地开发时看控制台输出又能在生产环境把日志落到文件?xml version1.0 encodingUTF-8? configuration debugfalse property nameLOG_HOME value/data/logs/order-service/ property nameLOG_PATTERN value%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n/ appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern${LOG_PATTERN}/pattern charsetUTF-8/charset /encoder /appender appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender file${LOG_HOME}/order-service.log/file encoder pattern${LOG_PATTERN}/pattern charsetUTF-8/charset /encoder rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePattern${LOG_HOME}/order-service.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy /appender root levelINFO appender-ref refCONSOLE/ appender-ref refFILE/ /root /configuration这里有几处值得展开说明。LOG_HOME和LOG_PATTERN用property标签定义为变量后面通过${}引用好处是修改路径或者格式只需要改一处不用每个appender里都翻一遍。LOG_HOME路径根据自己项目的部署方式定注意确保这个目录存在且应用有写权限。charset设置为UTF-8也很关键尤其当你的日志里包含中文时不指定编码很容易在Windows和Linux环境之间切换后出现乱码。如果你是在Linux服务器上直接用tail -f看日志UTF-8基本是标配。maxHistory表示保留最近30天的历史日志文件超过的会被自动清理。这里的时间单位取决于fileNamePattern里的%d{yyyy-MM-dd}如果只精确到天就按天计算历史如果精确到小时就按小时计算。很多人在这个单位上吃过亏以为maxHistory30就是保留30个文件实际上是保留30个时间单位你按天配就是30天。3.2 滚动策略不只看时间还要看大小上面那个配置用的是TimeBasedRollingPolicy也就是纯按时间滚动。但生产环境里还有一个更常见的需求某个服务某个时段日志量特别大一天一个文件还是太大不方便按天切割归档这时候就需要同时按大小和时间来滚动。logback提供了SizeAndTimeBasedRollingPolicy可以同时根据时间周期和文件大小两个维度触发滚动。appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender file${LOG_HOME}/order-service.log/file encoder pattern${LOG_PATTERN}/pattern charsetUTF-8/charset /encoder rollingPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy fileNamePattern${LOG_HOME}/order-service.%d{yyyy-MM-dd}.%i.log/fileNamePattern maxFileSize200MB/maxFileSize maxHistory30/maxHistory totalSizeCap20GB/totalSizeCap /rollingPolicy /appender注意这里fileNamePattern里多了一个%i它是规模序号的占位符。当一个文件达到maxFileSize上限时会生成一个新的文件序号从0、1、2递增比如order-service.2024-01-01.0.log、order-service.2024-01-01.1.log。声明的顺序要看仔细了不加%i配合maxFileSize是可以的但实际上意义不大因为没有序号占位滚动时文件名会冲突或者覆盖。totalSizeCap是一个容易出错的地方。它表示所有归档日志文件的总大小上限超过时会删除最旧的归档文件。注意maxHistory和totalSizeCap这两个限制是同时生效的谁先触发了都会触发清理。如果你只配maxHistory而忘了totalSizeCap那么日志量大时磁盘依然可能被打满——因为30个文件每个都是200MB加起来6个GB这在某些小磁盘机器上已经是灾难了。反过来只配totalSizeCap而不配maxHistory也可能出现文件很快被删除你想查三个月前的日志却查不到的情况。所以我现在的习惯是两者都配互为兜底。3.3 异步Appender提升吞吐量同时别丢日志日志输出本质上是IO操作如果每次请求都在同步写文件高并发下性能损耗会非常明显。logback的解法是AsyncAppender原理很简单业务线程把日志事件塞进一个阻塞队列立刻返回后台单独有一个工作线程从队列里取数据再写入真正的appender。这个模式把同步IO的耗时从业务线程里剥离开在压测场景下提升非常明显。一个标准的异步配置要两层套appender nameASYNC_FILE classch.qos.logback.classic.AsyncAppender queueSize8192/queueSize discardingThreshold0/discardingThreshold neverBlocktrue/neverBlock appender-ref refFILE/ /appender解释一下AsyncAppender的几个关键参数queueSize队列容量默认是256。在高并发场景下队列太小会导致大量日志丢失我通常调到8192。注意队列里存的是日志事件对象不是字符串所以容量大小和内存占用直接相关要根据业务量调整。discardingThreshold当队列剩余容量低于这个百分比时异步appender会直接丢弃级别较低的日志DEBUG、INFO、TRACE来腾出空间给更重要的WARN、ERROR。默认值是20也就是说队列快满时INFO日志可能悄悄丢了。这其实是有意为之的设计但对很多业务来说莫名其妙的日志丢失比日志丢弃更让人恼火。如果你不能接受INFO日志丢失把这个值设为0表示队列满时一视同仁等级低的也可以被丢弃避免重要日志被挤掉。neverBlock默认false意思是队列满时日志线程会阻塞等待队列有空位。把它设为true之后队列满时日志事件直接丢弃不会阻塞业务线程。怎么取舍我的建议是如果你更关心业务延迟选true即使丢日志也不要让业务线程卡在日志上如果你更关心日志完整性那就设false让业务线程承担短暂的等待也绝不丢日志。这个权衡没有绝对答案得看业务场景。还有两个容易被忽略的参数。includeCallerData默认是false如果你要日志里输出方法名和行号异步模式下需要额外生成调用栈信息开销大默认关闭是对的。maxFlushTime是队列刷出日志的最大等待时间配合AsyncAppender关闭时尽量把队列里的日志写完。如果你在用Spring Boot优雅停机建议把maxFlushTime调大一点否则进程退出时队列里残留的日志可能来不及落盘。3.4 Spring Boot多环境配置与logback-spring.xmlSpring Boot环境下我强烈建议直接用logback-spring.xml。它相比logback.xml多了两个Spring生态专用的标签springProfile和springProperty。这两个标签能让你一套配置文件同时适配开发、测试、生产多个环境不用每个环境一套配置文件。举个例子开发环境日志要输出到控制台、级别可以放到DEBUG生产环境只输出WARN和ERROR到文件不同环境用不同配置springProfile namedev root levelDEBUG appender-ref refCONSOLE/ /root /springProfile springProfile nameprod root levelINFO appender-ref refASYNC_FILE/ /root /springProfile这个写法的好处很明显代码库里只需要维护一份日志配置环境差异集中在springProfile里。springProfile的name可以写多个比如namedev,test表示两个环境共用同一段配置也可以写取反表达式如name!prod表示非生产环境用。springProperty的作用是把application.properties或application.yml里的配置值引入到logback配置中。常见的用途是写入日志路径或者应用名springProperty scopecontext nameappName sourcespring.application.name defaultValueunknown/ property nameLOG_HOME value/data/logs/${appName}/这样每个服务只要在配置里维护好自己的spring.application.name日志目录就自动跟着应用名走部署脚本里不用再单独传一个LOG_HOME。我维护过多套微服务的项目这个技巧省了非常多部署排错的精力。3.5 Filter过滤、MDC与日志脱敏的实战组合有时候你不需要一个包下的所有日志都输出只想输出某个级别的日志这时候要用filter。logback内置的LevelFilter和ThresholdFilter是最好用的两个。LevelFilter是精确匹配只输出指定级别。比如只输出ERROR日志appender nameERROR_FILE classch.qos.logback.core.rolling.RollingFileAppender file${LOG_HOME}/error.log/file encoder pattern${LOG_PATTERN}/pattern /encoder filter classch.qos.logback.classic.filter.LevelFilter levelERROR/level onMatchACCEPT/onMatch onMismatchDENY/onMismatch /filter /appenderonMatch和onMismatch是过滤器处理动作ACCEPT表示接受这条日志DENY表示拒绝。如果你只想把ERROR日志单独拎出来放一个文件排查故障这个配置非常实用。另一个思路是按包名拆分日志。比如你某天需要重点观察某个模块的日志可以单独给那个模块建一个appender并设置additivityfalse避免重复输出logger namecom.example.order.service levelDEBUG additivityfalse appender-ref refORDER_FILE/ /logger这样com.example.order.service包下的日志只会输出到ORDER_FILE不会再到root的appender里既减少了日志量也方便按业务模块追踪。再来说日志脱敏。这个需求现在很常见尤其是涉及手机号、身份证号、银行卡号等敏感信息的系统。logback没有内置一个专门的脱敏标签但pattern里有一个%replace转换器可以用正则做简单的脱敏。比如你日志里的消息是用户手机号:13812345678想把它变成138****5678pattern%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %replace(%msg){(\d{3})\d{4}(\d{4}), $1****$2}%n/pattern这个方案的问题在于它只能对满足固定正则的文本做替换如果日志里还有身份证号、地址等其他敏感字段你就得维护一个很长的正则列表可读性和维护性都不好。更重的方案是自定义一个Layout类在输出前统一做敏感信息脱敏这个工作量就大一些。作为一个中间方案%replace能应付大多数简单的隐藏需求先能用起来再说。MDCMapped Diagnostic Context是logback里特别实用的一个功能。它本质上是一个线程粒度的Map你在代码里put进去的值可以在日志pattern里用%X{key}输出。用一个实际例子说明MDC.put(traceId, UUID.randomUUID().toString()); log.info(开始创建订单); MDC.remove(traceId);配合日志pattern里的%X{traceId}每行日志都会带上这个traceId。如果整个请求处理流程都在同一个线程内你只需要在入口处put一次就能在这条链路上把所有日志串起来。如果涉及线程池、异步调用traceId的传递需要手动处理常见做法是自定义一个Runnable包装器在提交任务时把父线程的MDC拷贝到子线程。这一步很多项目不做导致异步场景日志串不起来排查问题非常痛苦。4. 配置踩坑实录排查思路与修复方案4.1 配置不生效先查这三处如果你改了配置启动后日志还是老样子不要急着怀疑框架有bug按顺序排查这三个地方第一确认你改的文件是不是被加载的那个。当前classpath下如果同时存在logback-test.xml和logback.xml前者会生效你改后者肯定是白发。如果项目是Spring Boot还要确认是不是被logback-spring.xml覆盖了。第二查看系统属性logback.configurationFile。很多部署脚本或IDE的启动配置里会加上这个参数指向外部配置文件一旦指定了classpath里的配置文件就完全失效。用jinfo pid | grep logback或者检查启动脚本都能确认。第三在项目最初启动阶段logback会把自身的配置和状态信息打印到控制台。如果你在configuration标签上设置了debugtrue它会把解析配置的详细过程输出包括哪个文件被加载、哪些标签被忽略、哪些警告被触发。打开这个开关是最快的排错方式configuration debugtrue还有一个排查姿势更直接——在代码里主动打印logback的内部状态LoggerContext lc (LoggerContext) LoggerFactory.getILoggerFactory(); StatusPrinter.print(lc);这段代码会输出当前实际生效的logger级别、appender列表、配置文件路径等比盯着日志猜快多了。4.2 日志文件不滚动、疯狂占磁盘文件不滚动的根本原因绝大多数是没有配置rollingPolicy。FileAppender只会无限追加写同一个文件所以如果你用的是FileAppender而不是RollingFileAppender那文件必然越来越大。另一个常见问题是TimeBasedRollingPolicy的时间触发精度。按天滚动时logback并不会在每天零点立刻生成新文件我第一次用的时候也以为会精准到秒。实际上logback是通过一个后台定时任务去检查时间如果应用刚好在零点到检查点之间没有日志写入新文件可能会延迟生成。这不是bug只是触发时机的问题。如果你发现日志文件在零点之后很久才切分不妨先确认一下是否还有日志在写入。还有一个更隐蔽的坑fileNamePattern里的时间戳和file里的文件名不一致。比如你当前写入的文件是order-service.log但fileNamePattern写的是orderservice.%d{yyyy-MM-dd}.log由于模式前缀和当前文件名对不上rollover之后可能生成一堆不规则的归档文件旧文件也不会被正确清理。所以务必确保fileNamePattern的前缀部分和file保持一致。磁盘爆满还有一个容易被忽略的原因归档文件总大小超限但totalSizeCap没配或者只配了maxHistory没配totalSizeCap。我给的建议是生产环境一定要同时配置maxHistory和totalSizeCap两者互为兜底确保无论日志量多疯狂磁盘都有一道保险。4.3 中文乱码与时间格式错位日志乱码问题在Windows上开发、Linux部署的项目里非常常见。根因是编码不一致——你的日志消息是UTF-8但logback写文件时如果没显式指定编码会使用平台默认编码Windows下是GBKLinux下是UTF-8导致同一个日志文件在不同环境打开时出现乱码。修复方案很简单每个带encoder的appender里都显式声明UTF-8encoder pattern${LOG_PATTERN}/pattern charsetUTF-8/charset /encoder千万不要只在pattern里设置格式而忽略charset这是我的老毛病后来统一在模版里加上才根治。时间格式错位的问题通常和时区有关。比如服务器用UTC你本地的日志时间是东八区看起来就差了8个小时。这时候不是在pattern里手动加8小时而是显式在pattern里指定时区pattern%d{yyyy-MM-dd HH:mm:ss.SSS, GMT8} %-5level .../pattern还有一种情况是时间字段的毫秒数不对比如打印出来全是.000那是因为操作系统没有提供毫秒精度的时钟或者日志框架拿到的Timestamp本身就是秒级。一般不用太纠结如果真要毫秒级时间戳用%d{yyyy-MM-dd HH:mm:ss.SSS}是标准姿势没生效再去查时区和系统时钟。4.4 异步日志丢了先看队列和丢弃策略异步日志吞日志是排查耗时大户。现象是压测的时候业务吞吐量上去了但日志文件里的条数明显少于实际业务量。第一步先看discardingThreshold。如果用的是默认值20队列快满时等级为TRACE、DEBUG、INFO的日志会被直接丢弃。这在很多只关心ERROR日志的系统里无所谓但如果你需要按INFO日志排查请求流程这就是灾难。如果确认需要保留全量INFO日志把discardingThreshold设为0。第二步看neverBlock。如果设成false而队列满的时候业务线程在阻塞等待这会导致接口RT飙升但日志不会丢。如果设成true日志会丢但业务线程不会受影响。这两个参数就是日志量和业务延迟之间的博弈没有标准答案。就我自己的实践来说业务日志和链路追踪日志对完整性要求高我会优先保日志对于访问量极大的网关层我会优先保延迟允许丢一些INFO日志。第三步看queueSize。队列太小是丢日志的根本原因。压测环境可能几秒钟就把256的队列塞满你还没反应过来前面的日志已经被丢弃了。把queueSize调到8192甚至更高能显著降低日志丢失概率。但队列是占用内存的每个日志事件包含时间、线程、logger、消息内容等信息队列越大内存开销越高需要配合堆内存一起评估。还有一个排查方法是给异步appender加maxFlushTime参数。应用停止时AsyncAppender会花时间把队列里剩余日志刷出去默认可能不给足够的时间导致应用退出时队列里的日志丢失。设一个合理的值比如maxFlushTime30000/maxFlushTime能减少这种最后一个请求的日志消失的情况。说回logback官网这篇文章里涉及的标签和参数官网的manual章节里都有更详细的解释尤其是RollingFileAppender的滚动策略部分。以我的经验配置这种东西看一遍未必记得住把官方文档当成字典遇到问题再去查对应章节效率反而最高。最后分享一个我个人的习惯。每次新项目落地我都会做三件事第一把日志配置做成团队的统一模版包含滚动、异步、分级过滤和MDC所有服务直接拷贝第二在启动脚本里显式打印logback.configurationFile的值确认最终加载的是哪份配置第三压测的时候专门盯一下日志文件的增长速度和异步队列的丢弃情况提前发现性能隐患。这套流程帮我省了无数次凌晨爬起来查日志的麻烦。
返回列表