在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

手動實現(xiàn)SpringBoot日志鏈路追蹤

jf_ro2CN3Fa ? 來源:csdn ? 作者:CSDN ? 2022-12-15 15:04 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

  • 前言
  • 正文
3abca07e-7c3d-11ed-8abf-dac502259ad0.jpg

前言

從文章標題就知道,這篇文章是介紹些什么。

這是我一位朋友的問題反饋:

3b060c64-7c3d-11ed-8abf-dac502259ad0.png

好像是的,確實這種現(xiàn)象是普遍存在的。

有時候一個業(yè)務調(diào)用鏈場景,很長,調(diào)了各種各樣的方法,看日志的時候,各個接口的日志穿插,確實讓人頭大。

模糊匹配搜索日志能解決嗎? 能解決一點點。 但是不能完全呈現(xiàn)出整個鏈路相關(guān)的日志。

那要做到方便,很顯然,我們需要的是把同一次的業(yè)務調(diào)用鏈上的日志串起來。

什么效果? 先看一個實現(xiàn)后的效果圖:

3b14d7d0-7c3d-11ed-8abf-dac502259ad0.png

這樣下來,我們再配合模糊匹配查找日志,效果不就剛剛的了。

cat-ninfo.log|grep"a415ad50dbf84e99b1b56a31aacd209c"

或者

grep-10'a415ad50dbf84e99b1b56a31aacd209c'info.log(10是指上下10行)

不多說,開整。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

正文

慣例,先看一眼這次實戰(zhàn)最終工程的結(jié)構(gòu):

3b58db1a-7c3d-11ed-8abf-dac502259ad0.png

①pom.xml 依賴

<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
dependency>

<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.10version>
dependency>
dependencies>

②整合logback,打印日志,logback-spring.xml (簡單配置下)


<configurationdebug="false">

<propertyname="log"value="D:/test/log"/>

<appendername="console"class="ch.qos.logback.core.ConsoleAppender">
<encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder">

<pattern>[%X{TRACE_ID}]%d{yyyy-MM-ddHHss.SSS}[%thread]%-5level%logger{50}-%msg%npattern>
encoder>
appender>

<appendername="file"class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<FileNamePattern>${log}/%d{yyyy-MM-dd}.logFileNamePattern>

<MaxHistory>30MaxHistory>
rollingPolicy>
<encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>[%X{TRACE_ID}]%d{yyyy-MM-ddHHss.SSS}[%thread]%-5level%logger{50}-%msg%npattern>
encoder>

<triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MBMaxFileSize>
triggeringPolicy>
appender>


<rootlevel="INFO">
<appender-refref="console"/>
<appender-refref="file"/>
root>
configuration>

application.yml

server:
port:8826
logging:
config:classpath:logback-spring.xml

③自定義日志攔截器 LogInterceptor.java

用途:每一次鏈路,線程維度,添加最終的鏈路ID TRACE_ID。

importorg.slf4j.MDC;
importorg.springframework.lang.Nullable;
importorg.springframework.util.StringUtils;
importorg.springframework.web.servlet.HandlerInterceptor;

importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.util.UUID;

/**
*@Author:JCccc
*@Date:2022-5-3010:45
*@Description:
*/
publicclassLogInterceptorimplementsHandlerInterceptor{

privatestaticfinalStringTRACE_ID="TRACE_ID";

@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){
Stringtid=UUID.randomUUID().toString().replace("-","");
//可以考慮讓客戶端傳入鏈路ID,但需保證一定的復雜度唯一性;如果沒使用默認UUID自動生成
if(!StringUtils.isEmpty(request.getHeader("TRACE_ID"))){
tid=request.getHeader("TRACE_ID");
}
MDC.put(TRACE_ID,tid);
returntrue;
}

@Override
publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,
@NullableExceptionex){
MDC.remove(TRACE_ID);
}

}

MDC(Mapped Diagnostic Context)診斷上下文映射,是@Slf4j提供的一個支持動態(tài)打印日志信息的工具。

WebConfigurerAdapter.java 添加攔截器

importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
*@Author:JCccc
*@Date:2022-5-3010:47
*@Description:
*/
@Configuration
publicclassWebConfigurerAdapterimplementsWebMvcConfigurer{
@Bean
publicLogInterceptorlogInterceptor(){
returnnewLogInterceptor();
}

@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(logInterceptor());
//可以具體制定哪些需要攔截,哪些不攔截,其實也可以使用自定義注解更靈活完成
//.addPathPatterns("/**")
//.excludePathPatterns("/testxx.html");
}
}

