研发排查问题的利器:一款方法调用栈跟踪工具

news/2025/10/27 17:33:22/文章来源:https://www.cnblogs.com/Jcloud/p/19169716

导语

本文从日常值班问题排查痛点出发,分析方法复用的调用链路和上下文业务逻辑,通过思考分析,借助栈帧开发了一个方法调用栈的链式跟踪工具,便于展示一次请求的方法串行调用链,有助于快速定位代码来源和流量入口,有效提升研发和运维排查定位效率。期望在大家面临类似痛点时可以提供一些实践经验和参考,也欢迎大家合适的场景下接入使用。

 

现状分析

在系统值班时,经常会有人拿着报错截图前来咨询,作为值班研发,我们则需要获取尽可能多的信息,帮助我们分析报错场景,便于排查识别问题。

例如,下图就是一个常见的的报错信息截图示例。

从图中,我们可以初步获取到一些信息:

•菜单名称:变更单下架,我们这是变更单下架操作时的一个报错提醒。

•报错信息:序列号状态为离库状态,请检查。

•其他辅助信息:例如用户扫描或输入的86开头编码,SKU、商品名称、储位等。

 

这时会有一些常见的排查思路:

1、根据提示,将用户输入的86编码,按照提示文案去检查用户数据,即作为序列号编码,去看一下序列号是否存在,是否真的是离库了。

2、如果86编码确实是序列号,而且真的是离库了,那么基本上可以快速结案了,这个86编码确实是序列号并且是已离库,正如提示文案所示,这时跟提问人做好解释答疑即可。

3、如果第2步排查完,发现86编码不是序列号编码,或并非离库状态,即与提示文案不符,这时就要定位报错文案的代码来源,继续查看代码逻辑来进行判案了。(这种也比较常见,一种是报错场景较多,但提示文案比较单一,不便于在提示文案中覆盖所有报错场景;另一种提示文案陈旧未跟随需求演变而更新。这两点可以通过细分场景细化对应的报错文案,或更新报错文案,使得报错文案更优更新,但不是本文讨论的重点。)

4、如何根据报错文案快速找到代码来源呢?一般我们会在代码库中搜索提示文案,或者在日志中检索报错信息,辅助定位代码来源,后者依赖于代码中打印了该报错信息,且日志级别配置能够确保该信息打印到日志文件中。

5、倘若我们根据提示文案搜索代码时,发现该提示文案有多处代码出现,此时就较为复杂了,我们需要进一步识别,哪个才与本次报错直接有关。

 

 

 

 

 

每个方法向上追溯,又发现调用来源众多:

 

 

 

 

 

 

 

 


 

在业务复杂的系统中,方法复用比较常见,不同的上下文和参数传递,也有着不同的业务逻辑判断和走向。

这时,基本上进入到本文要讨论的痛点:如何根据有限的提示信息快速定位代码来源?以便于分析报错业务场景,答疑解惑或快速处理问题。

 

屏幕前的小伙伴,如果你也经常值班排查问题,应该也会有类似的痛点所在。

 

启发

这是我想到了Exception异常机制,作为一名Coder,我们对异常堆栈再熟悉不过了,异常堆栈是一个“可爱”又“可恨”的东西,“可爱”在于异常堆栈确实可以帮助我们快速定位问题所在,“可恨”在于有异常基本上就是有问题,堆栈让我们审美疲劳,累觉不爱。

 

下面是一个Java语言的异常堆栈信息示例:

 

 

 

 

异常类体系和异常处理机制在本文中不是重点,不做过多赘述,本文重点希望能从异常堆栈中获取一些启发。

 

让我们近距离再观察一下我们的老朋友。

在异常堆栈信息中,主要有四类信息:

•全限定类名

•方法名

•文件名

•代码行号

 

这四类信息可以帮助我们有效定位代码来源,而且堆栈中记录行先后顺序,也表示着异常发生的第一现场、第二现场、第三现场、……,以此传递。

 

这让我想起了JVM方法栈中的栈帧。

每当一个方法被调用时,JVM会为该方法创建一个新的栈帧,并将其压入当前线程的栈(也称为调用栈或执行栈)中。栈帧包含了方法执行所需的所有信息,包括局部变量、操作数栈、常量池引用等。

 

 

 

 

 

 

思路

从Java中的Throwable中,可以看到staceTrace的get和set,这个StackTraceElement数组里面存放的信息就是我们在异常堆栈中经常看到的信息。

 

 

 

 

 

 

 

 

 

 

 

