☰
Logger4j 从会用到用明白:核心机制、配置与性能优化
2026/11/3 5:46:33 网站建设 项目流程

日志这件事,几乎每个写过几行代码的人都碰过,但真正把日志框架用明白的人并不多。我见过太多项目,日志打得满屏都是,真出问题的时候却一条有用的都翻不出来;也见过有人把日志级别全开成 DEBUG,上线当天磁盘就被写满。Logger4j 这个名字,很多人第一反应是"哦,就是那个打日志的",但真问它内部怎么组织 Logger、Appender、Layout 这三层结构,怎么做到配置文件一改就生效,多数人答不上来。这篇内容就是想把 Logger4j 从"会用"讲到"用明白",把它的核心机制、配置逻辑、性能取舍和实战踩坑一次说透。不管你是刚接触日志框架的新手,还是已经用了几年但一直没深究的老手,都能从里面找到能直接抄作业的配置和能少走弯路的经验。

1. 先搞清楚 Logger4j 到底在解决什么问题

1.1 从 System.out.println 的局限说起

很多人刚学编程时打日志的方式就是System.out.println,简单直接,一行代码就能把变量值甩到控制台。但项目一旦上了规模,这种做法的毛病就全暴露出来了。第一,输出目标单一,只能往控制台写,想同时写文件、发到远程、按天切割,全靠自己手写 IO 逻辑。第二,没有级别概念,调试信息、警告、错误混在一起,线上环境想只看错误日志根本做不到。第三,性能差,println是同步阻塞的,高并发场景下大量日志会直接拖慢主线程。第四,格式不可控,想加时间戳、线程名、类名,得自己拼字符串,拼错一个分隔符就乱套。

Logger4j 的出现,本质上是把这些零散的诉求收敛成一套可配置、可扩展的体系。它把"日志记录"这件事拆成了几个正交的维度:记录什么(Logger)、记录到哪(Appender)、记成什么样(Layout)、什么级别才记(Level)。这几个维度互相独立,你可以自由组合,而不需要改一行业务代码。这就是它比println高明的地方——关注点分离。

我个人的体会是,理解 Logger4j 的关键不在于记住多少个 API,而在于理解它这套"分层解耦"的设计哲学。一旦你脑子里有了 Logger、Appender、Layout 这三层模型,后面所有的配置项、所有的坑,都能在这个模型里找到位置。

1.2 三层模型:Logger、Appender、Layout 各管什么

先把这个核心模型讲清楚,后面所有内容都围绕它展开。

Logger(日志记录器)是你在代码里直接打交道的对象。你通过Logger.getLogger("com.example.service")拿到一个 Logger,然后调用info()、warn()、error()等方法。Logger 本身不负责输出,它只负责"接收日志请求"并"判断这个请求该不该往下传"。判断的依据就是日志级别,以及这个 Logger 上挂的级别配置。Logger 之间还有父子继承关系,按包名层级组织,子 Logger 没配级别就往上找父级,最终找到根 Logger。这个继承机制是理解 Logger4j 配置的第一把钥匙。

Appender(输出目的地)决定日志最终写到哪。常见的有控制台 Appender、文件 Appender、按大小滚动的文件 Appender、按时间滚动的文件 Appender,还有发邮件、写数据库、发到消息队列的 Appender。一个 Logger 可以挂多个 Appender,所以同一条日志可以同时进控制台和文件。Appender 还带一个"阈值"属性,可以设置只有某个级别以上的日志才进这个 Appender,这就实现了"错误日志单独存一个文件"的需求。

Layout(格式化器)决定日志长什么样。最常用的是 PatternLayout,用占位符拼出时间、级别、线程、类名、消息。还有专门输出 JSON 的 Layout,方便日志采集系统解析。Layout 挂在 Appender 上,一个 Appender 配一个 Layout。

这三层的关系可以这样类比:Logger 是"记者",负责采集信息;Appender 是"发行渠道",决定信息发到报纸、网站还是电台;Layout 是"排版编辑",决定信息最终呈现的格式。记者只管写稿,发到哪、怎么排版是后面两层的事,各司其职。

1.3 为什么它比手写日志更值得用

