但是有一件事一直困擾著我一段時間。 臭名昭著的“ 來自地獄的堆棧跟蹤 ”癥狀–堆棧跟蹤包含數百種不相關的,隱秘的,通常是自動生成的方法。 AOP框架和過度設計的庫往往會產生瘋狂的長執行跟蹤。 讓我展示一個真實的例子。 在一個示例應用程序中,我正在使用以下技術堆棧:

顏色很重要。 根據框架/層的顏色,我繪制了一個示例堆棧跟蹤,該堆棧跟蹤是由于嘗試從數據庫中獲取數據時在某處深處拋出異常而引起的:

不再那么愉快,你不覺得嗎? 在第一張圖中,將Spring放在應用程序和Hibernate之間是一個極大的簡化。 Spring框架是一個膠合代碼,用于連接并攔截周圍層的業務邏輯。 這就是為什么應用程序代碼被數十行技術調用分散和交織的原因(請參見綠線)。 我在應用程序中投入了盡可能多的內容(Spring AOP,方法級別的@Secured批注,自定義方面和攔截器等)來強調該問題-但這不是特定于Spring的。 EJB服務器在EJB調用之間生成同樣可怕的堆棧跟蹤(…從地獄)。 我應該在乎嗎? 想想看,當您從BookController.listBooks()
無辜地調用BookService.listBooks()
,您希望看到此消息嗎?
at com.blogspot.nurkiewicz.BookService.listBooks()
at com.blogspot.nurkiewicz.BookService$$FastClassByCGLIB$$e7645040.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke()
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed()
at com.blogspot.nurkiewicz.LoggingAspect.logging()
at sun.reflect.NativeMethodAccessorImpl.invoke0()
at sun.reflect.NativeMethodAccessorImpl.invoke()
at sun.reflect.DelegatingMethodAccessorImpl.invoke()
at java.lang.reflect.Method.invoke()
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs()
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod()
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.interceptor.AbstractTraceInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept()
at com.blogspot.nurkiewicz.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks()
at com.blogspot.nurkiewicz.web.BookController.listBooks()
您甚至注意到它們之間存在自定義方面嗎? 事實就是如此,如今堆棧跟蹤中雜亂無章,幾乎不可能遵循實際的業務邏輯。 我們擁有的最好的故障排除工具之一是在99%的情況下都不需要與框架相關的無關內容。
工具和IDE在減少噪聲方面做得很好。 Eclipse具有用于Junit的堆棧跟蹤過濾器模式 , IntelliJ IDEA支持控制臺折疊自定義 。 另請參閱: 從Java堆棧跟蹤中清除噪音 ,這啟發了我寫這篇文章。 那么,為什么在Logback等日志記錄框架中根本沒有這種可能性呢?
我在Logback中實現了一個非常簡單的增強。 基本上,您可以定義一組應該從堆棧跟蹤中排除的堆棧跟蹤框架模式。 通常,您將使用不希望看到的包或類名。 這是啟用了新功能的示例logback.xml
摘錄:
<root level="ALL"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} | %-5level | %thread | %logger{1} | %m%n%rEx{full,java.lang.reflect.Method,org.apache.catalina,org.springframework.aop,org.springframework.security,org.springframework.transaction,org.springframework.web,sun.reflect,net.sf.cglib,ByCGLIB}</pattern></encoder></appender>
</root>
在過濾幾乎整個Spring框架+ Java反射和CGLIB類時,我有點極端。 但這只是給您一種印象,您可以得到多少。 將我的增強功能應用到Logback后,出現了非常相同的錯誤:

提醒一下,綠色是我們的應用程序。 最終在一個地方,最終您可以真正看到發生錯誤時代碼在做什么:
at com.blogspot.nurkiewicz.DefaultBookHelper.findBooks()
at com.blogspot.nurkiewicz.BookService.listBooks()
at com.blogspot.nurkiewicz.LoggingAspect.logging()
at com.blogspot.nurkiewicz.web.BookController.listBooks()
更簡單? 如果您喜歡此功能,我打開了一張票LBCLASSIC-325 : 篩選出選定的堆棧跟蹤框架 。 投票討論。 這只是一個概念證明,但是如果您想看一下實現(歡迎改進!),可以在我的Logback 分支下找到(大約20行代碼)。
參考:從JCG合作伙伴的 日志中過濾無關的堆棧跟蹤行 ? Java和社區博客中的Tomasz Nurkiewicz。
翻譯自: https://www.javacodegeeks.com/2012/03/filter-irrelevant-stack-trace-lines-in.html