iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >java多线程使用mdc追踪日志方式
  • 943
分享到

java多线程使用mdc追踪日志方式

2024-04-02 19:04:59 943人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录多线程使用mdc追踪日志背景解决方案实现参考多线程日志追踪1.问题描述2. 代理实现日志追踪多线程使用mdc追踪日志 背景 多线程情况下,子线程的sl4j打印日志缺少traceI

多线程使用mdc追踪日志

背景

多线程情况下,子线程的sl4j打印日志缺少traceId等信息,导致定位问题不方便

解决方案

  • 打印日志时添加用户ID、trackId等信息,缺点是每个日志都要手动添加
  • 使用mdc直接拷贝父线程值

实现


// 新建线程时:
Map<String, String> mdcContextMap = MDC.getCopyOfContextMap()
// 子线程运行时:
if(null != mdcContextMap){
    MDC.setContextMap(mdcContextMap);
}
// 销毁线程时
MDC.clear();

参考


import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;

public class MdcThreadPoolExecutor extends ThreadPoolExecutor {
    final private boolean useFixedContext;
    final private Map<String, Object> fixedContext;
    
    public static MdcThreadPoolExecutor newWithInheritedMdc(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                                                            TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(null, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
    
    @SuppressWarnings("unchecked")
    public static MdcThreadPoolExecutor newWithCurrentMdc(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                                                          TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(MDC.getCopyOfContextMap(), corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue);
    }
    
    public static MdcThreadPoolExecutor newWithFixedMdc(Map<String, Object> fixedContext, int corePoolSize,
                                                        int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                                        BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(fixedContext, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
    private MdcThreadPoolExecutor(Map<String, Object> fixedContext, int corePoolSize, int maximumPoolSize,
                                  long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.fixedContext = fixedContext;
        useFixedContext = (fixedContext != null);
    }
    @SuppressWarnings("unchecked")
    private Map<String, Object> getContextForTask() {
        return useFixedContext ? fixedContext : MDC.getCopyOfContextMap();
    }
    
    @Override
    public void execute(Runnable command) {
        super.execute(wrap(command, getContextForTask()));
    }
    public static Runnable wrap(final Runnable runnable, final Map<String, Object> context) {
        return new Runnable() {
            @Override
            public void run() {
                Map previous = MDC.getCopyOfContextMap();
                if (context == null) {
                    MDC.clear();
                } else {
                    MDC.setContextMap(context);
                }
                try {
                    runnable.run();
                } finally {
                    if (previous == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(previous);
                    }
                }
            }
        };
    }
}

多线程日志追踪

主要目的是记录工作中的一些编程思想和细节,以便后来查阅。

1.问题描述

由于项目中设计高并发内容,涉及到一个线程创建多个子线程的情况。 那么,如何跟踪日志,识别子线程是由哪个主线程创建的,属于哪个request请求。

例如, 在现有项目中,一个设备信息上传的请求(包括基本数据和异常数据两种数据),然后主线程创建两个子线程,来处理基本数据和异常数据。

简化代码如下:


public class mainApp {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                //接收到一个request
                System.out.println("[Thread-"+ Thread.currentThread().getId() +"]开始发起请求");
                String[] data = {"异常数据","基本数据"};
                //创建子线程1,处理异常数据
                MThread mThread1 = new MThread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("[Thread-"+ Thread.currentThread().getId() +"]处理了" + data[0]);
                    }
                });
                创建子线程2,处理普通数据
                MThread mThread2 = new MThread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("[Thread-"+ Thread.currentThread().getId() +"]处理了"  + data[1]);
                    }
                });
                new Thread(mThread1).start();
                new Thread(mThread2).start(); 
            }
        });
        t.start();
    }
}
 
class MThread implements Runnable { 
    private Runnable r; 
    public MThread(Runnable r) {
        this.r = r;
    }
 
    @Override
    public void run() {
        r.run();
    }
}

运行结果如下:

一个请求有三个线程,如果有多个请求,运行结果如下:

从日志中无法看出他们之间的所属关系(判断不出来他们是否是处理同一个request请求的)。如果某一个线程出现问题,我们也很难快速定位是哪个请求的处理结果。

2. 代理实现日志追踪

因此,我们使用MDC来在日志中增加traceId(同一个请求的多个线程拥有同一个traceId)。