有人会问,我就打个日志,自己封装一个工具类不行吗?短期看行,长期看一定会踩坑。手写日志工具类通常逃不过几个问题:级别控制写死、输出目标写死、格式写死,一旦需求变化就得改代码重新发版。而 Logger4j 把这些都外置到配置文件里,改配置不用改代码,甚至不用重启(配合自动重载)。另外,Logger4j 在性能上做了大量优化,比如异步 Appender、延迟格式化、对象池复用,这些手写很难做到位。

还有一点容易被忽略:生态兼容。大量第三方库内部就是用 Logger4j 或它的门面接口打日志的,你项目里统一用 Logger4j,这些库的日志就能自然汇入你的日志体系,不用额外桥接。如果自己造轮子,第三方库的日志你根本管不到。

2. 配置文件才是 Logger4j 的主战场

2.1 三种配置方式怎么选

Logger4j 支持三种配置方式:properties 文件、XML 文件、编程式配置。实际项目里怎么选,我有一套自己的判断标准。

properties 格式最简单,键值对一目了然,适合配置项不多、结构简单的小项目。但它的表达能力有限,配置复杂的 Appender 层级关系时会很别扭,键名又长又难记,比如log4j.appender.file.rollingPolicy.MaxFileSize这种,写错一个字母就静默失效,排查起来很痛苦。

XML 格式表达能力强,层级结构清晰,Appender、Layout、Filter 的嵌套关系一目了然,是中型以上项目的首选。缺点是文件比较冗长,手写容易漏闭合标签。我一般建议用 XML,因为它的结构天然对应 Logger4j 的对象模型,读配置就像读对象树,理解成本反而低。

编程式配置适合需要动态调整的场景,比如根据运行环境在代码里决定日志路径。但它把配置硬编码进了代码,失去了"改配置不改代码"的最大优势,除非有特殊需求,否则不推荐作为主要方式。

提示:不管用哪种格式,配置文件放哪很关键。放在 classpath 根目录下,Logger4j 启动时会自动加载;放在自定义路径,需要在启动参数里指定。放错位置导致配置不生效,是新手最常见的坑之一。

2.2 一个能直接用的 XML 配置骨架

下面这份配置是我在多个项目里反复打磨过的骨架,覆盖了控制台输出、按天滚动的文件输出、错误日志单独归档三个典型需求,可以直接拿去改。

<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Properties> <Property name="LOG_HOME">./logs</Property> <Property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="${PATTERN}"/> </Console> <RollingFile name="RollingFile" fileName="${LOG_HOME}/app.log" filePattern="${LOG_HOME}/app-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="${PATTERN}"/> <Policies> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> <DefaultRolloverStrategy max="30"/> </RollingFile> <RollingFile name="ErrorFile" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/error-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="${PATTERN}"/> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <Policies> <TimeBasedTriggeringPolicy interval="1"/> </Policies> <DefaultRolloverStrategy max="60"/> </RollingFile> </Appenders> <Loggers> <Logger name="com.example" level="DEBUG" additivity="false"> <AppenderRef ref="RollingFile"/> <AppenderRef ref="ErrorFile"/> </Logger> <Root level="INFO"> <AppenderRef ref="Console"/> <AppenderRef ref="RollingFile"/> </Root> </Loggers> </Configuration>

这份配置里有几个点值得单独说。additivity="false"表示这个 Logger 的日志不再往父级传递,避免同一条日志被重复输出。ThresholdFilter让错误文件只收 ERROR 及以上级别。DefaultRolloverStrategy max="30"控制历史文件保留数量,防止磁盘被撑爆。这些细节后面还会展开。

2.3 配置加载顺序与优先级

Logger4j 启动时会按固定顺序找配置文件:先看系统属性指定的路径,再看 classpath 下的log4j2.xml、log4j2.json、log4j2.properties等。找到第一个就用,后面的不再加载。这个顺序意味着,如果你项目里同时存在多个配置文件,实际生效的可能不是你以为的那个。

我踩过一次坑:项目依赖的某个第三方包自带了一份log4j2.xml,打包时它被放进了 classpath,结果我们自己写的配置被覆盖了,日志行为完全不对。排查了半天才发现是依赖里的配置文件在捣乱。解决办法是在打包配置里排除掉依赖中的配置文件,或者用系统属性显式指定配置路径,优先级最高,能压过 classpath 里的。

