wordpress做过的大型网站,泉州免费建站,wordpress首页api幻灯片,汕头澄海网站建设缘起#xff1a;前几天有个粉丝私信#xff0c;想了解现网环境如果出现问题#xff0c;怎么快速定位。可能有些小伙伴这时候就会脱口而出#xff0c;直接去看log 呗#xff0c;有什么好说的。 但是#xff0c;众所周知#xff0c;后端服务面向的前端应用是多种多样的前几天有个粉丝私信想了解现网环境如果出现问题怎么快速定位。可能有些小伙伴这时候就会脱口而出直接去看log 呗有什么好说的。 但是众所周知后端服务面向的前端应用是多种多样的比如有web , app ,小程序等等。也就是说后端服务往往是在一个并发环境下工作如下图所示所以日志文件并不一定是按顺序持久化打印的当然这只是一个极其简陋的模型实际上在互联网公司工作遇到的日志比这复杂很多日志的采集也特别讲究 … 所以在一个混乱的日志文件中有个标识可以将跳跃日志信息关联起来往往可以决定你是否可以正常下班这也是本篇文章也要达到的目的 话不多说进入正文 文章目录 什么是全链路日志追踪怎么用1. 配置Logback2. 编写日志追踪切面3. 定义任务装饰器4. 定义线程池5. 使用线程池6. 完整的pom.xml 为什么是必备的总结 本文将介绍什么是全链路日志追踪如何使用Spring AOP和Logback 来实现以及为什么全链路日志追踪在现网系统中是必备的。 环境 jdk17 springboot 3 在分布式系统中追踪请求的全链路日志对于故障排查和性能优化非常重要。Spring AOP 是常用的Java 技术它可以结合其他技术比如logback灵活实现各种功能。比如本次结合使用来实现全链路日志追踪。
什么是全链路日志追踪
全链路日志追踪是指在一个分布式系统中追踪请求的完整路径并记录每个请求在系统中的处理过程中所产生的日志信息。它可以帮助我们了解请求在系统中的每个环节的执行情况从而更好地排查问题和优化系统性能。
在一个典型的分布式系统中一个请求可能会经过多个服务组件的处理每个组件都会产生日志。全链路日志追踪可以将这些日志串联起来并为每个请求生成一个唯一的标识符以便在需要时能够方便地查找和分析。
其实说白了就是在日志文件中加入一个可以标识每个请求的东西如下所示可以看到a请求的日志中有一串字符串bb946fc6-7b4f-41eb-800c-7ac7b727ab76 , b请求也有一串字符串6f4a64e1-6fce-4701-ad9f-21ff992b5987 在现网的日志文件我们就可以通过搜索这个字符串将跳跃的日志文件串联成一个完整的请求上下文而这个字符串就是后端程序员常说的trace_id 分布式追踪id 。 怎么用
下面将介绍如何使用Spring AOP和Logback MDC来实现全链路日志追踪。
首先简单展示一下demo 项目结构 1. 配置Logback
首先我们需要在应用程序中配置Logback作为日志框架。可以通过在resources下创建logback.xml文件来进行配置。
!--每隔15s 重新加载日志配置文件--
configuration scantrue scanPeriod15 secondsappender nameSTDOUT classch.qos.logback.core.ConsoleAppenderencoder
!-- %X{var} 注入var变量的内容--pattern%d{HH:mm:ss} [%thread] %-5level %logger{36} [%X{logId}] - %msg%n/pattern/encoder/appender!-- 定义变量--property nameLOG_DIR valuelogs /property nameLOG_FILE valuedetail.log /!-- 注意不要用错class --appender nameFILE classch.qos.logback.core.rolling.RollingFileAppenderfile${LOG_DIR}/${LOG_FILE}/file
!-- 压缩归档策略--rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy!-- daily rollover --!-- 按分钟归档 --fileNamePattern${LOG_DIR}/${LOG_FILE}.%d{yyyy-MM-dd-HH-mm}.gz/fileNamePattern!-- keep 30 days worth of history capped at 3GB total size --maxHistory30/maxHistorytotalSizeCap1GB/totalSizeCap/rollingPolicyappendtrue/appendencoderpattern%d{HH:mm:ss} [%thread] %-5level %logger{36} [%X{logId}] - %msg%n/pattern/encoder/appenderroot levelinfoappender-ref refSTDOUT /appender-ref refFILE //root
/configuration在这个配置文件中我们定义了日志输出格式、日志文件路径以及日志的归档方式。你可以根据自己的需求进行配置具体可以简单谷歌一下。
2. 编写日志追踪切面
接下来我们需要使用Spring AOP 编写一个切面来实现日志追踪功能。切面可以拦截请求并在每个请求的上下文注入追踪id。
Component
Aspect
Order(Ordered.HIGHEST_PRECEDENCE)
AllArgsConstructor
public class MdcAspect {public static final String logIdKey logId;private static final Logger log LoggerFactory.getLogger(MdcAspect.class);private final HttpServletRequest httpServletRequest;Around(annotation(org.springframework.web.bind.annotation.GetMapping) || annotation(org.springframework.web.bind.annotation.PostMapping) || annotation(org.springframework.web.bind.annotation.PutMapping) || annotation(org.springframework.web.bind.annotation.DeleteMapping) )public Object injectLogId(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{String logId httpServletRequest.getHeader(MdcAspect.logIdKey);logId StringUtils.isNotBlank(logId) ? (logId _) : UUID.randomUUID();MDC.put(logIdKey, logId);log.info(inject logId {}, logId);try {return proceedingJoinPoint.proceed();} finally {MDC.remove(logIdKey);}}}
在这个切面中我们使用Around注解来拦截所有的方法调用。这个切面主要是用于拦截请求然后在每个请求中注入一个唯一字符串作为每个请求的标识。
现在当应用程序启动时切面将会拦截所有被注解的方法然后在请求上下文注入这么一串唯一字符串。
但是有个问题就是在多线程环境下这个唯一字符串没办法从父进程传递给子进程所以我们采用TaskDecorator的线程任务装饰器方式为线程池的线程提供traceId的传递操作。
3. 定义任务装饰器
public class MDCTaskDecorator implements TaskDecorator {Overridepublic NonNull Runnable decorate(NonNull Runnable runnable) {// 此时获取的是父线程的上下文数据MapString, String contextMap MDC.getCopyOfContextMap();return () - {try {if (!ObjectUtils.isEmpty(contextMap)) {String logId contextMap.getOrDefault(MdcAspect.logIdKey, UUID.randomUUID().toString());logId _ Thread.currentThread().getName();contextMap.put(MdcAspect.logIdKey, logId);// 内部为子线程的领域范围所以将父线程的上下文保存到子线程上下文中而且每次submit/execute调用都会更新为最新的上 // 下文对象MDC.setContextMap(contextMap);}runnable.run();} finally {// 清除子线程的避免内存溢出就和ThreadLocal.remove()一个原因MDC.clear();}};}
}
4. 定义线程池
Configuration
public class PoolConfig {Bean(taskExecutor)public Executor taskExecutor() {ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(5);//配置最大线程数executor.setMaxPoolSize(10);//配置队列大小executor.setQueueCapacity(100);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix(mdc-trace-);// 异步MDCexecutor.setTaskDecorator(new MDCTaskDecorator());//执行初始化executor.initialize();return executor;}
}
5. 使用线程池 ResourceQualifier(taskExecutor)private Executor executor;PostMapping(/pool)public String pool() throws Exception{CompletableFutureString future CompletableFuture.supplyAsync(() - {log.info(线程池里面);try{Thread.sleep(1000);} catch (Exception ignored){}return ;}, executor);future.thenApplyAsync( value -{log.info(线程池组合操作);try{Thread.sleep(1000);} catch (Exception ignored) {}return value 1;}, executor);return future.get();}6. 完整的pom.xml
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.2.1/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIdeasy_mdc/artifactIdversion0.0.1-SNAPSHOT/versionnameeasy_mdc/namedescriptioneasy_mdc/descriptionpropertiesjava.version17/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- 引入aop支持 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdcommons-lang/groupIdartifactIdcommons-lang/artifactIdversion2.6/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationexcludesexcludegroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/exclude/excludes/configuration/plugin/plugins/build/project
为什么是必备的
全链路日志追踪在现网系统中是必备的原因如下 故障排查当系统发生故障时全链路日志追踪可以帮助我们快速定位问题所在。通过追踪请求的完整路径我们可以找到导致故障的具体组件或方法从而更有效地进行故障排查。 性能优化全链路日志追踪可以帮助我们发现系统中的性能瓶颈。通过分析日志我们可以了解每个请求在系统中的处理时间和资源消耗从而找到需要优化的地方并进行相应的性能优化。 监控和统计全链路日志追踪可以为系统的监控和统计提供数据支持。通过收集和分析日志信息我们可以了解系统的运行情况、请求的处理情况和各个组件的性能指标从而对系统进行实时监控和统计分析。
综上所述全链路日志追踪是现网系统中必备的工具它可以帮助我们更好地排查问题、优化性能并提供对系统的监控和统计能力。
总结
本文介绍了全链路日志追踪的概念和重要性并通过使用Spring AOP和Logback来实现全链路日志追踪的示例。通过配置Logback作为日志框架在切面中记录请求的开始和结束日志我们可以实现全链路日志追踪的功能。最后我们强调了全链路日志追踪在现网系统中的必备性它可以帮助我们排查问题、优化性能并提供对系统的监控和统计能力。 当然本文只是一个简单参考实现方法还有很多仅抛砖引玉 希望本文能够帮助你理解和应用全链路日志追踪。如果你有任何问题或建议请随时留言。
欢迎关注我们的微信公众号获取更多有关Java和Spring的技术文章和教程。