首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot项目traceId生成_日志打印

SpringBoot项目traceId生成_日志打印

原创
作者头像
花落花相惜
修改于 2021-11-21 06:32:21
修改于 2021-11-21 06:32:21
2.2K0
举报

前言

查看服务日志时,当服务被调过于频繁,日志刷新太快,会影响到联调、测试、线上问题的排查效率,能不能为每一个请求的日志打一个唯一标识呢?后面使用该表示去匹配,直接检索出该请求的日志?引入本文的正题,“traceId”。

image

MDC

MDC定义 Mapped Diagnostic Context,即:映射诊断环境。

MDC是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。

MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。

MDC的使用方法

向MDC设置值:MDC.put(key, value);

从MDC中取值:MDC.get(key);

将MDC中的内容打印到日志中:%X{key};

初始化TraceId并向MDC设置值

这里主要是利用切面,方法执行前设置MDC,方法执行后擦除MDC。具体实现方式有很多,如过滤器、拦截器、AOP等等。个人比较推荐Filter实现,因为Filter是请求最先碰到的,也是响应给前端前最后一个碰到的。

过滤器实现

代码语言:txt
AI代码解释
复制
@Slf4j
代码语言:txt
AI代码解释
复制
@WebFilter(filterName = "traceIdFilter", urlPatterns = "/*")
代码语言:txt
AI代码解释
复制
@Order(0)
代码语言:txt
AI代码解释
复制
@Component
代码语言:txt
AI代码解释
复制
public class TraceIdFilter implements Filter {
代码语言:txt
AI代码解释
复制
    /**
代码语言:txt
AI代码解释
复制
     * 日志跟踪标识
     */
    public static final String TRACE_ID = "TRACE_ID";
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void init(FilterConfig filterConfig) {
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
代码语言:txt
AI代码解释
复制
            throws IOException, ServletException {
代码语言:txt
AI代码解释
复制
        MDC.put(TRACE_ID, UUID.randomUUID().toString());
代码语言:txt
AI代码解释
复制
        filterChain.doFilter(servletRequest, servletResponse);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void destroy() {
代码语言:txt
AI代码解释
复制
        MDC.clear();
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}

拦截器实现

代码语言:txt
AI代码解释
复制
@Component
代码语言:txt
AI代码解释
复制
public class TraceIdInterceptor extends HandlerInterceptorAdapter {
代码语言:txt
AI代码解释
复制
    private static final String TRACE_ID = "TRACE_ID";
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
代码语言:txt
AI代码解释
复制
        MDC.put(TRACE_ID, UUID.randomUUID().toString());
代码语言:txt
AI代码解释
复制
        return true;
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) {
代码语言:txt
AI代码解释
复制
        MDC.clear();
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
@Configuration
代码语言:txt
AI代码解释
复制
public class WebConfig implements WebMvcConfigurer {
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void addInterceptors(InterceptorRegistry registry) {
代码语言:txt
AI代码解释
复制
        registry.addInterceptor(new TraceIdInterceptor());
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}

日志打印配置pattern中配置traceId

与之前的相比只是添加了%X{TRACE_ID}, %X{***}是一个模板,中间属性名是我们使用MDC put进去的。

代码语言:txt
AI代码解释
复制
%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - [%X{TRACE_ID}] - %msg%n

异步方法的日志打印traceId

异步方法会开启一个新线程,我们想要是异步方法和主线程共用同一个traceId,首先先新建一个任务适配器MdcTaskDecorator。

代码语言:txt
AI代码解释
复制
public class MdcTaskDecorator implements TaskDecorator {
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public Runnable decorate(Runnable runnable) {
代码语言:txt
AI代码解释
复制
        Map<String, String> map = MDC.getCopyOfContextMap();
代码语言:txt
AI代码解释
复制
        return () -> {
代码语言:txt
AI代码解释
复制
            try {
代码语言:txt
AI代码解释
复制
                MDC.setContextMap(map);
代码语言:txt
AI代码解释
复制
                String traceId = MDC.get(TRACE_ID);
代码语言:txt
AI代码解释
复制
                if (StringUtils.isBlank(traceId)) {
代码语言:txt
AI代码解释
复制
                    traceId = UUID.randomUUID().toString();
代码语言:txt
AI代码解释
复制
                    MDC.put(TRACE_ID, traceId);
代码语言:txt
AI代码解释
复制
                }
代码语言:txt
AI代码解释
复制
                runnable.run();
代码语言:txt
AI代码解释
复制
            } finally {
代码语言:txt
AI代码解释
复制
                MDC.clear();
代码语言:txt
AI代码解释
复制
            }
代码语言:txt
AI代码解释
复制
        };
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}

在线程池配置中增加executor.setTaskDecorator(new MdcTaskDecorator())的设置

代码语言:txt
AI代码解释
复制
@EnableAsync
代码语言:txt
AI代码解释
复制
@Configuration
代码语言:txt
AI代码解释
复制
public class ThreadPoolConfig {
代码语言:txt
AI代码解释
复制
    private int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
代码语言:txt
AI代码解释
复制
    private int maxPoolSize = corePoolSize * 2;
代码语言:txt
AI代码解释
复制
    private static final int queueCapacity = 50;
代码语言:txt
AI代码解释
复制
    private static final int keepAliveSeconds = 30;
代码语言:txt
AI代码解释
复制
    @Bean(name = "threadPoolTaskExecutor")
代码语言:txt
AI代码解释
复制
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
代码语言:txt
AI代码解释
复制
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
代码语言:txt
AI代码解释
复制
        executor.setMaxPoolSize(maxPoolSize);
代码语言:txt
AI代码解释
复制
        executor.setCorePoolSize(corePoolSize);
代码语言:txt
AI代码解释
复制
        executor.setQueueCapacity(queueCapacity);
代码语言:txt
AI代码解释
复制
        executor.setKeepAliveSeconds(keepAliveSeconds);
代码语言:txt
AI代码解释
复制
        executor.setTaskDecorator(new MdcTaskDecorator());
代码语言:txt
AI代码解释
复制
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
代码语言:txt
AI代码解释
复制
        return executor;
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}

使用指定的线程池执行业务代码

代码语言:txt
AI代码解释
复制
@Async("threadPoolTaskExecutor")
代码语言:txt
AI代码解释
复制
public void testThreadPoolTaskExecutor(){
代码语言:txt
AI代码解释
复制
    log.info("Async 测试一下");
代码语言:txt
AI代码解释
复制
}

在响应DTO中返回traceId

代码语言:txt
AI代码解释
复制
@Data
代码语言:txt
AI代码解释
复制
@AllArgsConstructor
代码语言:txt
AI代码解释
复制
public class RetResult<T> {
代码语言:txt
AI代码解释
复制
    private Integer code;
代码语言:txt
AI代码解释
复制
    private String msg;
代码语言:txt
AI代码解释
复制
    private T data;
代码语言:txt
AI代码解释
复制
    private String traceId;
代码语言:txt
AI代码解释
复制
    public RetResult(Integer code, String msg, T data) {
代码语言:txt
AI代码解释
复制
        this.code = code;
代码语言:txt
AI代码解释
复制
        this.msg = msg;
代码语言:txt
AI代码解释
复制
        this.data = data;
代码语言:txt
AI代码解释
复制
        this.traceId = MDC.get(TRACE_ID);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    public static <T> RetResult<T> success(T t) {
代码语言:txt
AI代码解释
复制
        return new RetResult<>(RetCode.SUCCESS.getCode(), "ok", t);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    public static <T> RetResult<T> success() {
代码语言:txt
AI代码解释
复制
        return new RetResult<>(RetCode.SUCCESS.getCode(), "ok", null);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    public static <T> RetResult<T> fail() {
代码语言:txt
AI代码解释
复制
        return new RetResult<>(RetCode.FAIL.getCode(), "fail", null);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    public static <T> RetResult<T> fail(String msg) {
代码语言:txt
AI代码解释
复制
        return new RetResult<>(RetCode.FAIL.getCode(), msg, null);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    public static <T> RetResult<T> fail(Integer code, String msg) {
代码语言:txt
AI代码解释
复制
        return new RetResult<>(code, msg, null);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
暂无评论
推荐阅读
『中级篇』RoutingMesh之Ingress负载均衡(48)
PS:负载均衡解决了单一入口负载到多个容器上问题, 但是由于容器调度之后可能落到多个机器上, 假如某些主机上面没有工作的容器,而对外服务时候又希望服务可以被访问, Routing Mesh概念引入是解决多个入口点负载到单个容器的问题。
IT架构圈
2018/08/16
3920
『中级篇』RoutingMesh之Ingress负载均衡(48)
Docker Swarm 服务发现和负载均衡原理
Docker 使用了 Linux 内核 iptables 和 IPVS 的功能来实现服务发现和负载均衡。
用户1257215
2018/07/27
3.3K0
Docker Swarm 服务发现和负载均衡原理
『中级篇』集群服务间通信之RoutingMesh(47)
PS:内部负载均衡 当在docker swarm集群模式下创建一个服务时,会自动在服务所属的网络上给服务额外的分配一个虚拟IP,当解析服务名字时就会返回这个虚拟IP。对虚拟IP的请求会通过overlay网络自动的负载到这个服务所有的健康任务上。这个方式也避免了客户端的负载均衡,因为只有单独的一个虚拟IP会返回到客户端,docker会处理虚拟IP到具体任务的路由,并把请求平均的分配给所有的健康任务。
IT架构圈
2018/08/16
5110
『中级篇』集群服务间通信之RoutingMesh(47)
『中级篇』集群服务间通信之RoutingMesh(47)
机器进行迁移的时候有一套map<k,v>关系,虚拟ip 和实际的ip 有个对应的关系,
IT架构圈
2018/08/11
4280
SWARM大法好——Docker1.12 引擎使用体验
背景 凭借敏捷开发部署理念的推行,相信对于很多人来说docker这项容器技术已经并不陌生,Docker 1.12引擎发布了快两个月,新引擎中包含了许多特性。诸如: Swarm模式,容器集群的健康检查,节点的身份加密,docker Service API调用,容器启动的过滤匹配方式(constraint), docker的内建路由,以及支持在多平台系统上运行docker(MAC、Windows、AWS、AZURE),以及一些插件升级等等. 特性之多,就连Docker 自己的产品经理也表示这次的新版本可能是公司
小小科
2018/05/04
1.3K0
SWARM大法好——Docker1.12 引擎使用体验
K8s网络模型
k8s网络模型设计基础原则:每个Pod都拥有一个独立的 IP地址,而且 假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中 。 所以不管它们是否运行在同 一 个 Node (宿主机)中,都要求它们可以直接通过对方的 IP 进行访问。设计这个原则的原因 是,用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑将容器端口映射到主机端口等问题。
加多
2019/04/18
3.9K2
K8s网络模型
kubernetes service 原理解析
在 kubernetes 中,当创建带有多个副本的 deployment 时,kubernetes 会创建出多个 pod,此时即一个服务后端有多个容器,那么在 kubernetes 中负载均衡怎么做,容器漂移后 ip 也会发生变化,如何做服务发现以及会话保持?这就是 service 的作用,service 是一组具有相同 label pod 集合的抽象,集群内外的各个服务可以通过 service 进行互相通信,当创建一个 service 对象时也会对应创建一个 endpoint 对象,endpoint 是用来做容器发现的,service 只是将多个 pod 进行关联,实际的路由转发都是由 kubernetes 中的 kube-proxy 组件来实现,因此,service 必须结合 kube-proxy 使用,kube-proxy 组件可以运行在 kubernetes 集群中的每一个节点上也可以只运行在单独的几个节点上,其会根据 service 和 endpoints 的变动来改变节点上 iptables 或者 ipvs 中保存的路由规则。
田飞雨
2019/12/19
5830
一文为你图解 Kubernetes 网络通信原理
1、网络的命名空间:Linux 在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命名空间中,彼此间无法通信;Docker 利用这一特性,实现不容器间的网络隔离。
我的小碗汤
2022/01/14
2.5K0
一文为你图解 Kubernetes 网络通信原理
kubernetes service 原理解析
在 kubernetes 中,当创建带有多个副本的 deployment 时,kubernetes 会创建出多个 pod,此时即一个服务后端有多个容器,那么在 kubernetes 中负载均衡怎么做,容器漂移后 ip 也会发生变化,如何做服务发现以及会话保持?这就是 service 的作用,service 是一组具有相同 label pod 集合的抽象,集群内外的各个服务可以通过 service 进行互相通信,当创建一个 service 对象时也会对应创建一个 endpoint 对象,endpoint 是用来做容器发现的,service 只是将多个 pod 进行关联,实际的路由转发都是由 kubernetes 中的 kube-proxy 组件来实现,因此,service 必须结合 kube-proxy 使用,kube-proxy 组件可以运行在 kubernetes 集群中的每一个节点上也可以只运行在单独的几个节点上,其会根据 service 和 endpoints 的变动来改变节点上 iptables 或者 ipvs 中保存的路由规则。
田飞雨
2019/12/15
1.6K0
kubernetes service 原理解析
干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇
OpenShift的OVS网络组件有三种模式:ovs-subnet、ovs-multitenant、ovs-networkpolicy。
魏新宇
2018/07/30
2.1K0
干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇
《Kubernetes》,你需要掌握的 Service 和 Ingress
k8s 我们已经从 NameSpace、Pod、PodController到Volumn都介绍过了,相信看完的小伙伴们也会很有收获的~那么今天我们继续来到k8s的课堂,这节我们将要来说下 k8S 搭建完服务后如何访问!
潜行前行
2021/06/25
1.4K0
《Kubernetes》,你需要掌握的 Service 和 Ingress
.Net微服务实战之负载均衡(下)
相关源码:https://github.com/SkyChenSky/Sikiro
陈珙
2021/01/21
6280
Kubernetes 网络模型基础指南
Kubernetes 是为运行分布式集群而建立的,分布式系统的本质使得网络成为 Kubernetes 的核心和必要组成部分,了解 Kubernetes 网络模型可以使你能够正确运行、监控和排查应用程序故障。
我是阳明
2022/05/22
1.1K0
Kubernetes 网络模型基础指南
如何在集群的负载均衡过程保留请求源IP
应用部署不一定总是简单的安装和运行, 有时候还需要考虑网络的问题. 本文将介绍如何在k8s集群中使服务能获取到请求的源IP.
jqknono
2024/05/28
3190
Kubernetes | Service - Ingress
Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略——通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector。
Zkeq
2023/05/11
6700
Kubernetes | Service - Ingress
Getting Started and Beyond|云原生应用负载均衡选型指南
冉昕,腾讯云服务网格TCM产品经理,现负责云原生流量接入网关与应用通信可观测性等产品特性策划与设计工作。
腾讯云原生
2021/05/12
1.1K0
实例解读Docker Swarm
① docker-compose是docker引擎之外的容器编排工具(Python实现),需要单独安装;docker stack 是docker引擎原生支持的容器编排技术(Go实现)
有态度的马甲
2020/04/15
1.6K0
强大的负载均衡策略:Kubernetes Gateway API
第41集:从ClusterIP到Ingress和Gateway API。探索Kubernetes中最常见的服务负载均衡策略。
云云众生s
2025/01/27
2250
强大的负载均衡策略:Kubernetes Gateway API
Docker Swarm 零基础入门
Docker Swarm 是 Docker 官方项目之一,提供 Docker 容器集群服务,是 Docker 官方对容器云生态进行支持的核心方案。使用它,用户可以将多个 Docker 主机封装为单个大型的虚拟 Docker 主机,快速打造一套容器云平台。
羽月
2022/10/08
1.3K0
Docker Swarm 零基础入门
Docker实践(六):Docker Swarm
默认情况下管理节点也是work节点,如果希望管理节点专用,即不作为work节点,可以使用‘--availability’参数:
loong576
2019/09/10
1.2K0
Docker实践(六):Docker Swarm
相关推荐
『中级篇』RoutingMesh之Ingress负载均衡(48)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档