全局请求添加TraceId轻松看日志

目录
  • 引言
  • 请求拦截器
  • 统一返回值
  • 日志配置
  • 测试
  • 异步调用配置

引言

不知道大家有没有一堆日志就是定位不到那块是异常部分,接口错误无法复现,也找不到报错信息等比较棘手的问题。

其实解决上面的问题很简单,只要我们为每一个请求都分配一个唯一的 RequestId 或者叫 TraceId ,一旦出了问题,只需要拿着 Id 去日志里一搜,妖魔鬼怪立马原形毕露。

对于分布式链路追踪,有很多开源中间件,本文主要通过 logback 的 MDC 实现。

请求拦截器

@Component
public class TraceIdInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String traceId = String.format("%s - %s",request.getRequestURI(), UUID.fastUUID().toString(true));
        MDC.put("traceId", traceId);
        return true;
 }

注册拦截器

@Component
@RequiredArgsConstructor
public class GlobalWebMvcConfigurer implements WebMvcConfigurer {
    private final TraceIdInterceptor traceIdInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(traceIdInterceptor);
    }
}

统一返回值

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResponse<T> implements Serializable {
    /** 错误码 */
    private Integer code;
    /** 错误消息 */
    private String message;
    /** 泛型响应数据 */
    private T Data;
    private String traceId;
    public CommonResponse(Integer code, String message) {
        this.code = code;
        this.message = message;
        this.traceId = MDC.get("traceId");
    }
}

日志配置

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATTERN"
              value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%highlight(%-5level)] [%boldYellow(%X{traceId})] [%boldYellow(%thread)] %boldGreen(%logger{36} %F.%L) %msg%n">
    </property>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <!-- 控制台打印INFO及以上级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
    </appender>
    <root>
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

测试

接口返回值

{
  "code": 0,
  "message": "",
  "traceId": "/commerce-user/user/findById - 73e470120ef24d57a111de6b671d030b",
  "data": {
    "id": 1,
    "username": "test1",
    "password": "test",
    "extraInfo": "{}",
    "createTime": "2022-06-20T09:23:18.000+00:00",
    "updateTime": "2022-06-20T09:23:18.000+00:00",
    "balance": 0
  }
}

日志

如此,如果线上有接口出现问题,拿着 traceId 到日志文件搜索,就能检索到该请求的日志调用链路,处理问题就变得简单了。

异步调用配置

因为是 ThreadLocal ,异步任务必然是获取不到 traceId 的,需要再线程池配置中手动添加。

线程池配置

@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(20);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("user-async-");
        // 等待所有任务结果候再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        // 定义拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置线程装饰器
        executor.setTaskDecorator(runnable -> ThreadMdcUtils.wrapAsync(runnable, MDC.getCopyOfContextMap()));
        // 初始化线程池, 初始化 core 线程
        executor.initialize();
        return executor;
    }
}
public class ThreadMdcUtils {
    public static Runnable wrapAsync(Runnable task, Map<String,String> context){
        return () -> {
            if(context==null){
                MDC.clear();
            }else {
                MDC.setContextMap(context);
            }
            if(MDC.get("traceId")==null){
                MDC.put("traceId", UUID.fastUUID().toString(true));
            }
            try {
                task.run();
            }finally {
                MDC.clear();
            }
        };
    }
}

再次测试

以上就是全局请求添加TraceId轻松看日志的详细内容,更多关于全局请求添加TraceId的资料请关注我们其它相关文章!

(0)