思路如下:

1. 在request进来的时候, 利用aop为每个request创建一个traceId(保证每个request的traceId不同, 同一个request的traceId相同)

2. 创建子线程的时候, 将traceId通过动态代理的方式,传递到子线程中


public class mainApp {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //AOP 生成一个traceId
                MDC.put("traceId", UUID.randomUUID().toString().replace("-", ""));
                //接收到一个request
                System.out.println("[Thread-"+ Thread.currentThread().getId() +"]traceId["+ MDC.get("traceId") +"]开始发起请求");
                String[] data = {"异常数据","基本数据"};
 
                MThread mThread1 = new MThread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("[Thread-"+ Thread.currentThread().getId() +"]traceId["+ MDC.get("traceId") +"]处理了" + data[0]);
                    }
                }, MDC.getCopyOfContextMap());
                MThread mThread2 = new MThread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("[Thread-"+ Thread.currentThread().getId() +"]traceId["+ MDC.get("traceId") +"]处理了"  + data[1]);
                    }
                }, MDC.getCopyOfContextMap());
                new Thread(mThread1).start();
                new Thread(mThread2).start(); 
            }
        };
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}
 
class MThread implements Runnable { 
    private Runnable r; 
    public MThread(Runnable r, Map<String, String> parentThreadMap) {
        LogProxy logProxy = new LogProxy(r, parentThreadMap);
        Runnable rProxy = (Runnable) Proxy.newProxyInstance(r.getClass().getClassLoader(), r.getClass().getInterfaces(), logProxy);
        this.r = rProxy;
    }
 
    @Override
    public void run() {
        r.run();
    }
}
 
//日志代理
class LogProxy implements InvocationHandler {
    private Runnable r;
    private  Map<String, String> parentThreadMap;
    public LogProxy(Runnable r, Map<String, String> parentThreadMap) {
        this.r = r;
        this.parentThreadMap = parentThreadMap;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("run")) {
            MDC.setContextMap(parentThreadMap);
        }
        return method.invoke(r, args);
    }
}

运行结果如下:

两个请求, 同一个请求的traceId相同,不同请求的traceId不同。 完美实现多线程的日志追踪。

实际WEB项目中,只需要在logback日志配置文件中,

logging.pattern.console参数增[%X{traceId}]即可在LOGGER日志中打印traceId的信息。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: java多线程使用mdc追踪日志方式

