《图书馆的女朋友》第一季第01集:在这场超高清的邂逅里,遇见初恋的余味

核心内容摘要

xkdsp.apk6.0:解锁隐藏的数字宇宙,体验前所未有的精彩!
甜蜜碰撞,豆浆飘香:当颜值遇上实力,一杯暖心的幸福启程

9.1日,与成长蓝莓和鲜美蘑菇共谱舌尖上的奇遇

前言从文章标题就知道这篇文章是介绍些什么。

这是我一位朋友的问题反馈图片好像是的确实这种现象是普遍存在的。

有时候一个业务调用链场景很长调了各种各样的方法看日志的时候各个接口的日志穿插确实让人头大。

模糊匹配搜索日志能解决吗 能解决一点点。

但是不能完全呈现出整个链路相关的日志。

那要做到方便很显然我们需要的是把同一次的业务调用链上的日志串起来。

什么效果 先看一个实现后的效果图图片这样下来我们再配合模糊匹配查找日志效果不就刚刚的了。

cat -n info.log |grep a415ad50dbf84e99b1b56a31aacd209c或者grep -10 a415ad50dbf84e99b1b56a31aacd209c info.log 10是指上下10行不多说开整。

正文惯例先看一眼这次实战最终工程的结构图片①pom.xml 依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-logging/artifactId /dependency !--lombok配置-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version

1.

1

10/version /dependency /dependencies②整合logback打印日志logback-spring.xml (简单配置下)?xml version