相关推荐

  • dubbo集成zipkin获取Traceid的实现

    dubbo集成zipkin获取Traceid dubbo集成zipkin有java的客户端brave,实现原理的通过定义Filter来实现的,然后通过RpcContext.getContext().getAttachments()来获取,通过RpcContext.getContext()来传递. 按照dubbo官网的博客配置了这个MDCScopeDecorator应该就可以做在日志里面使用[%X{traceId}/%X{spanId}来打印了,但是我的一致打印不出来,不知道为啥,然后就想了一个折

  • Springboot+MDC+traceId日志中打印唯一traceId

    目录 1. 为什么需要这个traceId 2.通过MDC设置traceId 2.1 使用filter过滤器设置traceId 2.2 使用JWT token过滤器的项目 2.3 使用Interceptor拦截器设置traceId 3.logback.xml中配置traceId 4.补充异步方法带入上下文的traceId 5.在接口放回中,增加traceId返回 先看一张图: 有同学问:日志中[]中类似uuid的这个traceId是怎么实现的,这边文章就介绍下如何在springboot工程下用MD

  • Sleuth+logback 设置traceid 及自定义信息方式

    Sleuth+logback 设置traceid及自定义信息 背景: 分布式系统中,如何快速定位某个用户的请求日志? 使用Sleuth生成的traceid可以跟踪某个请求,但是很多时候我们需要知道traceid 与某个用户的映射关系,方便定位某个用户的日志 方案: @Component @Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1) public class CustomHttpSpanExtractor ext

  • Node绑定全局TraceID的实现方法

    问题描述 由于Node.js的 单线程模型 的限制,我们无法设置全局 traceid 来聚合请求,即 实现输出日志与请求的绑定 .如果不实现日志和请求的绑定,我们难以判断日志输出与对应用户请求的对应关系,这对 线上问题排查 带来了困难. 例如,在用户访问 retrieveOne API 时,其会调用 retrieveOneSub 函数,如果我们想在 retrieveOneSub 函数中输出当前请求对应的学生信息,是繁琐的.在 course-se 现有实现下,我们针对此问题的解决方法是: 方案1:

  • 全局请求添加TraceId轻松看日志

    目录 引言 请求拦截器 统一返回值 日志配置 测试 异步调用配置 引言 不知道大家有没有一堆日志就是定位不到那块是异常部分,接口错误无法复现,也找不到报错信息等比较棘手的问题. 其实解决上面的问题很简单,只要我们为每一个请求都分配一个唯一的 RequestId 或者叫 TraceId ,一旦出了问题,只需要拿着 Id 去日志里一搜,妖魔鬼怪立马原形毕露. 对于分布式链路追踪,有很多开源中间件,本文主要通过 logback 的 MDC 实现. 请求拦截器 @Component public cla

  • Vue全局监测错误并生成错误日志实现方法介绍

    目录 一.准备工作 (1)规定错误码 (2)设置错误处理函数 (3)保存错误日志 二.监听错误 (1)JS错误与静态资源加载错误 (2)Vue逻辑错误 (3)请求错误与Promise错误 三.效果演示 四.完整代码 一.准备工作 (1)规定错误码 像是请求码(404.500)一样,我觉得错误都应该规定好对应的错误码.个人喜好. // 错误代码 const errCode = new Map([ // 本地系统错误 ['E1001', '系统未知错误'], ['E1002', 'vue逻辑错误']

  • Spring Boot 搭建 ELK正确看日志的配置流程

    为什么要用ELK ELK实际上是三个工具,Elastricsearch + Logstash + Kibana,通过ELK,用来收集日志还有进行日志分析,最后通过可视化UI进行展示.一开始业务量比较小的时候,通过简单的SLF4J+Logger在服务器打印日志,通过grep进行简单查询,但是随着业务量增加,数据量也会不断增加,所以使用ELK可以进行大数量的日志收集和分析 简单画了一下架构图 在环境配置中,主要介绍Mac和Linux配置,Windows系统大致相同,当然,前提是大家都安装了JDK 1

  • C++软件添加dump调试打印日志(推荐)

    C++软件添加dump调试打印日志(推荐) #include <DbgHelp.h> #pragma comment(lib, "dbghelp.lib") LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) { //cout << "Enter TopLevelExceptionFilter Function" << en

  • axios全局请求参数设置,请求及返回拦截器的方法

    应用场景: 1,每个请求都带上的参数,比如token,时间戳等. 2,对返回的状态进行判断,比如token是否过期 代码如下: axios.interceptors.request.use( config => { var xtoken = getXtoken() if(xtoken != null){ config.headers['X-Token'] = xtoken } if(config.method=='post'){ config.data = { ...config.data, _

  • 微信小程序设置全局请求URL及封装wx.request请求操作示例

    本文实例讲述了微信小程序设置全局请求URL及封装wx.request请求操作.分享给大家供大家参考,具体如下: app.js: App({ //设置全局请求URL globalData:{ URL: 'https://www.oyhdo.com', }, /** * 封装wx.request请求 * method: 请求方式 * url: 请求地址 * data: 要传递的参数 * callback: 请求成功回调函数 * errFun: 请求失败回调函数 **/ wxRequest(metho

  • 为jquery的ajax请求添加超时timeout时间的操作方法

    下面给大家介绍为jquery的ajax请求添加超时timeout时间的实例 有时侯要用ajax来轮询某个服务是否可用,但是各个浏览器ajax的超时时间有可能不一样,所以希望ajax能只尝试几秒钟,然后隔几秒再次发送一次ajax检查一次.可以用timeout属性. var checkLoading = function(timer) { //先延时再获取状态,否则立即获取可能重启前的服务还没有关闭 setTimeout(function() { $.ajax({ url: '/onceos/ver

  • IntelliJ IDEA远程Debug Linux的Java程序,找问题不要只会看日志了(推荐)

    1 前言 我们习惯于在本地开发的时候debug,能快速定位与解决问题,那部署在服务器上是不是就没有办法了呢?只能通过查看日志来定位? 不是的,在远端的服务器上,我们一样可以debug. 2 IDEA的debug 我们先来看一下在IntelliJ IDEA直接debug是怎样的. 先准备一个简单的Java程序: package com.pkslow.basic; import java.util.Map; public class RemoteDebug { public static void

  • swagger中如何给请求添加header

    目录 如何给请求添加header 1.后台代码如下 2.添加后的swagger中接口样式如下 swagger添加头部参数 1.代码截图 2.使用代码 3.swagger文档说明截图显示 4.swagger文档调试显示框 如何给请求添加header 背景:在集成了swagger的项目中,调用后台接口往往会经过一些自定义的拦截器,而拦截器加了token限制的话,直接在swagger页面中请求后台接口会调不通,那么就需要在swagger中也设置请求token 1.后台代码如下 required(fal

  • 详解SpringBoot中异步请求和异步调用(看完这一篇就够了)

    一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应.一句话:增加了服务器对客户端请求的吞吐量(实际生产上我们用的比较少,如果并发请求量很大的情况下,我们会通过nginx把请求负载到集群服务的各个节点上来分摊请求压力,当然还可以通过消息队列来做请求的缓冲). 2.异步请求的实现 方式一:Servlet方式实现异步请求

随机推荐