作者:京東物流 郭忠強(qiáng)
導(dǎo)語(yǔ)
本文從日常值班問(wèn)題排查痛點(diǎn)出發(fā),分析方法復(fù)用的調(diào)用鏈路和上下文業(yè)務(wù)邏輯,通過(guò)思考分析,借助棧幀開(kāi)發(fā)了一個(gè)方法調(diào)用棧的鏈?zhǔn)礁櫣ぞ撸阌谡故疽淮握?qǐng)求的方法串行調(diào)用鏈,有助于快速定位代碼來(lái)源和流量入口,有效提升研發(fā)和運(yùn)維排查定位效率。期望在大家面臨類似痛點(diǎn)時(shí)可以提供一些實(shí)踐經(jīng)驗(yàn)和參考,也歡迎大家合適的場(chǎng)景下接入使用。
?
現(xiàn)狀分析
在系統(tǒng)值班時(shí),經(jīng)常會(huì)有人拿著報(bào)錯(cuò)截圖前來(lái)咨詢,作為值班研發(fā),我們則需要獲取盡可能多的信息,幫助我們分析報(bào)錯(cuò)場(chǎng)景,便于排查識(shí)別問(wèn)題。
例如,下圖就是一個(gè)常見(jiàn)的的報(bào)錯(cuò)信息截圖示例。
從圖中,我們可以初步獲取到一些信息:
?菜單名稱:變更單下架,我們這是變更單下架操作時(shí)的一個(gè)報(bào)錯(cuò)提醒。
?報(bào)錯(cuò)信息:序列號(hào)狀態(tài)為離庫(kù)狀態(tài),請(qǐng)檢查。
?其他輔助信息:例如用戶掃描或輸入的86開(kāi)頭編碼,SKU、商品名稱、儲(chǔ)位等。
?
這時(shí)會(huì)有一些常見(jiàn)的排查思路:
1、根據(jù)提示,將用戶輸入的86編碼,按照提示文案去檢查用戶數(shù)據(jù),即作為序列號(hào)編碼,去看一下序列號(hào)是否存在,是否真的是離庫(kù)了。
2、如果86編碼確實(shí)是序列號(hào),而且真的是離庫(kù)了,那么基本上可以快速結(jié)案了,這個(gè)86編碼確實(shí)是序列號(hào)并且是已離庫(kù),正如提示文案所示,這時(shí)跟提問(wèn)人做好解釋答疑即可。
3、如果第2步排查完,發(fā)現(xiàn)86編碼不是序列號(hào)編碼,或并非離庫(kù)狀態(tài),即與提示文案不符,這時(shí)就要定位報(bào)錯(cuò)文案的代碼來(lái)源,繼續(xù)查看代碼邏輯來(lái)進(jìn)行判案了。(這種也比較常見(jiàn),一種是報(bào)錯(cuò)場(chǎng)景較多,但提示文案比較單一,不便于在提示文案中覆蓋所有報(bào)錯(cuò)場(chǎng)景;另一種提示文案陳舊未跟隨需求演變而更新。這兩點(diǎn)可以通過(guò)細(xì)分場(chǎng)景細(xì)化對(duì)應(yīng)的報(bào)錯(cuò)文案,或更新報(bào)錯(cuò)文案,使得報(bào)錯(cuò)文案更優(yōu)更新,但不是本文討論的重點(diǎn)。)
4、如何根據(jù)報(bào)錯(cuò)文案快速找到代碼來(lái)源呢?一般我們會(huì)在代碼庫(kù)中搜索提示文案,或者在日志中檢索報(bào)錯(cuò)信息,輔助定位代碼來(lái)源,后者依賴于代碼中打印了該報(bào)錯(cuò)信息,且日志級(jí)別配置能夠確保該信息打印到日志文件中。
5、倘若我們根據(jù)提示文案搜索代碼時(shí),發(fā)現(xiàn)該提示文案有多處代碼出現(xiàn),此時(shí)就較為復(fù)雜了,我們需要進(jìn)一步識(shí)別,哪個(gè)才與本次報(bào)錯(cuò)直接有關(guān)。
?
每個(gè)方法向上追溯,又發(fā)現(xiàn)調(diào)用來(lái)源眾多:
?
?
在業(yè)務(wù)復(fù)雜的系統(tǒng)中,方法復(fù)用比較常見(jiàn),不同的上下文和參數(shù)傳遞,也有著不同的業(yè)務(wù)邏輯判斷和走向。
這時(shí),基本上進(jìn)入到本文要討論的痛點(diǎn):如何根據(jù)有限的提示信息快速定位代碼來(lái)源?以便于分析報(bào)錯(cuò)業(yè)務(wù)場(chǎng)景,答疑解惑或快速處理問(wèn)題。
屏幕前的小伙伴,如果你也經(jīng)常值班排查問(wèn)題,應(yīng)該也會(huì)有類似的痛點(diǎn)所在。
啟發(fā)
這是我想到了Exception異常機(jī)制,作為一名Coder,我們對(duì)異常堆棧再熟悉不過(guò)了,異常堆棧是一個(gè)“可愛(ài)”又“可恨”的東西,“可愛(ài)”在于異常堆棧確實(shí)可以幫助我們快速定位問(wèn)題所在,“可恨”在于有異?;旧暇褪怯袉?wèn)題,堆棧讓我們審美疲勞,累覺(jué)不愛(ài)。
下面是一個(gè)Java語(yǔ)言的異常堆棧信息示例:
?
異常類體系和異常處理機(jī)制在本文中不是重點(diǎn),不做過(guò)多贅述,本文重點(diǎn)希望能從異常堆棧中獲取一些啟發(fā)。
讓我們近距離再觀察一下我們的老朋友。
在異常堆棧信息中,主要有四類信息:
?全限定類名
?方法名
?文件名
?代碼行號(hào)
這四類信息可以幫助我們有效定位代碼來(lái)源,而且堆棧中記錄行先后順序,也表示著異常發(fā)生的第一現(xiàn)場(chǎng)、第二現(xiàn)場(chǎng)、第三現(xiàn)場(chǎng)、……,以此傳遞。
這讓我想起了JVM方法棧中的棧幀。
每當(dāng)一個(gè)方法被調(diào)用時(shí),JVM會(huì)為該方法創(chuàng)建一個(gè)新的棧幀,并將其壓入當(dāng)前線程的棧(也稱為調(diào)用?;驁?zhí)行棧)中。棧幀包含了方法執(zhí)行所需的所有信息,包括局部變量、操作數(shù)棧、常量池引用等。
?
思路
從Java中的Throwable中,可以看到staceTrace的get和set,這個(gè)StackTraceElement數(shù)組里面存放的信息就是我們?cè)诋惓6褩V薪?jīng)??吹降男畔?。
?
?
?
再次放一下這張圖,方便對(duì)照著看。
?
StackTraceElement類的注釋中赫然寫著:
StackTraceElement represents a stack frame.
對(duì),StackTraceElement代表著一個(gè)棧幀。
這個(gè)StackTraceElement就是我要找的東西,即使非異常情況下,每個(gè)線程在執(zhí)行方法調(diào)用時(shí)都會(huì)記錄棧幀信息。
?
按照方法調(diào)用先后順序,將調(diào)用棧中方法依次串聯(lián)起來(lái),就像糖葫蘆一樣,就可以得到我想要的方法調(diào)用鏈了。
NEXT,我可以動(dòng)工寫個(gè)工具了。
工具開(kāi)發(fā)
工具的核心代碼并不復(fù)雜,StackTraceElement 也是 Java JDK 中現(xiàn)成的,我所做的工作主要是從中過(guò)濾出必要的信息,加工簡(jiǎn)化成,按照順序整理成鏈?zhǔn)叫畔?,方便我們一眼就可以看出?lái)方法的調(diào)用鏈。
?
入?yún)⒔榻B
pretty:表示是只拼接類和方法,不拼接文件名和行號(hào),非 pretty 是四個(gè)都會(huì)拼接。
simple:表示會(huì)過(guò)濾一些我們代碼中場(chǎng)景的代理增強(qiáng)出來(lái)的方法的信息輸出。
specifiedPrefix:指定保留相應(yīng)的包路徑堆棧信息,去掉一些過(guò)多的中間件信息。
其他還會(huì)過(guò)濾一些常見(jiàn)代理的堆棧信息:
?FastClassBySpringCGLIB
?EnhancerBySpringCGLIB
?lambda$
?Aspect
?Interceptor
對(duì)此,還封裝了一些默認(rèn)參數(shù)的方法,使用起來(lái)更為方便。
?
還有一些其他工具方法也可以使用:
?
使用效果
1、不過(guò)濾中間件、代理增強(qiáng)方法的調(diào)用棧信息
Thread#run ==> ThreadPoolExecutor$Worker#run ==> ThreadPoolExecutor#runWorker ==> BaseTask#run ==> JSFTask#doRun ==> ProviderProxyInvoker#invoke ==> FilterChain#invoke ==> SystemTimeCheckFilter#invoke ==> ProviderExceptionFilter#invoke ==> ProviderContextFilter#invoke ==> InstMethodsInter#intercept ==> ProviderContextFilter$eone$auxiliary$9f9kd21#call ==> ProviderContextFilter#eone$original$invoke$p882ot3$accessor$eone$pclcbe2 ==> ProviderContextFilter#eone$original$invoke$p882ot3 ==> ProviderGenericFilter#invoke ==> ProviderUnitValidationFilter#invoke ==> ProviderHttpGWFilter#invoke ==> ProviderInvokeLimitFilter#invoke ==> ProviderMethodCheckFilter#invoke ==> ProviderTimeoutFilter#invoke ==> ValidationFilter#invoke ==> ProviderConcurrentsFilter#invoke ==> ProviderSecurityFilter#invoke ==> WmsRpcExceptionFilter#invoke ==> WmsRpcExceptionFilter#invoke4provider ==> AdmissionControlJsfFilter#invoke ==> AdmissionControlJsfFilter#providerSide ==> AdmissionControlJsfFilter#processRequest ==> ChainedDeadlineJsfFilter#invoke ==> ChainedDeadlineJsfFilter#providerSide ==> JsfPerformanceMonitor#invoke ==> AbstractMiddlewarePerformanceMonitor#doExecute ==> PerformanceMonitorTemplateComposite#execute ==> PerformanceMonitorTemplateComposite#lambda$execute$0 ==> PerformanceMonitorTemplateUmp#execute ==> PerformanceMonitorTemplateComposite#lambda$execute$0 ==> PerformanceMonitorTemplatePayload#execute ==> JsfPerformanceMonitor#lambda$invoke$0 ==> JsfPerformanceMonitor#doInvoke ==> ProviderInvokeFilter#invoke ==> ProviderInvokeFilter#reflectInvoke ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor1704#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> ExposeInvocationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor344#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor1276#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor868#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor869#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor1642#invoke ==> MagicAspect#magic ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> ExposeInvocationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor868#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor869#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor1295#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> ExposeInvocationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor868#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> ExposeInvocationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AnnotationAwareRetryOperationsInterceptor#invoke ==> RetryOperationsInterceptor#invoke ==> RetryTemplate#execute ==> RetryTemplate#doExecute ==> RetryOperationsInterceptor$1#doWithRetry ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> CglibAopProxy$DynamicAdvisedInterceptor#intercept ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> ExposeInvocationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor1276#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> TransactionInterceptor#invoke ==> TransactionAspectSupport#invokeWithinTransaction ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> AspectJAroundAdvice#invoke ==> AbstractAspectJAdvice#invokeAdviceMethod ==> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs ==> Method#invoke ==> DelegatingMethodAccessorImpl#invoke ==> GeneratedMethodAccessor869#invoke ==> MethodInvocationProceedingJoinPoint#proceed ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> PersistenceExceptionTranslationInterceptor#invoke ==> CglibAopProxy$CglibMethodInvocation#proceed ==> ReflectiveMethodInvocation#proceed ==> CglibAopProxy$CglibMethodInvocation#invokeJoinpoint ==> MethodProxy#invoke ==> StackTraceUtils#trace
2、指定包路徑過(guò)濾中間件后的調(diào)用棧棧信息
LockAspect#lock ==> StockTransferAppServiceImpl#increaseStock ==> MonitorAspect#monitor ==> StockRetryExecutor#operateStock ==> StockRetryExecutor$$FastClassBySpringCGLIB$$5188d6e#invoke ==> BaseStockOperation$$FastClassBySpringCGLIB$$9d76cd9a#invoke ==> StockTransferServiceImpl$$FastClassBySpringCGLIB$$85bb181e#invoke ==> ValidationAspect#logAndReturn ==> LogAspect#log ==> ThreadLocalRemovalAspect#removal ==> ValidationAspect#validate ==> BaseStockOperation#go ==> StockRepositoryImpl$$EnhancerBySpringCGLIB$$1388ef12#operateStock ==> StockTransferAppServiceImpl$$EnhancerBySpringCGLIB$$1095eafa#increaseStock ==> StockRepositoryImpl$$FastClassBySpringCGLIB$$a1b4dae4#invoke ==> StockTransferServiceImpl#increaseStock ==> DataBaseExecutor#execute ==> StockRetryExecutor$$EnhancerBySpringCGLIB$$b42789a#operateStock ==> StockInitializer$$EnhancerBySpringCGLIB$$85faf510#go ==> StockTransferServiceImpl$$EnhancerBySpringCGLIB$$afc21975#increaseStock ==> StockRepositoryImpl#operateStock ==> DataBaseExecutor#operate ==> StockTransferAppServiceImpl$$FastClassBySpringCGLIB$$e348d8e1#invoke
3、去掉Spring代理增強(qiáng)之后的調(diào)用棧信息
LogAspect#log ==> LockAspect#lock ==> ValidationAspect#validate ==> ValidationAspect#logAndReturn ==> MonitorAspect#monitor ==> StockTransferAppServiceImpl#decreaseStock ==> ThreadLocalRemovalAspect#removal ==> StockTransferServiceImpl#decreaseStock ==> StockOperationLoader#go ==> BaseStockOperation#go ==> DataBaseExecutor#operate ==> DataBaseExecutor#execute ==> StockRetryExecutor#operateStock ==> StockRepositoryImpl#operateStock
4、去掉一些自定義代理之后的調(diào)用棧棧信息
StockTransferAppServiceImpl#increaseStock ==> StockTransferServiceImpl#increaseStock ==> BaseStockOperation#go ==> DataBaseExecutor#operate ==> DataBaseExecutor#execute ==> StockRetryExecutor#operateStock ==> StockRepositoryImpl#operateStock
5、如果帶上文件名和行號(hào)后的調(diào)用棧棧信息
StockTransferAppServiceImpl#increaseStock(StockTransferAppServiceImpl.java:103) ==> StockTransferServiceImpl#increaseStock(StockTransferServiceImpl.java:168) ==> BaseStockOperation#go(BaseStockOperation.java:152) ==> BaseStockOperation#go(BaseStockOperation.java:181) ==> BaseStockOperation#go(BaseStockOperation.java:172) ==> DataBaseExecutor#operate(DataBaseExecutor.java:34) ==> DataBaseExecutor#operate(DataBaseExecutor.java:64) ==> DataBaseExecutor#execute(DataBaseExecutor.java:79) ==> StockRetryExecutor#operateStock(StockRetryExecutor.java:64) ==> StockRepositoryImpl#operateStock(StockRepositoryImpl.java:303)
?
線上應(yīng)用實(shí)踐
接入方法調(diào)用棧跟蹤工具后,根據(jù)報(bào)錯(cuò)提示詞,可以檢索到對(duì)應(yīng)日志,從 ImmediateTransferController#offShelf ==> AopConfig#pointApiExpression ==> TransferOffShelfAppServiceImpl#offShelf ==> TransferOffShelfAppServiceImpl#doOffShelf 中順藤摸瓜可以快速找到流量入口的代碼位置。
?
?
?
適用場(chǎng)景
該方法調(diào)用棧工具類,可以在一些堆棧信息進(jìn)行輔助排查分析的地方進(jìn)行預(yù)埋,例如:
?業(yè)務(wù)異常時(shí)輸出堆棧到日志信息中。
?業(yè)務(wù)監(jiān)控告警信息中加入調(diào)用棧信息。
?一些復(fù)用方法調(diào)用復(fù)雜場(chǎng)景下,打印調(diào)用棧信息,展示調(diào)用鏈,方便分析。
?其他一些場(chǎng)景等。
延伸
在《如何一眼定位SQL的代碼來(lái)源:一款SQL染色標(biāo)記的簡(jiǎn)易MyBatis插件》一文中,我發(fā)布了一款SQL染色插件,該插件目前已有statementId信息,還支持通過(guò)SQLMarkingThreadLocal傳遞自定義附加信息。其他BGBU的技術(shù)小伙伴,也有呼聲,希望在statementId基礎(chǔ)上可以繼續(xù)追溯入口方法。通過(guò)本文引入的方法調(diào)用棧跟蹤工具,我在SQL染色插件中增加了方法調(diào)用棧染色信息。
SQL染色工具新版特性,歡迎大家先在TEST和UAT環(huán)境嘗鮮試用,TEST和UAT環(huán)境驗(yàn)證沒(méi)問(wèn)題后,再逐步推廣正式環(huán)境。
升級(jí)方法:
1、sword-mybatis-plugins版本升級(jí)至1.0.6-SNAPSHOT。
2、同時(shí)新引入本文的工具依賴
!-- http://sd.jd.com/article/45616?shareId=105168&isHideShareButton=1 --?> com.jd.sword/groupId?> sword-utils-common/artifactId?> 1.0.0-SNAPSHOT/version?> /dependency?>
3、mybatis config xml 配置文件按最新配置調(diào)整
!-- http://sd.jd.com/article/42942?shareId=105168&isHideShareButton=1 --?> !-- SQLMarking Plugin,放在第一個(gè)Plugin的位置,不影響其他組件,但不強(qiáng)要求位置,也可以靈活調(diào)整順序位置 --?> !-- 是否開(kāi)啟SQL染色標(biāo)記插件 --?> !-- 是否開(kāi)啟方法調(diào)用棧跟蹤 --?> !-- 指定需要方法調(diào)用棧跟蹤的package,減少信息量,value配置為自己工程的package路徑,多個(gè)路徑用英文逗號(hào)分割 --?> !-- 結(jié)合CPU利用率和性能考慮,方法調(diào)用棧跟蹤采集率配置采集率,配置示例: m/n,表示n個(gè)里面抽m個(gè)進(jìn)行采集跟蹤 --?> !-- 預(yù)發(fā)環(huán)境和測(cè)試環(huán)境可以配置全采集,例如配置1/1,生產(chǎn)環(huán)境可以結(jié)合CPU利用率和性能考慮按需配置采集率 --?> /plugin?>
接入效果
SELECT id, tenant_code, warehouse_no, sku, location_no, container_level_1, container_level_2, lot_no, sku_level, owner_no, pack_code, conversion_rate, stock_qty, prepicked_qty, premoved_qty, frozen_qty, diff_qty, broken_qty, status, md5_value, version, create_user, update_user, create_time, update_time, extend_content FROM st_stock WHERE deleted = 0 AND warehouse_no = ? AND location_no IN(?) AND container_level_1 IN(?) AND container_level_2 IN(?) AND sku IN(?) /* [SQLMarking] statementId: com.jdwl.wms.stock.infrastructure.jdbc.main.dao.StockQueryDao.selectExtendedStockByLocation, stackTrace: BaseJmqConsumer#onMessage ==> StockInfoConsumer#handle ==> StockInfoConsumer#handleEvent ==> StockExtendContentFiller#fillExtendContent ==> StockInitializer#queryStockByWarehouse ==> StockInitializer#batchQueryStockByWarehouse ==> StockInitializer#queryByLocationAndSku ==> StockQueryRepositoryImpl#queryExtendedStockByLocationAndSku, warehouseNo: 6_6_601 */
如何接入本文工具?
如果小伙伴也有類似使用訴求,大家可以先在測(cè)試、UAT環(huán)境接入試用,然后再逐步推廣線上生產(chǎn)環(huán)境。
1、新引入本文的工具依賴
com.jd.sword/groupId?> sword-utils-common/artifactId?> 1.0.0-SNAPSHOT/version?> /dependency?>
2、使用工具類靜態(tài)方法
com.jd.sword.utils.common.runtime.StackTraceUtils#simpleTrace()
com.jd.sword.utils.common.runtime.StackTraceUtils#simpleTrace(java.lang.String...)
com.jd.sword.utils.common.runtime.StackTraceUtils#trace()
com.jd.sword.utils.common.runtime.StackTraceUtils#trace(java.lang.String...)
com.jd.sword.utils.common.runtime.StackTraceUtils#trace(boolean)
com.jd.sword.utils.common.runtime.StackTraceUtils#trace(boolean, boolean, java.lang.String...) 審核編輯 黃宇
-
編程
+關(guān)注
關(guān)注
88文章
3674瀏覽量
94739 -
SQL
+關(guān)注
關(guān)注
1文章
780瀏覽量
44736 -
定位
+關(guān)注
關(guān)注
5文章
1387瀏覽量
35783
發(fā)布評(píng)論請(qǐng)先 登錄
開(kāi)發(fā)一款支持主機(jī)模式的FreeModbus協(xié)議棧

C函數(shù)調(diào)用機(jī)制與棧幀原理詳解

[分享]分享一款免費(fèi)的電子技術(shù)研發(fā)工程師必備軟件
基于分布式調(diào)用鏈監(jiān)控技術(shù)的全息排查功能
如何開(kāi)發(fā)一款自己的App
CodeViz--一款分析C/C++源代碼中函數(shù)調(diào)用關(guān)系的調(diào)用
mongodb可視化工具如何使用_介紹一款好用 mongodb 可視化工具

開(kāi)發(fā)一款segmentation標(biāo)記的工具
一款用于Windows的開(kāi)源反rookit (ARK)工具

一款?跨平臺(tái)指紋識(shí)別工具原理解析

系統(tǒng)調(diào)用:用戶棧與內(nèi)核棧的切換(上)

評(píng)論