ps: 其實這個攔截的部分改為使用自定義注解+aop也是很靈活的。

到這時候,其實已經(jīng)完成,就是這么簡單。

我們寫個測試接口,看下效果:

@PostMapping("doTest")
publicStringdoTest(@RequestParam("name")Stringname)throwsInterruptedException{
log.info("入?yún)ame={}",name);
testTrace();
log.info("調(diào)用結(jié)束name={}",name);
return"Hello,"+name;
}
privatevoidtestTrace(){
log.info("這是一行info日志");
log.error("這是一行error日志");
testTrace2();
}
privatevoidtestTrace2(){
log.info("這也是一行info日志");

}

效果(OK的):

3b7c2340-7c3d-11ed-8abf-dac502259ad0.png

還沒完。

接下來看一個場景, 使用子線程的場景:

故意寫一個異步線程,加入這個調(diào)用里面:

3bb25adc-7c3d-11ed-8abf-dac502259ad0.png

再次執(zhí)行看開效果,顯然子線程丟失了trackId:

3bc97a14-7c3d-11ed-8abf-dac502259ad0.png

所以我們需要針對子線程使用情形,做調(diào)整,思路: 將父線程的trackId傳遞下去給子線程即可。

①ThreadPoolConfig.java 定義線程池,交給spring管理

importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.scheduling.annotation.EnableAsync;
importjava.util.concurrent.Executor;

/**
*@Author:JCccc
*@Date:2022-5-3011:07
*@Description:
*/
@Configuration
@EnableAsync
publicclassThreadPoolConfig{
/**
*聲明一個線程池
*
*@return執(zhí)行器
*/
@Bean("MyExecutor")
publicExecutorasyncExecutor(){
MyThreadPoolTaskExecutorexecutor=newMyThreadPoolTaskExecutor();
//核心線程數(shù)5:線程池創(chuàng)建時候初始化的線程數(shù)
executor.setCorePoolSize(5);
//最大線程數(shù)5:線程池最大的線程數(shù),只有在緩沖隊列滿了之后才會申請超過核心線程數(shù)的線程
executor.setMaxPoolSize(5);
//緩沖隊列500:用來緩沖執(zhí)行任務的隊列
executor.setQueueCapacity(500);
//允許線程的空閑時間60秒:當超過了核心線程出之外的線程在空閑時間到達之后會被銷毀
executor.setKeepAliveSeconds(60);
//線程池名的前綴:設(shè)置好了之后可以方便我們定位處理任務所在的線程池
executor.setThreadNamePrefix("asyncJCccc");
executor.initialize();
returnexecutor;
}
}

② MyThreadPoolTaskExecutor.java 是我們自己寫的,重寫了一些方法:

importorg.slf4j.MDC;
importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

importjava.util.concurrent.Callable;
importjava.util.concurrent.Future;

/**
*@Author:JCccc
*@Date:2022-5-3011:13
*@Description:
*/
publicfinalclassMyThreadPoolTaskExecutorextendsThreadPoolTaskExecutor{
publicMyThreadPoolTaskExecutor(){
super();
}

@Override
publicvoidexecute(Runnabletask){
super.execute(ThreadMdcUtil.wrap(task,MDC.getCopyOfContextMap()));
}


@Override
publicFuturesubmit(Callabletask){
returnsuper.submit(ThreadMdcUtil.wrap(task,MDC.getCopyOfContextMap()));
}

@Override
publicFuturesubmit(Runnabletask){
returnsuper.submit(ThreadMdcUtil.wrap(task,MDC.getCopyOfContextMap()));
}
}

③ThreadMdcUtil.java

importorg.slf4j.MDC;

importjava.util.Map;
importjava.util.UUID;
importjava.util.concurrent.Callable;