本文链接: https://www.lsjlt.com/news/136426.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • java多线程使用mdc追踪日志方式
    目录多线程使用mdc追踪日志背景解决方案实现参考多线程日志追踪1.问题描述2. 代理实现日志追踪多线程使用mdc追踪日志 背景 多线程情况下,子线程的sl4j打印日志缺少traceI...
    99+
    2024-04-02
  • 使用log4j MDC实现日志追踪
    目录log4j MDC实现日志追踪1、新建线程处理类 ThreadContext2、添加工具类TraceUtil3、添加ContextFilter4、在webConfiguriati...
    99+
    2024-04-02
  • java 如何实现日志追踪MDC
    目录java 日志追踪MDC简单的demoMDC的介绍及使用1、MDC是什么?2、MDC的原理3、MDC的使用java 日志追踪MDC MDC ( Mapped Diagnostic...
    99+
    2024-04-02
  • SpringBoot 项目添加 MDC 日志链路追踪的执行流程
    目录1. 线程池配置2. 拦截器配置3. 日志文件配置4. 使用方法示例4.1. 异步使用4.2. 定时任务日志链路追踪的意思就是将一个标志跨线程进行传递,在一般的小项目中也就是在你...
    99+
    2024-04-02
  • 使用MDC实现日志链路跟踪
    目录1.原理2.实现3.过滤器4.logback.xml5.返回体6.效果日志前言: 在微服务环境中,我们经常使用Skywalking、CAT等去实现整体请求链路的追踪,但是这个整体...
    99+
    2024-04-02
  • 如何使用 Git 追踪日志中的二维码?
    在日常的软件开发中,Git 是一个非常常用的版本控制系统。它可以帮助我们追踪代码的变化,方便地进行协作开发。但是,如果你的项目中包含了二维码,如何才能在 Git 中追踪它们的变化呢?本文将为您介绍如何使用 Git 追踪日志中的二维码。 一...
    99+
    2023-10-06
    二维码 日志 git
  • Java gRPC拦截器如何实现分布式日志链路追踪器
    这篇文章主要介绍“Java gRPC拦截器如何实现分布式日志链路追踪器”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java gRPC拦截器如何实现分布式日志链路追踪器”文章能帮...
    99+
    2023-07-05
  • JavagRPC拦截器简单实现分布式日志链路追踪器过程详解
    目录跨进程链路追踪原理代码实现总结之前开源过一个分布式日志链路追踪的工具,其作用是规范日志格式,实现分布式日志层面的链路追踪,并且工具支持SpringMVC,Dubbo,OpenFe...
    99+
    2023-03-01
    Java gRPC拦截器 Java 分布式日志链路追踪
  • 如何利用Java日志记录系统,高效地追踪应用程序的运行状态?
    Java日志是一种非常有用的工具,它可以帮助我们追踪应用程序的运行状态,诊断问题,甚至是进行性能优化。本文将介绍如何利用Java日志记录系统,高效地追踪应用程序的运行状态。 一、为什么需要日志? 在开发应用程序时,我们通常需要保持对应用程序...
    99+
    2023-11-09
    同步 numy 日志
  • Java多线程使用方式和实现原理
    本篇内容介绍了“Java多线程使用方式和实现原理”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Java中的线程Java之父对线程的定义是:线...
    99+
    2023-06-02
  • SpringBoot 2.5.5整合轻量级的分布式日志标记追踪神器TLog的详细过程
    目录项目整合项目结构添加依赖logback-spring.xml请求类ControllerSpanId的生成规则 TLog业务标签演示示例1示例二示例三示例四示例五 随着微...
    99+
    2022-11-13
    SpringBoot 2.5.5整合轻量级 SpringBoot分布式日志标记追踪TLog
  • Java日志的使用方法
    这篇文章将为大家详细讲解有关Java日志的使用方法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3. 客户端开发;4....
    99+
    2023-06-14
  • Java多线程的创建方式
    这篇文章主要介绍“Java多线程的创建方式”,在日常操作中,相信很多人在Java多线程的创建方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java多线程的创建方式”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-20
  • Java日志记录的新方式:使用Spring和Bash
    在Java应用程序中,日志记录是非常重要的一部分。它可以帮助我们追踪应用程序的行为、问题和异常情况。传统的日志记录方式是通过Java自带的log4j、logback等框架进行记录,但是这些框架的日志记录方式有时候会比较繁琐和复杂。本文将介...
    99+
    2023-06-21
    日志 spring bash
  • C#中如何使用日志记录跟踪程序运行
    C#中如何使用日志记录跟踪程序运行,需要具体代码示例引言:在开发软件时,常常需要对程序运行情况进行跟踪和记录,以便在出现问题时能够准确找到问题所在。日志记录是一种重要的技术手段,可以记录程序的运行状态、错误信息和调试信息,以便进行异常定位和...
    99+
    2023-10-22
    跟踪 程序运行 C#: 日志记录
  • C# 使用多线程的几种方式
    在C#中,有几种方式可以使用多线程:1. 使用Thread类:可以创建一个新线程并在其中执行指定的方法。可以使用Thread类来启动...
    99+
    2023-09-15
    C#
  • log4j2使用filter过滤日志方式
    目录背景说明Filter.Result的三种过滤结果log4j2提供的过滤器(功能简述)Filter的作用范围常用过滤器使用示例(文字版代码见文末链接)StringMatchFilt...
    99+
    2024-04-02
  • Java的Future多线程模式怎么使用
    本篇内容介绍了“Java的Future多线程模式怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在Java5后,提供了大量处理多线程的...
    99+
    2023-06-17
  • java多线程之CyclicBarrier的使用方法
    java多线程之CyclicBarrier的使用方法public class CyclicBarrierTest { public static void main(String[] args) { ExecutorServi...
    99+
    2023-05-30
    java 多线程 cyclicbarrier
  • 详解Java多线程tryLock()方法使用
    tryLock(long time, TimeUnit unit) 的作用在给定等待时长内锁没有被另外的线程持有,并且当前线程也没有被中断,则获得该锁,通过该方法可以实现锁对象的限时...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作