再次放一下这张图,方便对照着看。

 

 

 

 

StackTraceElement类的注释中赫然写着:

StackTraceElement represents a stack frame.

对,StackTraceElement代表着一个栈帧。

 

这个StackTraceElement就是我要找的东西,即使非异常情况下,每个线程在执行方法调用时都会记录栈帧信息。

 

 


 

按照方法调用先后顺序,将调用栈中方法依次串联起来,就像糖葫芦一样,就可以得到我想要的方法调用链了。

NEXT,我可以动工写个工具了。

 

工具开发

工具的核心代码并不复杂,StackTraceElement 也是 Java JDK 中现成的,我所做的工作主要是从中过滤出必要的信息,加工简化成,按照顺序整理成链式信息,方便我们一眼就可以看出来方法的调用链。

 

 

 

 

 

入参介绍

pretty:表示是只拼接类和方法,不拼接文件名和行号,非 pretty 是四个都会拼接。

simple:表示会过滤一些我们代码中场景的代理增强出来的方法的信息输出。

specifiedPrefix:指定保留相应的包路径堆栈信息,去掉一些过多的中间件信息。

 

其他还会过滤一些常见代理的堆栈信息:

•FastClassBySpringCGLIB

•EnhancerBySpringCGLIB

•lambda$

•Aspect

•Interceptor

 

对此,还封装了一些默认参数的方法,使用起来更为方便。

 

 

 

 

还有一些其他工具方法也可以使用:

 

 

 

 

使用效果

1、不过滤中间件、代理增强方法的调用栈信息

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、指定包路径过滤中间件后的调用栈栈信息

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代理增强之后的调用栈信息

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、去掉一些自定义代理之后的调用栈栈信息

StockTransferAppServiceImpl#increaseStock ==> StockTransferServiceImpl#increaseStock ==> BaseStockOperation#go ==> DataBaseExecutor#operate ==> DataBaseExecutor#execute ==> StockRetryExecutor#operateStock ==> StockRepositoryImpl#operateStock

 

5、如果带上文件名和行号后的调用栈栈信息

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)

 

线上应用实践

 


 

接入方法调用栈跟踪工具后,根据报错提示词,可以检索到对应日志,从 ImmediateTransferController#offShelf ==> AopConfig#pointApiExpression ==> TransferOffShelfAppServiceImpl#offShelf ==> TransferOffShelfAppServiceImpl#doOffShelf 中顺藤摸瓜可以快速找到流量入口的代码位置。

 

 


 

 

 


 

 

适用场景

该方法调用栈工具类,可以在一些堆栈信息进行辅助排查分析的地方进行预埋,例如:

•业务异常时输出堆栈到日志信息中。

•业务监控告警信息中加入调用栈信息。

•一些复用方法调用复杂场景下,打印调用栈信息,展示调用链,方便分析。

•其他一些场景等。

 

延伸

在《如何一眼定位SQL的代码来源:一款SQL染色标记的简易MyBatis插件》一文中,我发布了一款SQL染色插件,该插件目前已有statementId信息,还支持通过SQLMarkingThreadLocal传递自定义附加信息。其他BGBU的技术小伙伴,也有呼声,希望在statementId基础上可以继续追溯入口方法。通过本文引入的方法调用栈跟踪工具,我在SQL染色插件中增加了方法调用栈染色信息。

 

SQL染色工具新版特性,欢迎大家先在TEST和UAT环境尝鲜试用,TEST和UAT环境验证没问题后,再逐步推广正式环境。

升级方法:

 

1、sword-mybatis-plugins版本升级至1.0.8-SNAPSHOT。

 

2、同时新引入本文的工具依赖

<!-- http://sd.jd.com/article/45616?shareId=105168&isHideShareButton=1 -->
<dependency><groupId>com.jd.sword</groupId><artifactId>sword-utils-common</artifactId><version>1.0.3-SNAPSHOT</version>
</dependency>

 

3、mybatis config xml 配置文件按最新配置调整