注意:配置文件的加载是"先到先得",不是"后到覆盖"。多个配置文件共存时,谁先被找到谁生效,这个顺序一定要心里有数。

3. 日志级别与继承机制:配置不生效的根源都在这

3.1 级别从低到高,到底谁过滤谁

Logger4j 的级别从低到高依次是 TRACE、DEBUG、INFO、WARN、ERROR、FATAL,另外还有两个特殊值 ALL 和 OFF。规则很简单:Logger 上配置的级别是"门槛",只有级别大于等于门槛的日志才会被处理。比如 Logger 配了 INFO,那 DEBUG 和 TRACE 的日志直接被丢弃,连往下传的机会都没有。

这里有个容易混淆的点:Logger 级别和 Appender 阈值是两道独立的关卡。日志先过 Logger 级别这道关,过了之后才分发给各个 Appender,每个 Appender 再用自己的阈值过滤一次。所以一条 INFO 日志,如果 Logger 配的是 DEBUG(放行),但某个 Appender 阈值是 ERROR(拦截),那这条日志进不了那个 Appender。两道关卡是"与"的关系,任何一道拦下就出不去。

理解这一点,很多"为什么我的日志没打出来"的问题就迎刃而解了。先查 Logger 级别够不够,再查 Appender 阈值拦没拦,两个都对了才轮到 Layout 和输出目标的问题。

3.2 父子继承:为什么改了根 Logger 全变了

Logger 按名字的层级组织成树。com.example.service是com.example的子 Logger,com.example又是根 Logger 的子节点。子 Logger 如果没有显式配置级别,就继承父级的级别,一路往上找到根 Logger 为止。根 Logger 的级别是所有 Logger 的兜底。

这个机制的好处是配置省事,配一个com.example的级别,下面所有子包都跟着走。坏处是容易误伤:你把根 Logger 调成 DEBUG,整个应用所有没单独配级别的 Logger 全变 DEBUG,日志量瞬间爆炸。我见过有团队为了排查一个问题临时把根级别调成 DEBUG,结果忘了改回来,第二天磁盘告警。

正确的做法是:根 Logger 保持 INFO 或 WARN 这种相对保守的级别,需要详细日志的特定包单独配 DEBUG。这样既能精细控制,又不会全局失控。

3.3 additivity 与日志重复输出的排查

additivity是 Logger 的一个属性,默认 true,表示日志处理完后继续往父 Logger 传递。传递的过程中,父 Logger 挂的 Appender 也会收到这条日志。所以如果子 Logger 和父 Logger 都挂了同一个 Appender,同一条日志就会被输出两次。

日志重复是特别常见的现象,很多人第一次遇到会以为是代码里打了两遍。排查方法很直接:看子 Logger 有没有配additivity="false",或者看父子 Logger 是不是引用了同一个 Appender。我一般的习惯是,只要给某个包单独配了 Logger 和 Appender,就顺手加上additivity="false",从源头上杜绝重复。

还有一种隐蔽的重复:两个不同的 Appender 指向了同一个文件。这种情况日志内容会交错写入,看起来像重复但其实是两个 Appender 各写各的。排查时看日志里有没有两套不同的格式特征,如果有,基本就是 Appender 配重了。

4. Appender 选型:不同场景该往哪写

4.1 控制台、文件、滚动文件的取舍

控制台 Appender 适合开发环境,日志直接打到终端,调试方便。但生产环境一般不建议只靠控制台,因为终端输出会被容器或进程管理器接管,日志容易丢,也不好归档。生产环境的主力应该是文件 Appender。

普通文件 Appender 简单,但有个致命问题:文件会无限增长。跑上几天,日志文件几个 G,打开都费劲,更别说检索。所以生产环境几乎都用滚动文件 Appender(RollingFile),它能在文件达到一定大小或时间后自动切分,还能压缩历史文件、限制保留数量。

滚动策略有两种触发方式:按大小和按时间。按大小适合日志量波动大的场景,比如设 100MB 切一个,保证单个文件不会太大。按时间适合需要按天归档的场景,比如每天零点切一个,方便按日期检索。实际项目里两者经常组合使用,先按时间切,同一天内超过大小再按序号切,这就是配置里%d{yyyy-MM-dd}-%i这种 filePattern 的由来。

