使用MDC快速查询应用接口全部执行日志

目录
  • 引言

引言

对于每一个开发者来说,查询接口的执行日志都是一个高频率的操作,每当测试说接口有问题时,我们都需要去服务器或者日志系统上查报错的原因。

一般情况下,我们会通过对应的关键字或者接口地址去查询这个接口到底报了什么错,但是这带来一个问题,就是我们可能少打日志或者忘打某些关键字的日志,导致查询记录比较麻烦。

那么有没有一种简单高效的方法,即使我们在日志中不打印任何关键字,系统会自动生成一个关键字,让我们一次性查询出这个接口的所有log记录呢?

MDC

MDC是日志门面框架SLF4J提供的一个类,可以提供在多线程情况下记录日志的功能,log4j、logback、log4j2都有对这个类的实现。

从本质上来说,MDC可以看做一个ThreadLocal,由于其线程安全的特性,可以让我们轻松安全的保存数据。

MDC主要的API有clear()、get()、put()、remove()方法等,简洁的api让我们使用上手基本没有难度。

如何使用

1,修改日志打印格式

以日志框架logback为例,在logback.xml中,找到日志打印规则的配置,添加**-%X{reqId}** 属性,其中reqId可以任意指定,你写其他的属性也可以,博主这里演示指定为reqId。

<encoder>
  <pattern>-%X{reqId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line %msg%n</pattern>
  <charset>UTF-8</charset>
</encoder>
复制代码

2,添加过滤器MDCFilter

ps.使用拦截器也可以,效果是一样的。对每个接口做拦截。

@Component
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class MDCFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            //给每个请求接口生成一个requestId
            String requestId = RandomUtil.randomNumbers(10);
            //这里的reqId就是上面配置的,要保持一致
            MDC.put("reqId", "reqId:" + requestId);
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }

    @Override
    public void destroy() {
    }
}
复制代码

经过简单的两步就配置好了,下面我们看一下效果。

@PostMapping(value = "/mdcTest")
public ResponseEntity&lt;Object&gt; mdcTest(String id, String name) {
    log.info("测试日志打印,id={},name={}", id, name);
    log.info("测试日志打印1");
    log.info("测试日志打印2");
    log.info("测试日志打印3");
    log.info("测试日志打印4");
    return ResponseEntity.ok().build();
}
复制代码

每一行日志都有一个关键字reqId:9723829830,这样我们查询日志时只需要查询关键字9723829830就可以直接查出来这个接口所有的执行记录了。

如果想更方便的话,也可以把这个关键字直接输出到每一个接口的响应头或者响应体中。

进阶使用

对于普通的web应用我们可以直接拦截每个接口,自动生成一个请求id,那么对于微服务项目,一个接口可能会产生很多服务的调用,那如何一次性查出来所有系统内的日志呢?

对于日志的搜集本文暂不考虑,咱们先说如何做请求id的传递。

其实也很简单,当我们有多个系统间的调用时,把reqId放到request的header中进行传递,然后下游系统获取这个id就可以了。

比如下方的拦截器:

public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果有上层调用就从header中取出上层的ID
        String traceId = request.getHeader("reqId");
        if (traceId == null) {
            //如果没有,就生成一个默认的
            traceId = RandomUtil.randomNumbers(10);
        }
        MDC.put("reqId", traceId);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        //调用结束后删除
        MDC.remove("reqId");
    }
}
复制代码

MDC存在的一些问题

我们在上文说过,MDC的本质是ThreadLocal,它会把数据都绑定到当前线程上。但是当我们使用多线程的时候,就会带来一个数据丢失的问题。

所以,我们需要进行线程间的数据传递,保证MDC数据不丢失。

以线程池传递数据为例,ThreadPoolTaskExecutor提供了一个taskDecorator装饰器,通过这个属性,我们就可以实现属性的传递。

首先,定义一个MDCContextDecorator,

