失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > logback mdc日志跟踪

logback mdc日志跟踪

时间:2023-03-24 16:27:07

相关推荐

logback mdc日志跟踪

1、简介

MDC(Mapped Diagnostic Context,映射调试上下文)是log4jlogbacklog4j2提供的一种方便在多线程条件下记录日志的功能。MDC可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问

当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

2、API说明

clear() :移除所有MDCget (String key) :获取当前线程MDC中指定key的值getContext() :获取当前线程MDC的MDCput(String key, Object o) :往当前线程的MDC中存入指定的键值对remove(String key) :删除当前线程MDC中指定的键值对

3、作用

帮助开发快速定位日志位置。

用户请求日志关联项目间请求日志关联多服务间日志聚合调用关系分析日志分析

4、要求与实现

4.1 要求

一次请求生成一次RequestId,并且RequestId唯一一次请求响应给前端,都需要返回RequestId字段,接口正常、业务异常、系统异常,都需要返回该字段一次请求在控制台或者日志文件打印的日志,都需要显示RequestId一次请求的入参和出参都需要打印对于异步操作,需要在异步线程的日志同样显示RequestId

4.2 实现

4.2.1 接口

前端或上一个服务节点调用当前服务节点时。

header中获取RequestId,如果不存在,说明是前端,不是上一个服务节点,就生成一个RequestId。将RequestId放到header中,前端或下个服务节点可以获取到RequestId。将RequestId放入MDC中,日志中打印RequestId。

代码实现:

package com.ybwei.log.mdc.demo.interceptor;import com.fasterxml.jackson.databind.ObjectMapper;import com.ybwei.log.mdc.demo.constant.MDCConstant;import com.ybwei.log.mdc.demo.util.MyStringUtils;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.extern.slf4j.Slf4j;import mons.lang3.StringUtils;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.slf4j.MDC;import org.springframework.beans.factory.annotation.Autowired;import org.ponent;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** 接口日志** @author ybwei* @date /2/17 11:40**/@Aspect@Component@Slf4jpublic class WebLogAspect {private final ObjectMapper mapper;ThreadLocal<LogRecord> logRecordThreadLocal = new ThreadLocal<>();@Autowiredpublic WebLogAspect(ObjectMapper mapper) {this.mapper = mapper;}@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")public void requestLog() {}@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")public void postLog() {}@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")public void getLog() {}/*** 请求参数** @param joinPoint* @return void* @throws* @methodName: doBefore* @author ybwei* @date /2/17 13:54*/@Before("requestLog() || postLog() || getLog()")public void doBefore(JoinPoint joinPoint) {//1、获取requestIdString requestId = getRequestId();//2、往当前线程的MDC中存入指定的键值对MDC.put(MDCConstant.REQUEST_ID, requestId);//3、返回值设置requestIdServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletResponse httpServletResponse = sra.getResponse();httpServletResponse.setHeader(MDCConstant.REQUEST_ID, requestId);//4、打印参数日志//4.1 接口地址String name = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();logRecordThreadLocal.set(new LogRecord(System.currentTimeMillis(), name));for (Object object : joinPoint.getArgs()) {if (object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse) {continue;}try {log.info("请求接口:{},请求参数 :{}", name, mapper.writeValueAsString(object));} catch (Exception e) {log.info("打印请求参数错误:", e);}}}/*** 获取requestId** @methodName: getRequestId* @return: java.lang.String* @author: geoffrey* @date: /5/10**/private String getRequestId() {//1、从header中获取requestIdServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest httpServletRequest = sra.getRequest();String requestId = httpServletRequest.getHeader(MDCConstant.REQUEST_ID);if (StringUtils.isNotBlank(requestId)) {return requestId;}return MyStringUtils.generateUUIDNoCenterLine();}/*** 返回参数** @param response* @return void* @throws* @methodName: doAfterReturning* @author ybwei* @date /2/17 13:54*/@AfterReturning(returning = "response", pointcut = "requestLog() || postLog() || getLog()")public void doAfterReturning(Object response) {//1、打印日志LogRecord logRecord = logRecordThreadLocal.get();log.info("接口所花费时间{}毫秒,接口:{}", System.currentTimeMillis() - logRecord.getStartTime(), logRecord.getName());logRecordThreadLocal.remove();if (response != null) {try {log.info("返回参数:{}", mapper.writeValueAsString(response));} catch (Exception e) {log.info("打印返回参数错误:", e);}}//2、删除当前线程MDC中指定的键值对MDC.remove(MDCConstant.REQUEST_ID);}@AllArgsConstructor@Getterclass LogRecord {/*** 开始时间** @author: ybwei* @date: /3/22 9:53*/private Long startTime;/*** 接口地址** @author: ybwei* @date: /3/22 9:55*/private String name;}}