4.2 异步 Appender 的性能账怎么算

日志对性能的影响主要来自 IO。同步写日志时,业务线程要等日志真正落盘才能继续,高并发下这个等待会累积成明显的延迟。异步 Appender 的思路是把日志先扔进一个内存队列,由后台线程慢慢消费,业务线程写完队列就返回,不等落盘。

这个优化效果很显著,实测在日志量大的场景下,异步能把日志带来的延迟降低一个数量级。但异步不是没有代价:队列满了怎么办?Logger4j 提供了几种策略,默认是阻塞等待,也就是队列满时业务线程会被卡住,这其实又退回了同步的老路。另一种策略是丢弃,队列满了直接扔日志,保证业务不受影响,但会丢日志。选哪种取决于你的业务能不能接受丢日志。

我的经验是,核心交易类业务用阻塞策略,宁可慢一点也不能丢;普通业务用丢弃策略,日志丢了不影响主流程。队列大小也要调,默认值偏小,日志峰值高的时候容易满,一般调到 4096 或 8192 比较稳妥。

<Async name="AsyncFile" bufferSize="8192"> <AppenderRef ref="RollingFile"/> </Async>

这段配置把滚动文件 Appender 包成异步的,bufferSize就是队列容量。注意异步 Appender 要包在真正的输出 Appender 外面,而不是反过来。

4.3 按业务拆分日志文件的实践

单一日志文件在排查时很痛苦,所有模块的日志混在一起,想只看支付相关的得靠 grep 过滤。更好的做法是按业务或模块拆分文件,比如支付一个文件、订单一个文件、定时任务一个文件。

实现方式是利用 Logger 的包名层级,给不同包配不同的 Logger 和 Appender。比如com.example.pay包配一个PayFileAppender,com.example.order配一个OrderFile。这样各模块的日志自然分流,排查时直接看对应文件,效率高很多。

拆分的粒度要把握好。拆太细,Appender 和文件数量爆炸,维护成本高;拆太粗,又起不到隔离作用。我一般按核心业务域拆,一个业务域一个文件,公共组件和框架日志统一进一个 common 文件。这个粒度在实践中比较平衡。

5. Layout 与日志格式:让日志真正可读可查

5.1 PatternLayout 占位符逐个拆解

PatternLayout 是使用频率最高的 Layout,靠占位符拼格式。常用的占位符有这么几个,我按使用频率排一下:

占位符含义说明
%d时间可带格式,如%d{yyyy-MM-dd HH:mm:ss.SSS}
%level/%-5level级别带-5表示左对齐占 5 字符宽
%msg/%m日志消息最核心的内容
%n换行每条日志结尾必须有
%t线程名排查并发问题必备
%logger/%cLogger 名通常截断显示,如%logger{36}
%thread同%t线程名
%X{key}MDC 值链路追踪的关键

%d的时间格式建议精确到毫秒,排查问题时秒级精度往往不够。%level用%-5level对齐,日志看起来整齐,扫一眼就能定位级别。%logger{36}里的 36 是最大长度,超过会从左边截断,保留右边更有辨识度的部分。

一个我常用的格式是这样的:

%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

这个格式信息量足够,又不至于太冗长。时间、线程、级别、来源、消息五要素齐全,排查时基本够用。

5.2 MDC 在链路追踪里的妙用

MDC(Mapped Diagnostic Context)是 Logger4j 提供的一个线程绑定的上下文容器,你可以往里塞键值对,然后在 Layout 里用%X{key}取出来。它在链路追踪场景里特别好用。

典型用法是在请求入口处生成一个 traceId,塞进 MDC,然后这个请求处理过程中打的所有日志都会自动带上这个 traceId。排查问题时,拿一个 traceId 一搜,整个请求链路的日志全出来了,不用在茫茫日志里靠时间戳猜。

