日常开发中,你如何快速定位问题的?
a. 排除条件
特别是处理并发请求时,通过对每个方法生成traceId(自定义唯一标识符)用于快速定位目标日志,追踪整个系统中的请求流程。
b. 日志脱敏增强
返回值带着敏感信息,要么不返回,要么加密
请求参数带着敏感信息,要么不记录,要么加密
c. 解决方案
服务端入口处可以生成唯一的id,叫做:traceId
日志中均需要输出traceId的值
接口返回值中,添加一个通用的字段: traceld,将上面的traceld作为这个字段的值
这样前端发现接口有问题的时候,直接将这个traceld提供给我们,我们便可以在日志中快速查询出对应的日志。
MDC组件
使用Slf4j.MDC日志功能增强
org.slf4j.MDC是 SLF4J库中的一个组件,MDC提供了一种机制,允许在日志消息中插入上下文信息,这些信息可以跨多个方法调用和线程边界传播。这对于跟踪和调试分布式系统或多线程应用程序中的请求非常有用。
重点:**MDC 允许将键值对与当前线程关联起来(类似ThreadLocal)**然后,可以在我们的日志语句中引用这些值,从而能够更容易地识别和理解日志消息产生的上下文。
例如,你可能会在 Web 应用程序的每个请求开始时,将用户的 ID 或会话 ID 放入 MDC,然后在你的日志语句中引用这个值。这样,当你查看日志时,你可以很容易地看到哪个用户的哪个请求产生了哪些日志消息。
使用方式
Logback学习系列4(Pattern使用方法)https://www.jianshu.com/p/5cfc26caf50d
- 设置值:
MDC.put("userId", "12345"); - 在日志语句中使用值:
logger.info("Processing request for user: {}", MDC.get("userId")); - 清除值
MDC.remove("userId")
例子
假设我们正在开发一个电商网站,该网站由多个微服务组成,包括用户服务、订单服务、支付服务等。每个服务都运行在独立的服务器上,并通过REST API相互调用。为了方便调试和监控,我们需要在整个请求处理过程中跟踪每一个请求,并确保所有相关的日志条目都能关联起来。
-
当请求到达第一个服务(例如用户服务)时,
TraceFilter生成一个唯一的traceId并将其放入MDC中。 -
当用户服务需要调用订单服务时,将当前线程中的
traceId作为HTTP请求头的一部分传递给下游服务(订单服务)。在订单服务接收到请求时,从请求头中读取traceId并再次放入MDC中。// 客户端拦截器:将TraceId注入HTTP头 public class OpenFeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { String traceId = MDC.get("traceId"); // 从当前线程上下文中获取TraceId requestTemplate.header("X-Trace-ID", traceId); // 注入到HTTP头 } } // 服务端处理:从HTTP头提取TraceId public class TraceFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String traceId = request.getParameter("X-Trace-ID"); // 从HTTP头获取TraceId MDC.put("traceId", traceId); // 写入当前线程上下文 chain.doFilter(request, response); } } -
在每个服务的日志配置文件中,添加
traceId到日志格式中,以便每条日志都包含这个信息。开发人员只需搜索traceId: xyz789即可快速定位问题根源。[订单服务] [traceId: xyz789] - 开始创建订单 [库存服务] [traceId: xyz789] - 扣减库存成功 [支付服务] [traceId: xyz789] - 支付失败,数据库连接超时 -
现在,无论请求跨越了多少个服务,只要查看日志,就可以通过
traceId轻松地将所有相关的日志条目关联起来,从而快速定位问题或理解系统的运行情况。
核心代码
自定义注解
脱敏注解@NoLogAnnotation 相关方法加上注解后隐藏参数信息,脱敏前 密码没加密时会暴露用户密码等敏感信息
@Target({
ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogAnnotation {
//可以用在参数列表或者方法上,屏蔽不愿意记录的参数
}
日志配置logback.xml展示TraceId
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出格式 将MDC中的traceId自动嵌入每条日志(唯一)-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] [traceId:%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.zr.study" level="info" />
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
链路追踪组件
TraceConfiguration,配置类注入过滤器和切面类进ioc容器
package com.zr.study.trace;
import com.zr.study.aop.ResultTraceIdAspect;
import org.springframework.context.annotation.Bean;
import
注解脱敏与链路追踪快速定位错误

最低0.47元/天 解锁文章
4258

被折叠的 条评论
为什么被折叠?