4.2.2 异步和定时任务

配置线程池

package com.ybwei.log.mdc.demo.config.thread;import com.ybwei.log.mdc.demo.constant.MDCConstant;import com.ybwei.log.mdc.demo.util.MyStringUtils;import lombok.extern.slf4j.Slf4j;import org.slf4j.MDC;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.Map;import java.util.concurrent.Callable;import java.util.concurrent.Future;/*** MDC线程池* 实现内容传递** @author geoffrey* @version V1.0* @className MdcTaskExecutor* @date /5/9**/@Slf4jpublic class MdcTaskExecutor extends ThreadPoolTaskExecutor {@Overridepublic <T> Future<T> submit(Callable<T> task) {log.info("mdc thread pool task executor submit");Map<String, String> context = MDC.getCopyOfContextMap();return super.submit(() -> {T result;if (context != null) {//将父线程的MDC内容传给子线程MDC.setContextMap(context);} else {//直接给子线程设置MDCMDC.put(MDCConstant.REQUEST_ID, MyStringUtils.generateUUIDNoCenterLine());}try {//执行任务result = task.call();} finally {try {MDC.clear();} catch (Exception e) {log.warn("MDC clear exception", e);}}return result;});}@Overridepublic void execute(Runnable task) {log.info("mdc thread pool task executor execute");Map<String, String> context = MDC.getCopyOfContextMap();super.execute(() -> {if (context != null) {//将父线程的MDC内容传给子线程MDC.setContextMap(context);} else {//直接给子线程设置MDCMDC.put(MDCConstant.REQUEST_ID, MyStringUtils.generateUUIDNoCenterLine());}try {//执行任务task.run();} finally {try {MDC.clear();} catch (Exception e) {log.warn("MDC clear exception", e);}}});}}

异步方法

添加@Async("commonThreadPool")

@Async("commonThreadPool")public void async() {log.info("异步处理");}

定时任务

@Async(value = "scheduleThreadPool")@Scheduled(fixedRate = 2000)public void fixedRate() {log.info("定时间隔任务 fixedRate = {}", LocalDateTime.now());}

4.3 调用第三方日志

实现RestTemplate拦截器

package com.ybwei.log.mdc.demo.interceptor;import com.ybwei.log.mdc.demo.constant.MDCConstant;import org.slf4j.MDC;import org.springframework.http.HttpRequest;import org.springframework.http.client.ClientHttpRequestExecution;import org.springframework.http.client.ClientHttpRequestInterceptor;import org.springframework.http.client.ClientHttpResponse;import org.ponent;import java.io.IOException;/*** @author geoffrey* @version V1.0* @className RestTemplateTraceIdInterceptor* @date /5/10**/public class RestTemplateTraceIdInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {String traceId = MDC.get(MDCConstant.REQUEST_ID);if (traceId != null) {request.getHeaders().add(MDCConstant.REQUEST_ID, traceId);}return execution.execute(request, body);}}

为RestTemplate添加拦截器:

@Beanpublic RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {// boot中可使用RestTemplateBuilder.build创建RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(clientHttpRequestFactory));// 配置请求工厂// restTemplate.setRequestFactory(clientHttpRequestFactory);List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();interceptors.add(new LoggingRequestInterceptor());restTemplate.setInterceptors(interceptors);return restTemplate;}

5、示例代码

share: 分享仓库 -

6、日志跟踪

快速定位日志,提高解决问题效率。

6.1 浏览器端

访问接口,响应头会返回x-request-Id。

6.2 服务器端

> grep '50db615d2d6648dba59cdfab574c3eb7' logs/all/all.log [INFO ] -06-02 09:51:46.354 [50db615d2d6648dba59cdfab574c3eb7] [http-nio-8080-exec-72] c.z.z.s.interceptor.WebLogAspect - 请求接口:com.zt.ztsc.server.modules.user.controller.SysConfMerchantController.findMerchantList,请求参数 :{"name":"","type":null,"pageNum":1,"pageSize":10}[INFO ] -06-02 09:51:46.355 [50db615d2d6648dba59cdfab574c3eb7] [http-nio-8080-exec-72] c.z.z.s.interceptor.WebLogAspect - 请求接口:com.test.controller.TestController.findList,请求参数 :"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNSU5HIiwiZXhwIjoxNjU0MTM0NzAwLCJpYXQiOjE2NTQxMzQ3MDAsInVzZXJJZCI6IjEwMDAwMSJ9.LzWC6ZSU3CJoIXfKanvKNoInz6cOsjFPYdkPh4MxwY8".....

根据浏览器的x-request-Id可以跟踪此次访问日志。

如果觉得《logback mdc日志跟踪》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。