/**
*@Author:JCccc
*@Date:2022-5-3011:14
*@Description:
*/
publicfinalclassThreadMdcUtil{
privatestaticfinalStringTRACE_ID="TRACE_ID";

//獲取唯一性標識
publicstaticStringgenerateTraceId(){
returnUUID.randomUUID().toString();
}

publicstaticvoidsetTraceIdIfAbsent(){
if(MDC.get(TRACE_ID)==null){
MDC.put(TRACE_ID,generateTraceId());
}
}

/**
*用于父線程向線程池中提交任務時,將自身MDC中的數(shù)據(jù)復制給子線程
*
*@paramcallable
*@paramcontext
*@param
*@return
*/
publicstaticCallablewrap(finalCallablecallable,finalMapcontext){
return()->{
if(context==null){
MDC.clear();
}else{
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try{
returncallable.call();
}finally{
MDC.clear();
}
};
}

/**
*用于父線程向線程池中提交任務時,將自身MDC中的數(shù)據(jù)復制給子線程
*
*@paramrunnable
*@paramcontext
*@return
*/
publicstaticRunnablewrap(finalRunnablerunnable,finalMapcontext){
return()->{
if(context==null){
MDC.clear();
}else{
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try{
runnable.run();
}finally{
MDC.clear();
}
};
}
}

OK,重啟服務,再看看效果:

3be73004-7c3d-11ed-8abf-dac502259ad0.png

可以看的,子線程的日志也被串起來了。



審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14963
  • 日志
    +關(guān)注

    關(guān)注

    0

    文章

    143

    瀏覽量

    10845
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    175

    瀏覽量

    356

原文標題:手動實現(xiàn) SpringBoot 日志鏈路追蹤,無需引入組件,日志定位更方便!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    詳解journalctl日志管理

    systemd 提供了自己的日志系統(tǒng)(logging system),稱為 journal。使用 systemd 日志,無需額外安裝日志服務(syslog)。
    的頭像 發(fā)表于 06-05 17:22 ?263次閱讀
    詳解journalctl<b class='flag-5'>日志</b>管理

    愛立信攜手Telstra、高通刷新5G上行速度紀錄

    愛立信、Telstra、高通近日攜手創(chuàng)下5G上行516 Mbps速度新紀錄,成為目前在商用Sub-6GHz 5G SA現(xiàn)網(wǎng)實現(xiàn)的最高上行
    的頭像 發(fā)表于 03-26 16:31 ?7890次閱讀

    智能推送系統(tǒng)的全統(tǒng)計功能:數(shù)據(jù)閉環(huán)下的運營增效革命

    在精細化運營時代,APP企業(yè)面臨的核心挑戰(zhàn)已從“如何觸達用戶”轉(zhuǎn)向“如何量化每一次觸達的價值”。MobPush智能推送系統(tǒng)的全統(tǒng)計功能,通過追蹤用戶從推送接收、點擊到最終轉(zhuǎn)化的完整路徑,構(gòu)建
    的頭像 發(fā)表于 02-25 17:23 ?335次閱讀

    分布式日志追蹤ID實戰(zhàn)

    作者:京東物流 張小龍 本文通過介紹分布式應用下各個場景的全局日志ID透傳思路,以及介紹分布式日志追蹤ID簡單實現(xiàn)原理和實戰(zhàn)效果,從而達到通過提高
    的頭像 發(fā)表于 01-20 10:16 ?608次閱讀

    Wine開發(fā)系列——如何使用Wine日志調(diào)試問題

    助于快速理解代碼的執(zhí)行流程和功能。在大型項目中,通常會先實現(xiàn)一套自己的調(diào)試日志框架,主要有兩個目的: 統(tǒng)一日志風格和存儲:確保日志格式一致,并且有統(tǒng)一的存儲方式,這有助于用戶更容易地報
    的頭像 發(fā)表于 01-06 11:29 ?908次閱讀

    靜態(tài)路由和聚合的通信原理

    靜態(tài)路由和聚合的通信原理
    發(fā)表于 12-23 16:59 ?0次下載

    調(diào)試PCIE動態(tài)均衡介紹

    隨著連續(xù)幾代 PCI Express 以 8 Gbps、16 Gbps 和 32 Gbps 的速度運行,動態(tài)均衡變得至關(guān)重要。均衡會補償通信信道對信號的影響。 這些影響包括充當?shù)屯V波器的
    的頭像 發(fā)表于 12-05 09:18 ?1683次閱讀
    調(diào)試PCIE<b class='flag-5'>鏈</b><b class='flag-5'>路</b>動態(tài)均衡介紹

    負載均衡設(shè)置在哪里?

    負載均衡設(shè)置涉及交換機、路由器和(可選)負載均衡器的設(shè)置。首先規(guī)劃網(wǎng)絡(luò)拓撲和IP地址,備份設(shè)備配置。然后,在交換機上配置VLAN和Trunk,在路由器上配置接口、路由協(xié)議和策
    的頭像 發(fā)表于 11-13 10:19 ?442次閱讀

    基于光線追蹤的渲染算法實現(xiàn)

    我們已經(jīng)涵蓋了所有需要說的內(nèi)容!我們現(xiàn)在準備寫我們的第一個光線追蹤器。你現(xiàn)在應該能夠猜到光線追蹤算法是如何工作的了。首先,注意到自然界中光的傳播只是從光源發(fā)出無數(shù)條射線,反彈直到它們撞到我們眼睛的表面。因此,光線追蹤是優(yōu)雅的,因
    的頭像 發(fā)表于 10-30 08:06 ?607次閱讀
    基于光線<b class='flag-5'>追蹤</b>的渲染算法<b class='flag-5'>實現(xiàn)</b>

    nginx日志配置方法

    access_log用來定義日志級別,日志位置。
    的頭像 發(fā)表于 10-24 17:43 ?543次閱讀

    Linux日志管理經(jīng)驗總結(jié)

    日志內(nèi)容,合理的日志內(nèi)容(日志錨點,內(nèi)容格式,等)可以為應用服務的執(zhí)行記錄、問題排查提供最有力的幫助。
    的頭像 發(fā)表于 10-24 17:36 ?504次閱讀

    日志篇:模組日志總體介紹

    ?今天我們學習合宙模組日志總體介紹,以下進入正文。 一、本文討論的邊界 本文是對合宙 4G 模組, 以及 4G+GNSS 模組的日志功能的總體介紹。通過日志,可以對研發(fā)過程中,以及模組運行過程中
    的頭像 發(fā)表于 10-24 07:16 ?535次閱讀
    <b class='flag-5'>日志</b>篇:模組<b class='flag-5'>日志</b>總體介紹

    PCle培訓概述

    電子發(fā)燒友網(wǎng)站提供《PCle培訓概述.pdf》資料免費下載
    發(fā)表于 09-11 09:16 ?0次下載
    PCle<b class='flag-5'>鏈</b><b class='flag-5'>路</b>培訓概述

    IR615如何實現(xiàn)VPN備份?

    目的:IR615的備份(WAN為主、Wi-Fi做STA為從),當VPN建好后,WAN
    發(fā)表于 07-25 08:27

    IG網(wǎng)關(guān)產(chǎn)品實現(xiàn)備份的方法

    的-接口備份模塊。 3、選擇主接口以及備份接口等數(shù)據(jù)參數(shù);設(shè)定完成后添加即生效。(Dot11radio 1代表WLAN接口) 通過這樣設(shè)定即可實現(xiàn)備份的基礎(chǔ)功能設(shè)定:
    發(fā)表于 07-24 08:25
    主站蜘蛛池模板: 青青导航| 亚洲va久久久噜噜噜久久狠狠 | 久久婷婷成人综合色 | 亚洲成人网在线播放 | 中国一级做a爰片久久毛片 中韩日欧美电影免费看 | 色噜噜狠狠狠色综合久 | 国产激情三级 | 黄 色 录像成 人播放免费 | 中文字幕色综合久久 | 一区二区在线看 | 亚洲xx网站 | 夜夜操天天操 | 性欧美video视频另类 | 日韩亚洲人成网站在线播放 | 91大神大战高跟丝袜美女 | 欧美黄色大片免费 | 亚洲综合精品成人啪啪 | 欧美xxxxbbbb| 你懂的免费在线视频 | 男人视频在线观看 | 午夜视频在线免费播放 | 久久免费看视频 | 91久久婷婷国产综合精品青草 | 免费一级特黄特色大片在线观看 | 欧美日韩一区二区三区视频在线观看 | 色网站观看 | 伊人久久99 | 完全免费在线视频 | 欧美日韩生活片 | 奇米影视777狠狠狠888不卡 | 性色视频在线 | 免费一级毛片私人影院a行 免费一级毛片无毒不卡 | vip免费观看 | 4399一级成人毛片 | 青草久| 久久老色鬼天天综合网观看 | 9久久9久久精品 | 免费国产综合视频在线看 | 天堂中文资源在线地址 | 2021最新久久久视精品爱 | 久久视频精品36线视频在线观看 |