MDC.put("traceId", UUID.randomUUID().toString()); try { // 业务逻辑 } finally { MDC.remove("traceId"); }

这里有个坑必须提醒:MDC 是线程绑定的,异步线程里取不到父线程的 MDC 值。如果你的业务用了线程池,日志里的 traceId 会丢。解决办法是在任务提交时把 MDC 内容复制到子线程,或者用支持上下文传递的线程池包装。这个问题在微服务项目里特别常见,很多人配了 MDC 却发现异步日志里 traceId 是空的,根源就在这。

注意:MDC 用完一定要 remove,尤其是在线程池场景下。线程被复用,上一个请求的 MDC 值会残留到下一个请求,导致 traceId 串号,排查时会被误导。

5.3 JSON 格式日志与采集系统对接

如果日志要进采集系统(比如做集中检索和告警),纯文本格式解析起来很麻烦,正则写错一点就漏数据。这时候用 JSON Layout 更合适,每条日志是一个 JSON 对象,字段清晰,采集端直接解析。

JSON Layout 的配置大致是这样:

<JsonLayout complete="false" compact="true"> <KeyValuePair key="service" value="order-service"/> </JsonLayout>

complete="false"表示不输出最外层的包装数组,每条日志独立一行,方便逐行采集。compact="true"去掉多余空白,减小体积。还可以用KeyValuePair加固定字段,比如服务名、环境标识,方便采集端按服务过滤。

JSON 格式的代价是可读性下降,人眼直接看不如文本格式直观。所以我的做法是控制台用文本格式给人看,文件用 JSON 格式给机器采集,两套 Layout 各司其职。

6. 性能优化与常见故障排查

6.1 日志拖慢接口的几种典型原因

日志导致接口变慢,原因通常集中在几个地方。第一是同步 IO,前面说过,用异步 Appender 能缓解。第二是日志量过大,DEBUG 级别在生产环境全开,每条日志都要格式化、写盘,累积起来很可观。第三是格式化开销,比如在日志参数里做字符串拼接logger.debug("value: " + heavyObject.toString()),即使这条日志最终被级别过滤掉,字符串拼接也已经执行了,白白浪费 CPU。

针对第三点,正确做法是用占位符:logger.debug("value: {}", heavyObject)。这样只有日志真正要输出时才会调用toString(),被过滤掉时零开销。这个细节很多人不注意,但在高频调用的方法里,差别很明显。

还有一种隐蔽的性能问题:日志里打了大对象,比如整个请求体、整个列表。序列化成字符串本身耗时,写盘也慢,日志文件还会迅速膨胀。日志应该打关键信息,而不是把整个对象 dump 出来。

6.2 配置文件不生效的完整排查链路

配置文件不生效是最高频的问题,我整理了一套排查顺序,按这个链路走基本能定位。

第一步,确认配置文件被加载了。启动时加-Dlog4j2.debug=true,控制台会打印配置加载的详细过程,包括找了哪些路径、最终用了哪个文件。这一步能排除掉大部分"配置根本没被读到"的情况。

第二步,确认配置内容没写错。XML 标签拼错、属性名写错、Appender 引用名对不上,都会导致配置静默失效。Logger4j 对配置错误有时只打一条 WARN 就过去了,不仔细看日志根本发现不了。所以配置改完一定要看启动日志有没有相关警告。

第三步,确认 Logger 名字匹配。代码里getLogger("com.example.pay"),配置里写的是com.example.payment,名字对不上,配置自然不生效。包名层级要严格对应。

第四步,确认级别和阈值。前面讲过,Logger 级别和 Appender 阈值两道关卡,任何一道拦下日志都出不来。排查时先把级别调到 ALL,确认能输出,再逐步收紧,定位是哪道关卡拦的。

第五步,确认输出目标可写。文件路径没权限、目录不存在,日志写不进去但可能不报错。检查一下目标目录的权限和存在性。

这套链路我用了很多次,绝大多数配置问题都能在前三步定位到。

6.3 磁盘写满与日志膨胀的预防

日志把磁盘写满,是运维事故里的常客。预防手段主要有三个。第一,滚动策略一定要配,按大小或按时间切分,别让单文件无限长。第二,保留数量一定要限制,DefaultRolloverStrategy max设一个合理值,比如 30 或 60,超出的自动删除。第三,历史文件压缩,filePattern 里带.gz,滚动后的文件自动压缩,体积能小一个数量级。

还有一个容易被忽略的点:错误日志和普通日志分开存,错误日志保留时间长一些,普通日志保留时间短一些。因为排查问题时错误日志的价值更高,普通 INFO 日志过了几天基本没人看。分开配置保留策略,能在有限磁盘空间里保留更有价值的信息。

我见过一个项目,日志目录没做任何限制,跑了半年占了 200 多 G,最后是磁盘告警才发现的。加上滚动和保留策略后,同样的日志量只占几个 G。这个投入产出比非常高,值得每个项目在上线前就配好。

7. 我在实际项目里踩过的几个坑

7.1 依赖冲突导致的日志实现错乱

项目里同时引入了多个日志门面和实现,是日志行为异常的常见根源。比如既有 Logger4j 的核心包,又有旧版日志框架的桥接包,还有别的日志实现,类路径上谁先被加载就用谁,行为完全不可控。表现可能是日志格式不对、级别不生效、甚至日志直接不输出。

排查这类问题,先看依赖树,把所有日志相关的包列出来,理清门面和实现的关系。原则是:门面只留一个,实现只留一个,多余的桥接包该排的排、该换的换。统一之后,日志行为才可预期。

7.2 异步日志丢日志的定位过程

有次线上反馈,某些请求的日志查不到,但业务确实执行了。排查发现用的是异步 Appender,队列满了之后按丢弃策略处理,高峰期的日志被扔了。当时队列大小用的是默认值,日志峰值一上来就满了。

解决办法是把队列调大,同时评估丢弃策略是否合适。对于这类需要完整日志的场景,改成了阻塞策略,虽然峰值时略有延迟,但保证了日志不丢。这个取舍没有标准答案,取决于业务对日志完整性的要求。

7.3 热更新配置的边界条件

Logger4j 支持配置自动重载,改完配置文件不用重启就能生效,调试时很方便。但它有边界条件:重载有检测间隔,不是改完立刻生效;重载过程中如果有大量日志在写,可能出现短暂的输出异常;某些配置项(比如 Appender 的某些底层属性)重载后行为可能和预期不一致。

我的建议是,热更新用于开发调试没问题,生产环境的关键配置变更还是走重启流程更稳妥。热更新适合调级别这种轻量变更,涉及 Appender 结构变化的,重启更保险。

8. 一套可以直接落地的日志规范

8.1 级别使用的约定

级别用错是日志混乱的源头。我的约定是:ERROR 只用于需要人介入处理的异常,比如外部依赖挂了、数据不一致;WARN 用于可自愈但值得关注的情况,比如重试成功、降级触发;INFO 用于关键业务节点,比如订单创建、支付完成,量要控制,不能每个方法进出都打;DEBUG 用于排查问题的详细信息,生产环境默认关闭,需要时按包临时打开;TRACE 基本不用,太细了。

很多团队的问题是 INFO 打太多,把业务日志当调试日志用,结果生产环境日志量巨大还找不到重点。INFO 应该是"业务视角的关键事件",不是"代码执行轨迹"。

8.2 日志内容的写法要点

一条好日志应该能独立回答"发生了什么"。反面例子是logger.info("success"),成功什么了?谁成功了?正面例子是logger.info("order created, orderId={}, userId={}, amount={}", orderId, userId, amount),信息完整,排查时不用再去翻上下文。

异常日志要带堆栈,logger.error("pay failed, orderId={}", orderId, e)这种写法,最后一个参数传异常对象,Logger4j 会自动打堆栈。千万别只打e.getMessage(),丢了堆栈等于丢了排查线索。

敏感信息不能进日志,手机号、身份证、密码这些要么脱敏要么不打。这个不是技术问题,是合规问题,一旦日志泄露就是事故。

8.3 上线前的日志检查清单

每次上线前,我都会过一遍这个清单:配置文件是否在正确位置、滚动和保留策略是否配置、生产环境级别是否是 INFO 及以上、异步队列大小是否合理、敏感信息是否脱敏、错误日志是否单独归档、MDC 是否在异步场景正确传递。这几项检查花不了几分钟,但能避免绝大多数日志相关的线上问题。

日志这东西,平时不起眼,出事时就是救命稻草。配好它、用好它,是每个项目的基本功。上面这些配置和经验,拿去改改就能用,希望能帮你少踩几个我踩过的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询