1. 前文回顾和优化需求分析
前面五篇文章已经让《手写链路追踪》更进了一步
前文1:手写链路追踪
前文2:手写链路追踪优化-自动全局追踪代替局部手动追踪
前文3:手写链路追踪优化-多线程追踪
前文4:手写链路追踪优化-MDC改造
前文5:手写链路追踪优化-自定义用户信息追踪
前文想做用户信息的全链路追踪,但是只有阻塞线程生效了,请看日志详情
2025-09-10 22:19:26.856 [main] INFO o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/'
2025-09-10 22:19:26.866 [main] INFO com.sandwich.logtracing.LogTracingApplication - Started LogTracingApplication in 3.117 seconds (process running for 4.259)
2025-09-15 22:20:05.442 [http-nio-8080-exec-1] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-09-15 22:20:05.442 [http-nio-8080-exec-1] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2025-09-15 22:20:05.443 [http-nio-8080-exec-1] INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in 1 ms
2025-09-15 22:20:05.545 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 1 done
2025-09-15 22:20:05.551 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 1 done
2025-09-15 22:20:05.608 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 2 done
2025-09-15 22:20:05.608 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 2 done
2025-09-15 22:20:05.669 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 3 done
2025-09-15 22:20:05.669 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 3 done
2025-09-15 22:20:05.733 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 4 done
2025-09-15 22:20:05.733 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 4 done
2025-09-15 22:20:05.796 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 5 done
2025-09-15 22:20:05.796 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 5 done
2025-09-15 22:20:05.858 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 6 done
2025-09-15 22:20:05.858 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 6 done
2025-09-15 22:20:05.920 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 7 done
2025-09-15 22:20:05.920 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 7 done
2025-09-15 22:20:05.984 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 8 done
2025-09-15 22:20:05.984 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 8 done
2025-09-15 22:20:06.047 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 9 done
2025-09-15 22:20:06.047 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 9 done
2025-09-15 22:20:06.109 [async-thread-1] 1HBE4VReh3Vz1TQ INFO c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 10 done
2025-09-15 22:20:06.109 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 10 done
2025-09-15 22:20:06.172 [async-thread-1] 1HBE4VReh3Vz1TQ INFO com.sandwich.logtracing.util.ThreadLocalContext - traceId removed from thread local
2025-09-15 22:20:06.172 [http-nio-8080-exec-1] 1HBE4VReh3Vz1TQ INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.sandwich.logtracing.controller.LoginController - user Sandwich login success
2025-09-15 22:20:11.421 [http-nio-8080-exec-1] INFO com.sandwich.logtracing.util.ThreadLocalContext - traceId removed from thread local
由以上日志看出,异步线程并没有生效,如果我们想做好全链路用户信息可视化,在多线程项目中对异步线程的支持也是必不可少的。
多线程项目中有很多种异步线程,我们先来梳理一下常见的异步线程
根据代码和常见的Java应用架构,异步线程主要有以下几种类型:
-
@Async注解异步方法
- Spring框架提供的异步执行方式
- 通过在方法上添加
@Async注解实现异步调用
-
CompletableFuture异步任务
- Java 8引入的异步编程工具
- 支持链式调用和组合多个异步操作
- 可以使用自定义线程池执行任务
-
ThreadPoolTaskExecutor线程池
- Spring封装的线程池实现
- 可以通过注入 [ThreadPoolTaskExecutor] bean来执行异步任务
-
原生Thread线程
- Java原生的线程创建方式
- 直接继承Thread类或实现Runnable接口
-
ScheduledExecutorService定时任务
- Java提供的定时任务执行器
- 支持延迟执行和周期性执行任务
-
@Scheduled定时任务
- Spring提供的定时任务注解
- 在方法上添加 [@Scheduled]注解实现定时执行
对于上述异步线程类型,如果需要传递上下文信息(如traceId、用户信息等),都需要相应的处理机制。不过其原理都差不多,这里还是只用@Async注解实现异步调用作实例,其他异步线程不再一一阐述。
2.代码实现
2.1 修改MDC工具类
package com.sandwich.logtracing.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
/**
* @Author 公众号: IT三明治
* @Date 2025/8/31
* @Description: MDC工具类
*/
public class MdcUtils {
public static final String TRACE_ID = "traceId";
public static final String USER_ID = "userId";
public static final String USER_NAME = "username";
public static final String CLIENT_IP = "clientIp";
public static void setTraceId(String traceId) {
if (StringUtils.isBlank(traceId)) {
setTraceId();
return;
}
MDC.put(TRACE_ID, traceId);
}
public static void setTraceId() {
String traceId = RandomStrUtils.generateRandomString(15);
MDC.put(TRACE_ID, traceId);
}
public static String getTraceId() {
return MDC.get(TRACE_ID);
}
public static void removeTraceId() {
MDC.remove(TRACE_ID);
}
public static void setUserId(String userId) {
if (StringUtils.isBlank(userId)) {
return;
}
MDC.put(USER_ID, userId);
}
public static String getUserId() {
return MDC.get(USER_ID);
}
public static void removeUserId() {
MDC.remove(USER_ID);
}
public static void setUserName(String userName) {
if (StringUtils.isBlank(userName)) {
return;
}
MDC.put(USER_NAME, userName);
}
public static String getUserName(String username) {
return MDC.get(USER_NAME);
}
public static void removeUserName() {
MDC.remove(USER_NAME);
}
public static void setClientIp(String clientIp) {
if (StringUtils.isBlank(clientIp)) {
return;
}
MDC.put(CLIENT_IP, clientIp);
}
public static String getClientIp() {
return MDC.get(CLIENT_IP);
}
public static void removeClientIp() {
MDC.remove(CLIENT_IP);
}
public static void clearAll() {
removeTraceId();
removeUserId();
removeUserName();
removeClientIp();
}
}
2.2 修改thread local 上下文工具类
package com.sandwich.logtracing.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/**
* @Author 公众号: IT三明治
* @Date 2025/8/30
* @Description: 这是一个用于在线程间存储和传递追踪ID(traceId)的上下文工具类,主要目的是实现请求链路追踪。
*/
@Slf4j
public class ThreadLocalContext {
/**
* 线程本地存储,用于存储追踪ID
* 使用 InheritableThreadLocal 而不是普通的 ThreadLocal
* InheritableThreadLocal 允许子线程继承父线程的值,这对于异步处理场景非常重要
*/
private static final InheritableThreadLocal<String> TRACE_ID = new InheritableThreadLocal<>();
private static final InheritableThreadLocal<String> USER_ID = new InheritableThreadLocal<>();
private static final InheritableThreadLocal<String> USER_NAME = new InheritableThreadLocal<>();
private static final InheritableThreadLocal<String> CLIENT_IP = new InheritableThreadLocal<>();
public static String getTraceId() {
return TRACE_ID.get();
}
public static void setTraceId(String traceId) {
TRACE_ID.set(traceId);
}
/**
* 清除当前线程的traceId,防止内存泄漏
*/
public static void clearTraceId() {
if (StringUtils.isBlank(TRACE_ID.get())) {
log.info("traceId is empty from thread local already");
return;
}
TRACE_ID.remove();
log.info("traceId removed from thread local");
}
public static String getUserId() {
return USER_ID.get();
}
public static void setUserId(String userId) {
USER_ID.set(userId);
}
public static void clearUserId() {
if (StringUtils.isBlank(USER_ID.get())) {
log.info("user id is empty from thread local already");
return;
}
USER_ID.remove();
log.info("user id removed from thread local");
}
public static String getUserName() {
return USER_NAME.get();
}
public static void setUserName(String userName) {
USER_NAME.set(userName);
}
public static void clearUserName() {
if (StringUtils.isBlank(USER_NAME.get())) {
log.info("user name is empty from thread local already");
return;
}
USER_NAME.remove();
log.info("user name removed from thread local");
}
public static String getClientIp() {
return CLIENT_IP.get();
}
public static void setClientIp(String clientIp) {
CLIENT_IP.set(clientIp);
}
public static void clearClientIp() {
if (StringUtils.isBlank(CLIENT_IP.get())) {
log.info("client ip is empty from thread local already");
return;
}
CLIENT_IP.remove();
log.info("client ip removed from thread local");
}
/**
* 清除所有线程本地存储
*/
public static void clearAll() {
clearTraceId();
clearUserId();
clearUserName();
clearClientIp();
}
}
2.3 用MDC工具类定义的常代替拦截器的key的魔法值,同时把filter的内容废除,追踪逻辑全部移动到拦截器处理
package com.sandwich.logtracing.interceptor;
import com.sandwich.logtracing.util.MdcUtils;
import com.sandwich.logtracing.util.RandomStrUtils;
import com.sandwich.logtracing.util.ThreadLocalContext;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.RequestFacade;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Author 公众号: IT三明治
* @Date 2025/9/9
* @Description:
*/
@Slf4j
@Component
public class MDCInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
// 从header中获取用户信息
String traceId = request.getHeader("x-request-correlation-id");
//if the request header don't have a trace id,then generate a random one
if (StringUtils.isBlank(traceId)) {
traceId = RandomStrUtils.generateRandomString(15);
}
MdcUtils.setTraceId(traceId);
String userId = request.getHeader("X-User-ID");
if (StringUtils.isNotBlank(userId)) {
// 添加用户ID到MDC
MdcUtils.setUserId(userId);
}
String username = request.getHeader("X-Username");
if (StringUtils.isNotBlank(username)) {
// 添加用户名到MDC
MdcUtils.setUserName(username);
}
// 可以添加其他请求相关信息
String clientIp = getClientIp( request);
MdcUtils.setClientIp(clientIp);
ThreadLocalContext.setTraceId(traceId);
ThreadLocalContext.setUserId(userId);
ThreadLocalContext.setUserName(username);
ThreadLocalContext.setClientIp(clientIp);
return true;
}
@Override
public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
@NotNull Object handler, @NotNull ModelAndView modelAndView) {
// 请求处理完成后可以添加响应信息
}
@Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
@NotNull Object handler, @Nullable Exception ex) {
// 请求完全结束后清理MDC,防止内存泄漏
MdcUtils.clearAll();
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
注释掉filter
package com.sandwich.logtracing.config;
import com.sandwich.logtracing.filter.LogFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* @Author 公众号: IT三明治
* @Date 2025/8/30
* @Description:
*/
@Configuration
public class WebConfiguration {
// @Bean
// @ConditionalOnMissingBean(LogFilter.class)
// @Order(Ordered.HIGHEST_PRECEDENCE + 101)
// public FilterRegistrationBean<LogFilter> logFilterFilterRegistrationBean() {
// FilterRegistrationBean<LogFilter> bean = new FilterRegistrationBean<>();
// bean.setFilter(new LogFilter());
// bean.addUrlPatterns("/*");
// return bean;
// }
}
2.4 创建统一的上下文传输对象
package com.sandwich.logtracing.dto;
import com.sandwich.logtracing.util.MdcUtils;
import com.sandwich.logtracing.util.ThreadLocalContext;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* @Author 公众号: IT三明治
* @Date 2025/9/15
* @Description:
*/
@Data
public class RequestContext {
private String traceId;
private String userId;
private String username;
private String clientIp;
public static RequestContext capture() {
RequestContext context = new RequestContext();
context.setTraceId(ThreadLocalContext.getTraceId());
context.setUserId(ThreadLocalContext.getUserId());
context.setUsername(ThreadLocalContext.getUserName());
context.setClientIp(ThreadLocalContext.getClientIp());
return context;
}
public void restore() {
if (StringUtils.isNoneBlank(traceId)) MdcUtils.setTraceId(traceId);
if (StringUtils.isNoneBlank(userId)) MdcUtils.setUserId(userId);
if (StringUtils.isNoneBlank(username)) MdcUtils.setUserName(username);
if (StringUtils.isNoneBlank(clientIp)) MdcUtils.setClientIp(clientIp);
}
}
2.5 修改AOP切面
package com.sandwich.logtracing.aspect;
import com.sandwich.logtracing.dto.RequestContext;
import com.sandwich.logtracing.util.MdcUtils;
import com.sandwich.logtracing.util.ThreadLocalContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* @Author 公众号: IT三明治
* @Date 2025/8/30
* @Description: 这是一个用于在异步线程中保持traceId一致性的AOP切面,主要目的是在使用@Async注解的方法执行时,将主线程中的traceId传递到异步线程中
* 主线程是父线程,异步线程是子线程, 因此需要使用ThreadLocal来保存traceId, 以便在异步线程中保持一致性
*/
@Slf4j
@Aspect
@Component
public class TraceIdAspect {
@Around("@annotation(org.springframework.scheduling.annotation.Async)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
RequestContext context = RequestContext.capture();
try {
context.restore();
// 执行目标方法并返回
return joinPoint.proceed();
} finally {
// clear all fields from thread local
ThreadLocalContext.clearAll();
// remove all fields from mdc
MdcUtils.clearAll();
}
}
}
3. 测试验证
- 启动项目,用shell请求api
shell原文如下
#!/bin/bash
# Define the API endpoint
API_URL="http://localhost:8080/test/login"
function generate_random_string() {
# 使用openssl生成随机字符串(如果已安装)
if command -v openssl &> /dev/null; then
openssl rand -base64 20 | tr -dc 'a-zA-Z0-9' | fold -w 15 | head -n 1
else
# 使用系统方法生成
local chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
local result=""
result=$(printf "%s" "${chars:$((RANDOM % ${#chars})):1}"{1..15} | tr -d '\n')
echo "$result"
fi
}
function normalLogin() {
# 生成15位随机字符串作为traceId
traceId=$(generate_random_string)
echo "Generated traceId from client side: $traceId"
response=$(curl -X POST $API_URL \
-H "Content-Type: application/json" \
-H "x-request-correlation-id: $traceId" \
-H "X-User-ID: 123456" \
-H "X-Username: Sandwich" \
-d '{"username": "Sandwich", "password": "test"}')
echo "Response from login API:"
echo "$response" | python -m json.tool
}
normalLogin
API请求如下
Administrator@USER-20230930SH MINGW64 /d/git/java/log-tracing/shell (master)
$ ./login.sh
Generated traceId from client side: PgPzcW32QlOOVNv
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 144 0 100 100 44 118 52 --:--:-- --:--:-- --:--:-- 171
Response from login API:
{
"responseCode": 200,
Dload Upload Total Spent Left Speed
100 144 0 100 100 44 125 55 --:--:-- --:--:-- --:--:-- 180
Response from login API:
{
"responseCode": 200,
"message": "success",
"data": "Sandwich login success",
"traceId": "PgPzcW32QlOOVNv"
}
- 用trace id: PgPzcW32QlOOVNv 查看日志,验证结果
2025-09-17 07:50:30.234 [main] INFO o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/'
2025-09-17 07:50:30.242 [main] INFO com.sandwich.logtracing.LogTracingApplication - Started LogTracingApplication in 1.905 seconds (process running for 2.492)
2025-09-17 07:50:42.002 [http-nio-8080-exec-1] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-09-17 07:50:42.003 [http-nio-8080-exec-1] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2025-09-17 07:50:42.004 [http-nio-8080-exec-1] INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in 1 ms
2025-09-17 07:50:42.124 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 1 done
2025-09-17 07:50:45.431 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 2 done
2025-09-17 07:50:45.703 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 3 done
2025-09-17 07:50:46.071 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 4 done
2025-09-17 07:50:48.350 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 5 done
2025-09-17 07:50:48.355 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 1 done
2025-09-17 07:50:48.415 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 6 done
2025-09-17 07:50:48.416 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 2 done
2025-09-17 07:50:48.478 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 7 done
2025-09-17 07:50:48.478 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 3 done
2025-09-17 07:50:48.542 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 8 done
2025-09-17 07:50:48.542 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 4 done
2025-09-17 07:50:48.606 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 9 done
2025-09-17 07:50:48.606 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 5 done
2025-09-17 07:50:48.669 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing login for user Sandwich, login step 10 done
2025-09-17 07:50:48.669 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 6 done
2025-09-17 07:50:48.732 [http-nio-8080-exec-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.sandwich.logtracing.controller.LoginController - user Sandwich login success
2025-09-17 07:50:48.732 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 7 done
2025-09-17 07:50:48.795 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 8 done
2025-09-17 07:50:48.857 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 9 done
2025-09-17 07:50:48.921 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] c.s.logtracing.service.impl.LoginServiceImpl - processing async login for user Sandwich, login step 10 done
2025-09-17 07:50:48.983 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] com.sandwich.logtracing.util.ThreadLocalContext - traceId removed from thread local
2025-09-17 07:50:48.983 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] com.sandwich.logtracing.util.ThreadLocalContext - user id removed from thread local
2025-09-17 07:50:48.983 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] com.sandwich.logtracing.util.ThreadLocalContext - user name removed from thread local
2025-09-17 07:50:48.984 [async-thread-1] PgPzcW32QlOOVNv INFO [userId: 123456] [username: Sandwich] [clientIp: 0:0:0:0:0:0:0:1] com.sandwich.logtracing.util.ThreadLocalContext - client ip removed from thread local
由上文日志可以看到trace id: PgPzcW32QlOOVNv, 追踪的日志包括了阻塞线程和异步线程。
4.总结
本文主要是完成了用户信息的异步追踪,实现了用户信息在多线程环境下全链路可视化。如果您有什么建议,请在评论区给我留言。请关注我,接下来我们继续探讨,谢谢。
587

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