0 encodingUTF-8? configuration debugfalse !--日志存储路径-- property namelog valueD:/test/log / !-- 控制台输出 -- appender nameconsole classch.qos.logback.core.ConsoleAppender encoder classch.qos.logback.classic.encoder.PatternLayoutEncoder !--输出格式化-- pattern[%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/pattern /encoder /appender !-- 按天生成日志文件 -- appender namefile classch.qos.logback.core.rolling.RollingFileAppender rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy !--日志文件名-- FileNamePattern${log}/%d{yyyy-MM-dd}.log/FileNamePattern !--保留天数-- MaxHistory30/MaxHistory /rollingPolicy encoder classch.qos.logback.classic.encoder.PatternLayoutEncoder pattern[%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/pattern /encoder !--日志文件最大的大小-- triggeringPolicy classch.qos.logback.core.rolling.SizeBasedTriggeringPolicy MaxFileSize10MB/MaxFileSize /triggeringPolicy /appender !-- 日志输出级别 -- root levelINFO appender-ref refconsole / appender-ref reffile / /root /configurationapplication.ymlserver: port: 8826 logging: config: classpath:logback-spring.xml③自定义日志拦截器 LogInterceptor.java用途每一次链路线程维度添加最终的链路ID TRACE_ID。

import org.slf4j.MDC; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; /** * Author: JCccc * Date:

10:45 * Description: */ publicclass LogInterceptor implements HandlerInterceptor { privatestaticfinal String TRACE_ID TRACE_ID; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String tid UUID.randomUUID().toString().replace(-, ); //可以考虑让客户端传入链路ID但需保证一定的复杂度唯一性如果没使用默认UUID自动生成 if (!StringUtils.isEmpty(request.getHeader(TRACE_ID))){ tidrequest.getHeader(TRACE_ID); } MDC.put(TRACE_ID, tid); returntrue; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Nullable Exception ex) { MDC.remove(TRACE_ID); } }MDC(Mapped Diagnostic Context)诊断上下文映射是Slf4j提供的一个支持动态打印日志信息的工具。

WebConfigurerAdapter.java 添加拦截器import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * Author: JCccc * Date:

10:47 * Description: */ Configuration publicclass WebConfigurerAdapter implements WebMvcConfigurer { Bean public LogInterceptor logInterceptor() { returnnew LogInterceptor(); } Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(logInterceptor()); //可以具体制定哪些需要拦截哪些不拦截其实也可以使用自定义注解更灵活完成 // .addPathPatterns(/**) // .excludePathPatterns(/testxx.html); } }ps: 其实这个拦截的部分改为使用自定义注解aop也是很灵活的。

到这时候其实已经完成就是这么简单。

我们写个测试接口看下效果PostMapping(doTest) public String doTest(RequestParam(name) String name) throws InterruptedException { log.info(入参 name{},name); testTrace(); log.info(调用结束 name{},name); returnHello,name; } private void testTrace(){ log.info(这是一行info日志); log.error(这是一行error日志); testTrace2(); } private void testTrace2(){ log.info(这也是一行info日志); }效果OK的图片还没完。

接下来看一个场景 使用子线程的场景故意写一个异步线程加入这个调用里面图片再次执行看开效果显然子线程丢失了trackId图片所以我们需要针对子线程使用情形做调整思路 将父线程的trackId传递下去给子线程即可。

①ThreadPoolConfig.java 定义线程池交给spring管理import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import java.util.concurrent.Executor; /** * Author: JCccc * Date:

11:07 * Description: */ Configuration EnableAsync publicclass ThreadPoolConfig { /** * 声明一个线程池 * * return 执行器 */ Bean(MyExecutor) public Executor asyncExecutor() { MyThreadPoolTaskExecutor executor new MyThreadPoolTaskExecutor(); //核心线程数5线程池创建时候初始化的线程数 executor.setCorePoolSize(

; //最大线程数5线程池最大的线程数只有在缓冲队列满了之后才会申请超过核心线程数的线程 executor.setMaxPoolSize(

; //缓冲队列500用来缓冲执行任务的队列 executor.setQueueCapacity(

; //允许线程的空闲时间60秒当超过了核心线程出之外的线程在空闲时间到达之后会被销毁 executor.setKeepAliveSeconds(

; //线程池名的前缀设置好了之后可以方便我们定位处理任务所在的线程池 executor.setThreadNamePrefix(asyncJCccc); executor.initialize(); return executor; } }② MyThreadPoolTaskExecutor.java 是我们自己写的重写了一些方法import org.slf4j.MDC; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Callable; import java.util.concurrent.Future; /** * Author: JCccc * Date:

11:13 * Description: */ publicfinalclass MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { public MyThreadPoolTaskExecutor() { super(); } Override public void execute(Runnable task) { super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap())); } Override public T FutureT submit(CallableT task) { returnsuper.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap())); } Override public Future? submit(Runnable task) { returnsuper.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap())); } }③ThreadMdcUtil.javaimport org.slf4j.MDC; import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; /** * Author: JCccc * Date:

11:14 * Description: */ publicfinalclass ThreadMdcUtil { privatestaticfinal String TRACE_ID TRACE_ID; // 获取唯一性标识 public static String generateTraceId() { return UUID.randomUUID().toString(); } public static void setTraceIdIfAbsent() { if (MDC.get(TRACE_ID) null) { MDC.put(TRACE_ID, generateTraceId()); } } /** * 用于父线程向线程池中提交任务时将自身MDC中的数据复制给子线程 * * param callable * param context * param T * return */ publicstatic T CallableT wrap(final CallableT callable, final MapString, String context) { return () - { if (context null) { MDC.clear(); } else { MDC.setContextMap(context); } setTraceIdIfAbsent(); try { return callable.call(); } finally { MDC.clear(); } }; } /** * 用于父线程向线程池中提交任务时将自身MDC中的数据复制给子线程 * * param runnable * param context * return */ public static Runnable wrap(final Runnable runnable, final MapString, String context) { return () - { if (context null) { MDC.clear(); } else { MDC.setContextMap(context); } setTraceIdIfAbsent(); try { runnable.run(); } finally { MDC.clear(); } }; } }OK重启服务再看看效果图片可以看的子线程的日志也被串起来了。

蜜桃视频app下载直播-蜜桃视频app下载直播应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123