Cleaning noise out of Java stack traces

梦想的初衷 提交于 2019-12-18 03:49:09

问题


My Java stack traces have a lot of entries that I don't care about, showing method invocation going through proxies and Spring reflection methods and stuff like that. It can make it pretty hard to pick out the part of the stack trace that's actually from my code. Ruby on Rails includes a "stack trace cleaner" where you can specify a list of stack trace patterns to omit from printed stack traces - what's the best way to do something like that, universally, for Java?

It'd be best if this worked everywhere, including in the Eclipse jUnit runner.


回答1:


eclipse has a preference Stack trace filter patterns (look at java > junit or search for stacktrace in the preferences). You can ignore packages (also with wildcards), classes or methods. Does work for direct Test calls (via Run as junit Test), not for commandline runs like ant or maven.




回答2:


intellij-idea allows customizable stack trace folding, especially useful with dynamic languages.


(source: jetbrains.com)

and an Analyzing external stack traces tool.

I can imagine general tool/filter working on logging framework (like logback or log4j) level. I don't think there is any general support for that, but I think it is a great idea to implement this. I will have a look, maybe it is not that much work.

UPDATE: I implemented filtering irrelevant stack trace lines in logs for logback, also follow LBCLASSIC-325.




回答3:


I actually wrote an open source library MgntUtils (available at Github and maven central) that contains several utilities. Here is a link to an article about the library: MgntUtils Open Source Java library. One of the utilities is a general purpose stacktrace filter that I used extensively and found it very useful. The class is called TextUtils and it has method getStacktrace() with several overridden signatures. It takes a Throwable instance and allows to set a package prefix of the packages that are relevant. Let's say your company's code always resides in packages that start with "com.plain.*" So you set such a prefix and do this

logger.info(TextUtils.getStacktrace(e, true, "com.plain."));

this will filter out very smartly all the useless parts of the trace leaving you with very concise stacktrace. Also I found it very convinient to pre-set the prefix and then just use the convinience method

TextUtils.getStacktrace(e);

It will do the same. To preset the prefix just use method

setRelevantPackage("com.plain.");

Also if you use Spring environment you can add the following segment to your Spring configuration and then you all set:

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetClass" value="com.mgnt.utils.TextUtils"/>
  <property name="targetMethod" value="setRelevantPackage"/>
  <property name="arguments" value="com.plain."/>
</bean>

The library comes with well written (I hope) Javadoc that explains everything in detail. But here is a little teaser: you will get a following stacktrace:

at com.plain.BookService.listBooks()
at com.plain.BookService$$FastClassByCGLIB$$e7645040.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke()
...
at com.plain.LoggingAspect.logging()
at sun.reflect.NativeMethodAccessorImpl.invoke0()
...
at com.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks()
at com.plain.web.BookController.listBooks()

instead of

at com.plain.BookService.listBooks()
at com.plain.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.plain.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.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks()
at com.plain.web.BookController.listBooks()



回答4:


For log4j:

package package1;

public class FilteringThrowableRenderer implements ThrowableRenderer {
    private static final String PACKAGES_SEPARATOR = "\\s*,\\s*";

    private final static String TRACE_PREFIX = "\tat ";

    private static final String FILTERED_WARNING = " [Stacktrace is filtered]";

    ThrowableRenderer defaultRenderer = new EnhancedThrowableRenderer();

    List<String> skippedLinePrefixes;

    public FilteringThrowableRenderer() {
        String skippedPackagesString = "java,org"; // TODO: move it to config
        String[] skippedPackages =
            skippedPackagesString.trim().split(PACKAGES_SEPARATOR);
        skippedLinePrefixes = new ArrayList<String>(skippedPackages.length);
        for (String packageName : skippedPackages) {
            skippedLinePrefixes.add(TRACE_PREFIX + packageName);
        }
    }

    @Override
    public String[] doRender(Throwable throwable) {
        String[] initialTrace = defaultRenderer.doRender(throwable);
        if (!skippedLinePrefixes.isEmpty()) {
            List<String> result = new ArrayList<String>(initialTrace.length);

            boolean filtered = false;
            trace: for (String element : initialTrace) {
                for (String skippedLinePrefix : skippedLinePrefixes) {
                    if (element.startsWith(skippedLinePrefix)) {
                        filtered = true;
                        continue trace;
                    }
                }
                result.add(element);
            }
            if (filtered && result.size() > 0) {
                result.set(0, result.get(0) + FILTERED_WARNING);
            }
            return result.toArray(new String[result.size()]);
        } else {
            return initialTrace;
        }
    }
}

to enable it with code:

ThrowableRendererSupport loggerRepository =
    (ThrowableRendererSupport) LogManager.getLoggerRepository();
loggerRepository.setThrowableRenderer(new FilteringThrowableRenderer());

or with log4j.properties:

log4j.throwableRenderer=package1.FilteringThrowableRenderer



回答5:


Not exactly what you are looking for (and, to the best of my knowledge, there is no universal solution for your problem, at least I've never heard of a famous tool to clean and extract info from Java stacktraces).

Anyway, this post from July, 05, 2011 at Faux' Blog describes a Java Agent in early stages whose purpose is to enrich (and not filter) stack traces. It evens provide a link to a git repository with a mavenized project. Maybe you can go from here, tweak his code and roll your own solution (who knows, maybe even start an open source project).




回答6:


This plugin's pretty nice

https://marketplace.eclipse.org/content/grep-console

Just a generalized grep formatting utility for the Eclipse console, so no additional dependencies. I format all my irrelevant noise to grey text.



来源:https://stackoverflow.com/questions/9606614/cleaning-noise-out-of-java-stack-traces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!