<!-- http://sd.jd.com/article/42942?shareId=105168&isHideShareButton=1 -->
<!-- SQLMarking Plugin,放在第一个Plugin的位置,不影响其他组件,但不强要求位置,也可以灵活调整顺序位置 -->
<plugin interceptor="com.jd.sword.mybatis.plugin.sql.SQLMarkingInterceptor"><!-- 是否开启SQL染色标记插件 --><property name="enabled" value="true"/><!-- 是否开启方法调用栈跟踪 --><property name="stackTraceEnabled" value="true"/><!-- 指定需要方法调用栈跟踪的package,减少信息量,value配置为自己工程的package路径,多个路径用英文逗号分割 --><property name="specifiedStackTracePackages" value="com.jdwl.wms.stock"/><!-- 忽略而不进行方法堆栈跟踪的类名列表,多个用英文逗号分割,减少信息量 --><property name="ignoredStackTraceClassNames" value=""/><!-- 结合CPU利用率和性能考虑,方法调用栈跟踪采集率配置采集率,配置示例: m/n,表示n个里面抽m个进行采集跟踪 --><!-- 预发环境和测试环境可以配置全采集,例如配置1/1,生产环境可以结合CPU利用率和性能考虑按需配置采集率 --><property name="stackTraceSamplingRate" value="1/2"/><!-- 是否允许SQL染色标记作为前缀,默认false表示仅作为后缀 --><property name="startsWithMarkingAllowed" value="false"/><!-- 方法调用栈跟踪最大深度,减少信息量 --><property name="maxStackDepth" value="10"/>
</plugin>

 

或代码配置方式

/*** SQLMarking Plugin* http://sd.jd.com/article/42942?shareId=105168&isHideShareButton=1** @return*/@Beanpublic SQLMarkingInterceptor sQLMarkingInterceptor() {SQLMarkingInterceptor sQLMarkingInterceptor = new SQLMarkingInterceptor();Properties properties = new Properties();// 是否开启SQL染色标记插件properties.setProperty("enabled", "true");// 是否开启方法调用栈跟踪properties.setProperty("stackTraceEnabled", "true");// 指定需要方法调用栈跟踪的package,减少信息量,value配置为自己工程的package路径,多个路径用英文逗号分割properties.setProperty("specifiedStackTracePackages", "com.jdwl.wms.picking");// 结合CPU利用率和性能考虑,方法调用栈跟踪采集率配置采集率,配置示例: m/n,表示n个里面抽m个进行采集跟踪// 预发环境和测试环境可以配置全采集,例如配置1/1,生产环境可以结合CPU利用率和性能考虑按需配置采集率properties.setProperty("stackTraceSamplingRate", "1/2");// 是否允许SQL染色标记作为前缀,默认false表示仅作为后缀properties.setProperty("startsWithMarkingAllowed", "false");sQLMarkingInterceptor.setProperties(properties);return sQLMarkingInterceptor;}

 

接入效果

SELECTid,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
FROMst_stock
WHEREdeleted = 0AND 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 */

 

如何接入本文工具?

如果小伙伴也有类似使用诉求,大家可以先在测试、UAT环境接入试用,然后再逐步推广线上生产环境。

1、新引入本文的工具依赖

<dependency><groupId>com.jd.sword</groupId><artifactId>sword-utils-common</artifactId><version>1.0.3-SNAPSHOT</version>
</dependency>

 

2、使用工具类静态方法

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...)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/947948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年多功能综合杆厂家排名前十推荐

摘要 多功能综合杆作为智能城市建设的核心基础设施,近年来行业发展迅速,集成照明、监控、通信、环境监测等多种功能,提升城市管理效率和市民生活品质。据行业报告显示,全球智能杆市场预计到2025年将达到150亿美元,…

PlantUML-EBNF语法学习

背景 一图胜千言。有时候用图表表达意图比较直观、容易理解。但是用一般画图工具比较麻烦,也不容易修改,所以用文本描述设计意图,然后自动呈现图表就是一种理想的方案。 本文用EBNF语法图描述了语法图的语法。 参考…

2025年度在线网站客服系统综合排行榜正式发布

在信息爆炸的今天,客户耐心正以秒计算。您的网站访客可能只因一个无人应答的咨询窗口便悄然离去。一个得力的在线客服系统,早已不再是简单的“沟通工具”,而是企业挽留潜在客户、提升服务体验、最终实现业绩转化的战…

JDD Oxygen智能零售论坛 | 《大模型时代的广告营销变革与实践》

核心观点 1. 通用大模型想解决营销领域问题需向垂类模型转型。 “全才”通用大模型难覆盖广告营销全流程,需升级为“懂营销”的垂直模型,实现从“知道”到“落地执行”的三维跨越。 2. 广告智能体破解传统投放门槛高…

常用Web 实时通信技术:原理+选型,一篇通关

在 Web 开发中,实时通信技术的核心目标是实现客户端(Browser)与服务器之间低延迟、双向 / 单向的动态数据交互,而非传统 HTTP 的 “请求 - 响应” 模式。以下是 Web 端最常用的实时通信技术,从概念、原理特点、适…

