当前位置: 首页 > news >正文

系统日志与用户信息绑定实现日志跟踪

一、需求背景

微服务环境下,在所有微服务日志中识别用户本次访问的日志,用于用户操作日志跟踪。

二、技术实现

在用户登录时,生成一个唯一ID,用户ID+时间戳,假设唯一ID的键为userTraceId,并将其存储到MDC(Mapped Diagnostic Context)中,MDC 是Logback`支持的线程上下文存储机制,适合在日志中动态传递变量。

1.修改 logback-spring.xml

添加%X{userTraceId},:

<property name="SYSLOG" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%msg]%n  [%X{userTraceId}]"/>

%X{userTraceId}会从MDC中获取userTraceId值,若不存在则为空。

2.设置拦截器

用户登录的拦截器中设置MDC值,确保在请求结束时清理MDC,避免线程池复用导致的上下文污染。

@Component
public class UserTraceInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String userId = extractUserId(request);if (userId != null) {// 生成唯一userTraceId,例如:userId + 时间戳 + 随机UUIDString userTraceId = userId + "-" + System.currentTimeMillis() + "-" + UUID.randomUUID().toString();MDC.put("userTraceId", userTraceId);} else {// 如果没有用户信息,可以设置默认值或空MDC.put("userTraceId", "anonymous-" + UUID.randomUUID().toString());}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 清理MDC,防止线程复用导致上下文污染MDC.remove("userTraceId");}private String extractUserId(HttpServletRequest request) {String authHeader = request.getHeader("Authorization");if (authHeader != null && authHeader.startsWith("Bearer ")) {return parseUserIdFromToken(authHeader.substring(7));}return null;}private String parseUserIdFromToken(String token) {return "user123";     }
}

注册拦截器到Spring配置中:

@Configuration
public class WebConfig implements WebMvcConfigurer {private final UserTraceInterceptor userTraceInterceptor;public WebConfig(UserTraceInterceptor userTraceInterceptor) {this.userTraceInterceptor = userTraceInterceptor;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userTraceInterceptor).addPathPatterns("/**");}
}

3.Feign传递参数-调用服务

创建一个 Feign 请求拦截器,将 MDC 中的 userTraceId 添加到 Feign 请求头中:

@Component
public class FeignTraceInterceptor implements RequestInterceptor {private static final String USER_TRACE_ID_HEADER = "X-User-Trace-Id";@Overridepublic void apply(RequestTemplate requestTemplate) {// 从 MDC 获取 userTraceIdString userTraceId = MDC.get("userTraceId");if (userTraceId != null) {// 将 userTraceId 添加到请求头requestTemplate.header("X-user-trace-id", userTraceId);}}
}

说明:

RequestInterceptor 会在每个 Feign 请求发送前被调用。它从 MDC 中读取 userTraceId,并将其放入请求头 X-User-Trace-Id。如果 userTraceId 不存在,请求头中不会添加该字段。

4.Feign传递参数-被调用微服务

在被调用微服务的 UserTraceInterceptor 中接收 userTraceId,修改 UserTraceInterceptor,在 extractUserId 方法中,优先获取请求头中的 X-UserTraceId,如果没有则按原逻辑生成新的 userTraceId:

/*** @description: 用户日志跟踪拦截器* @description: MDC(Mapped Diagnostic Context):Logback 支持的线程上下文存储机制,适合在日志中动态传递变量。*/
@Component
public class UserTraceInterceptor implements HandlerInterceptor {private static final String USER_TRACE_ID_HEADER = "X-UserTrace-Id";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 获取用户信息或请求头中的 userTraceIdString userTraceId = extractUserTraceId(request);if (userTraceId != null) {MDC.put("userTraceId", userTraceId);} else {// 默认值:访客 + 时间戳MDC.put("userTraceId", "访客-" + System.currentTimeMillis());}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 清理 MDC,防止线程复用导致上下文污染MDC.remove("userTraceId");}private String extractUserTraceId(HttpServletRequest request) {// 优先从请求头获取 userTraceId(来自上游服务)String userTraceId = request.getHeader("X-User-Trace-Id");if (userTraceId != null && !userTraceId.isEmpty()) {return userTraceId;}// 如果没有 userTraceId,则尝试获取 global-trace-id 或用户信息String globalTraceId = request.getHeader("global-trace-id");if (globalTraceId != null && !globalTraceId.isEmpty()) {return globalTraceId;}// 从会话中获取用户信息SellerUser sellerUser = WebSellerSession.getSellerUser(request);if (sellerUser != null && sellerUser.getName() != null) {return sellerUser.getName() + "-" + System.currentTimeMillis();}return null;}
}

说明:

extractUserTraceId 优先检查请求头 X-User-Trace-Id,以接收上游微服务传递的 userTraceId。如果没有 userTraceId,则按原逻辑检查 global-trace-id 或用户信息。如果都不可用,返回 null,在 preHandle 中生成默认的 访客-时间戳。

三、总结

通过在拦截器中设置MDC的userTraceId,并在logback-spring.xml的日志模式中添加%X{userTraceId},即可实现基于登录用户的日志跟踪功能。

http://www.dtcms.com/a/298454.html

相关文章:

  • 前端基础知识Vue系列 - 27(Vue项目中如何解决跨域)
  • 从 SQL Server 到 KingbaseES V9R4C12,一次“无痛”迁移与深度兼容体验实录
  • js基础概念-1
  • 牛客NC16660 [NOIP2004]FBI树(递归 + 二叉树后序遍历)
  • electron中IPC 渲染进程与主进程通信方法解析
  • 常用设计模式系列(十二)—享元模式
  • 如何在 FastAPI 中玩转 GraphQL 和 WebSocket 的实时数据推送魔法?
  • C++中使用Essentia实现STFT/ISTFT
  • git 连接GitHub仓库
  • 强化学习之策略熵坍塌优化-clip conv kv conv
  • 若依搭建详解
  • Android Paging 分页加载库详解与实践
  • 第七章 愿景11 琦琦复盘测试
  • Keepalived 深度技术解析与高可用实践指南
  • C++编程学习(第15天)
  • ServletRegistrationBean相关知识点
  • 用 Docker 一键部署 Flask + Redis 微服务
  • NX848NX854美光固态闪存NX861NX864
  • 截稿倒计时 TrustCom‘25大会即将召开
  • C++中AC、WA、RE、CE、TLE、MLE、PE、OLE的意思
  • 【ResNet50图像分类部署至RK3588】模型训练→转换RKNN→开发板部署
  • 安装本地python文件到site-packages
  • 专题:2025电商增长新势力洞察报告:区域裂变、平台垄断与银发平权|附260+报告PDF、原数据表汇总下载
  • Linux运维新人自用笔记(Rsync远程传输备份,服务端、邮箱和客户端配置、脚本)
  • 【c++思维题】洛谷 P1496 火烧赤壁
  • 【js(8) for...in和for...of】
  • NVM踩坑实录:配置了npm的阿里云cdn之后,下载nodejs老版本(如:12.18.4)时,报404异常,下载失败的问题解决
  • LeetCode|Day25|389. 找不同|Python刷题笔记
  • IOPaint 图像修复工具,学习笔记
  • clFlush和clFinish的区别 (来自deepseek)