public class MDCContextDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable runnable) {
        Map&lt;String,String&gt; previous = MDC.getCopyOfContextMap();
        return () -&gt; {
            try {
                if (previous != null) {
                    MDC.setContextMap(previous);
                }
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}
复制代码

然后设置线程池的taskDecorator属性,

public ThreadPoolTaskExecutor executor() {
  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(5);
  executor.setMaxPoolSize(20);
  //...其他属性
  //设置线程属性的自动传递
  executor.setTaskDecorator(new MDCContextDecorator());
  return executor;
}
复制代码

最后

MDC的使用其实很简单,对于我们查询日志也很有帮助,应用也算是非常广泛了。有兴趣的同学也可以去看一下它的内部实现,代码也并不复杂。

作者:程序员拾山
链接:https://juejin.cn/post/7184381927661994039
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

以上就是使用MDC快速查询应用接口全部执行日志的详细内容,更多关于MDC查询接口执行日志的资料请关注我们其它相关文章!

(0)

相关推荐

  • 使用MDC实现日志链路跟踪

    目录 1.原理 2.实现 3.过滤器 4.logback.xml 5.返回体 6.效果日志 前言: 在微服务环境中,我们经常使用Skywalking.CAT等去实现整体请求链路的追踪,但是这个整体运维成本高,架构复杂,我们来使用MDC通过Log来实现一个轻量级的会话事务跟踪功能. 1.原理 MDC org.sl4j.MDC其实内部就是ThreadLocal,MDC提供了put/get/clear等几个核心接口,用于操作ThreadLocal中的数据:ThreadLocal中的K-V,可以在log

  • 基于slf4j日志MDC输出格式的问题

    目录 slf4j日志MDC输出格式 配置使用 原因分析 slf4j输出日志的语法 一.slf4j输出log的语法 1. 直接拼接字符串 2. 使用SLF4J的格式化功能 3. 格式化占位符的转义 4. log前做条件判断 5.打印异常堆栈 二.slf4j总结 slf4j日志MDC输出格式 配置使用 // 自动配置模板 ... <Property name="layout">%d %p %X{traceId} [%t] %c{10}:%M:%L %m%n</Proper

  • 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

  • SpringBoot+MDC实现链路调用日志的方法

    目录 1.首先介绍什么是MDC 1.1MDC作用 2.MDC结合logback的使用demo 4.多线程下MDC工具类 5.自定义线程池 6.线程池配置类 7.测试接口 8.异步的业务类 1.首先介绍什么是MDC MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j .logback及log4j2 提供的一种方便在多线程条件下记录日志的功能.MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对.MDC 中包含的内容可以被同一线程中执行的代码所访

  • 利用Spring boot+LogBack+MDC实现链路追踪

    目录 MDC介绍 API说明 MDC使用 1.拦截器 2.工具类 MDC 存在的问题 子线程日志打印丢失traceId HTTP调用丢失traceId 1.接口调用方 2.第三方服务需要添加拦截器 MDC介绍 MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j .logback及log4j2 提供的一种方便在多线程条件下记录日志的功能.MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对.MDC 中包含的内容可以被同一线程中执行的代码所访问.

  • 使用MDC快速查询应用接口全部执行日志

    目录 引言 引言 对于每一个开发者来说,查询接口的执行日志都是一个高频率的操作,每当测试说接口有问题时,我们都需要去服务器或者日志系统上查报错的原因. 一般情况下,我们会通过对应的关键字或者接口地址去查询这个接口到底报了什么错,但是这带来一个问题,就是我们可能少打日志或者忘打某些关键字的日志,导致查询记录比较麻烦. 那么有没有一种简单高效的方法,即使我们在日志中不打印任何关键字,系统会自动生成一个关键字,让我们一次性查询出这个接口的所有log记录呢? MDC MDC是日志门面框架SLF4J提供的

  • MySQL中一条SQL查询语句是如何执行的

    目录 前言 1. 处理连接 1.1 客户端和服务端的通信方式 1.1.1 TCP/IP协议 1.1.2 UNIX域套接字 1.1.3 命名管道和共享内存 1.2 权限验证 1.3 查看MySQL连接 2. 解析与优化 2.1 查询缓存 2.2 解析器 & 预处理器(Parser & Preprocessor) 2.2.1 词法解析 2.2.2 语法分析 2.2.3 预处理器 2.3 查询优化器(Optimizer)与查询执行计划 2.3.1 什么是查询优化器? 2.3.2 优化器究竟做了什

  • php在多维数组中根据键名快速查询其父键以及父键值的代码

    我这么想的: 遍历一遍多维数组,将所有的键建立索引生成一个一维数组: 每次通过键名去查这个键的上级数组及数据 OK,代码如下 indexKey创建索引数组函数: 复制代码 代码如下: <?php /** * FILE_NAME : arr.php FILE_PATH : test/ * 在多维数组中根据键名快速查询其父键以及父键值 * * @copyright Copyright (c) 2006-2010 mail:levi@cgfeel.com * @author Levi * @packa

  • php版淘宝网查询商品接口代码示例

    本文来给大家介绍一个php版淘宝网查询商品接口代码的例子,下面要改成你的信息的在代码后面都有说明了,同时sdk包我们也要官方下载. 下载SDK后直接引用包,并创建如下的类,并运行之,即完成了调用接口(taobao.user.seller.get)的过程(调用接口说明可见下载的SDK)说明:    TopClient为调用SDK的实例化类    UserSellerGetRequest为API的请求参数封装类 注:该接口是在沙箱环境下调用,获取的数据,也是沙箱中数据.若要获取线上环境,请填写自己创

  • sql server实现在多个数据库间快速查询某个表信息的方法

    本文实例讲述了sql server实现在多个数据库间快速查询某个表信息的方法.分享给大家供大家参考,具体如下: 最近出来实习,所在公司的服务器有十几个数据库,为了方便根据某个数据表的  表名  快速找到对应的数据库,又复习了一下游标的知识,写了下面这个sql代码,方便自己的工作. 1.先了解一下系统存储过程和系统表的使用,简单介绍一下我用到的几个系统存储过程(资料参考网络) use master --切换到系统数据库,因为下面用到的系统存储过程和系统表大部分存在于该数据库 go exec sp_

  • 关于快速测试API接口的一个新技能

    前言 我们大家在日常开发过程中,或多或少都涉及到 API 接口的测试.例如,有的小伙伴使用 Chrome 的 Postman 插件,或者使用火狐的 restclient 等工具.事实上,这些工具是测试 API 接口非常有效的方式之一,笔者之前也一直使用 Postman 完成 API 接口的测试工作.今天,笔者推荐另外一个非常好用的小工具,能够帮助读者快速测试 API 接口.这个工具就是 IDEA 的 Editor REST Client. IDEA 的 Editor REST Client 在

  • 分析mysql中一条SQL查询语句是如何执行的

    目录 一.MySQL 逻辑架构概览 二.连接器(Connector) 三.查询缓存(Query Cache) 四.解析器(Parser) 五.优化器(Optimizer) 六.执行器 七.小结 一.MySQL 逻辑架构概览 MySQL 最重要.最与众不同的特性就是它的可插拔存储引擎架构(pluggable storage engine architecture),这种架构的设计将查询处理及其他系统任务和数据的存储/提取分离开来.来看官网的解释: The MySQL pluggable stora

  • Mysql中一千万条数据怎么快速查询

    目录 普通分页查询 如何优化 偏移量大 采用id限定方式 优化数据量大问题 普通分页查询 当我们在日常工作中遇到大数据查询的时候,第一反应就是使用分页查询. mysql支持limit语句来选取指定的条数数据,而oracle可以使用rownum来选取 mysql分页查询语句如下: SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset 第一个参数用来指定第一个返回记录行的偏移量 第二个参数指定返回记录行的最大数目 当相同的偏移量时

  • MySQL中explain使用快速查询手册

    目录 一. 前言 二 . explain 使用 三. 业务实践 3.1 以 user_id 为条件进行查找的思路 3.2 以更新时间为查询条件 3.3 简单优化通过 orgId 进行切割 3.4 多索引条件的抉择 3.5 连表查询的关注点 四. 深入问题 4.1 explain 的结果能作为最终决策吗? 总结 参考 : 一. 前言 上一篇整理完了 MySQL 的性能优化方式 , 其中最常用的就是 explain . 这一篇来详细看看 explain 中各个参数的含义和扩展 , 整理出来便于使用时

  • 查询Oracle中正在执行和执行过的SQL语句

    查询Oracle正在执行的sql语句及执行该语句的用户 SELECT b.sid oracleID, b.username 登录Oracle用户名, b.serial#, spid 操作系统ID, paddr, sql_text 正在执行的SQL, b.machine 计算机名 FROM v$process a, v$session b, v$sqlarea c WHERE a.addr = b.paddr AND b.sql_hash_value = c.hash_value 其它网友给出的正

随机推荐