ERP和CRM、SRM、MES之间的关系,怎么理解?

我的一个创业的朋友最近来问我,ERP、CRM、SRM和MES这四个系统到底该如何配合?这是很多公司在推进数字化转型时,都会遇到一个问题,我们公司也不例外。 虽然它们各自有不同的职能,但往往会让人搞不清楚。有时候,企…

2025年市面上新加坡留学品牌、行业内公司及口碑产品推荐排行

摘要 随着全球教育一体化加速,新加坡留学市场在2025年呈现稳健增长态势,行业竞争加剧,品牌分化明显。本文基于行业数据和用户反馈,梳理前十名新加坡留学服务提供商,并附详细排名表单供参考,旨在帮助读者做出明智…

滥用static inline

结论先行你现在的写法namespace hzi {static inline Config config; // ← 注意 static }会让每个包含 config.hpp 的 .cpp 文件都拥有自己独立的一份 config 对象。 因此: – 它们在内存中地址不同; – 在一个 .c…

2025年市面上氟碳铝单板品牌、市场氟碳铝单板公司、国内氟碳铝单板生产厂家、2025年氟碳铝单板品牌、口碑好的氟碳铝单板产品综合评测

文章摘要 氟碳铝单板作为现代建筑外墙装饰的核心材料,近年来随着绿色建筑和城市化进程的加速,行业年复合增长率超过15%(数据来源:中国建筑材料联合会2024年报告)。本文基于市场调研和用户反馈,为您呈现2025年氟碳…

2025/10/25(补)

A 用时:1h 预期:100pts 实际:100pts 处理出每个点开始有多少个三元组,依次用二分确定 \((x,y,z)\)。 总结:做题时要集中注意力,越快做完越好。 B 用时:1h 预期:60pts 实际:40pts 贪心的加点,加入的点的贡献为…

第二十四篇

今天是10月27日,绝望星期一,今天又在尝试链接数据库进行开发,当然又失败了,难受

2025强网杯ezphp复现

ezphp 终于,终于,终于肝出来了,其实肝了一天之后就基本全部出来了,一开始调用在匿名类中的全局函数的这一步卡了很久,于是先跳过这一步,对其他步骤先进行分析复现,到后来就差这最后一步被卡死了,百思不得其解,后来注意…

2025年西安楼房地产排名、口碑推荐榜单、公司推荐排行、开发商排名及高评价楼房地产分析

摘要 随着西安城市化进程加速和房地产市场持续升温,2025年西安楼房地产行业呈现出稳健增长态势,消费者对高品质住宅的需求日益凸显。本文基于行业数据和用户评价,整理了西安楼房地产排名前十的榜单,旨在为购房者提…

漏洞报告被拒绝的常见原因及避免方法

本文深入探讨了在漏洞赏金项目中漏洞报告被拒绝的常见原因,包括报告不清晰、缺乏重现步骤等问题,并提供了实用的改进建议,帮助安全研究人员提高报告质量并获得认可。24. 漏洞报告被拒绝的常见原因(以及如何避免) …

2025年市面上别墅石材品牌、行业内别墅石材公司、市场别墅石材供应商、目前别墅石材源头厂家、口碑好的别墅石材品牌推荐排行榜

摘要 别墅石材行业近年来随着高端住宅市场的蓬勃发展,需求持续增长,消费者对石材的品质、设计和供应商信誉要求越来越高。本文基于2025年市场数据,为您提供一份权威的别墅石材品牌推荐榜单,涵盖市面上热门品牌和供…

Spring AI alibaba Prompt模板Advisor自定义 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11-文件上传

11-文件上传$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");文件上传和下载的原理文件上传从浏览器上传文件的时候需要对文件进行限制 这个限制是在配置文件…

wqe

wqe报告名称:AR(Active Area Reverse)fin cut工艺业务需求与数据目标报告(深度调研增强版) 版本:V1.2 日期:2025-9-4 编制人:[李晓睿/数字智能部] 审核人:[姓名/部门] 引言 1.1 背景与业务场景项目发起背景(…

集采带量下医疗器械生产厂家如何通过数字化转型实现降本增效

数字化管理系统正在成为医疗器械生产厂家应对集采挑战的“手术刀”,精准切除管理冗余和效率低下的痛点。数字化管理系统正在成为医疗器械生产厂家应对集采挑战的“手术刀”,精准切除管理冗余和效率低下的痛点。 “订…