java - 将 Java/Android 堆栈跟踪分组到唯一的桶中

标签 java android stack-trace acra

在 Java 或 Android 中记录未处理异常的堆栈跟踪(例如通过 ACRA)时,您通常会以普通长字符串的形式获取堆栈跟踪。

现在,所有提供崩溃报告和分析的服务(例如 Google Play Developer Console、Crashlytics)都将这些堆栈跟踪分组到唯一的桶中。这显然很有帮助——否则,您的列表中可能有数万个崩溃报告,但其中可能只有十几个是独一无二的。

例子:

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:200)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
at java.lang.Thread.run(Thread.java:1027)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at com.my.package.MyClass.i(SourceFile:1059)
...

上面的堆栈跟踪可能以多种形式出现,例如由于不同的平台版本,像 AsyncTask 这样的平台类可能会出现不同的行号。

为每个崩溃报告获取唯一标识符的最佳技术是什么?

清楚的是,对于您发布的每个新应用程序版本,崩溃报告都应该单独处理,因为编译源是不同的。在 ACRA 中,您可以考虑使用字段 APP_VERSION_CODE

但除此之外,您如何识别具有独特原因的报告?通过第一行搜索自定义(非平台)类的第一次出现并查找文件和行号?

最佳答案

如果您正在寻找一种方法来获取异常的唯一值,同时忽略特定于操作系统的类,您可以迭代 getStackTrace() 并对不是来自已知操作系统类的每个帧进行哈希处理。我认为将原因异常添加到哈希中也很有意义。它可能会产生一些漏报,但如果您散列的异常是通用的,例如 ExecutionException,那会比误报好。

import com.google.common.base.Charsets;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

public class Test
{

    // add more system packages here
    private static final String[] SYSTEM_PACKAGES = new String[] {
        "java.",
        "javax.",
        "android."
    };

    public static void main( String[] args )
    {
        Exception e = new Exception();
        HashCode eh = hashApplicationException( e );
        System.out.println( eh.toString() );
    }

    private static HashCode hashApplicationException( Throwable exception )
    {
        Hasher md5 = Hashing.md5().newHasher();
        hashApplicationException( exception, md5 );
        return md5.hash();
    }

    private static void hashApplicationException( Throwable exception, Hasher hasher )
    {
        for( StackTraceElement stackFrame : exception.getStackTrace() ) {
            if( isSystemPackage( stackFrame ) ) {
                continue;
            }

            hasher.putString( stackFrame.getClassName(), Charsets.UTF_8 );
            hasher.putString( ":", Charsets.UTF_8 );
            hasher.putString( stackFrame.getMethodName(), Charsets.UTF_8 );
            hasher.putString( ":", Charsets.UTF_8 );
            hasher.putInt( stackFrame.getLineNumber() );
        }
        if( exception.getCause() != null ) {
            hasher.putString( "...", Charsets.UTF_8 );
            hashApplicationException( exception.getCause(), hasher );
        }
    }

    private static boolean isSystemPackage( StackTraceElement stackFrame )
    {
        for( String ignored : SYSTEM_PACKAGES ) {
            if( stackFrame.getClassName().startsWith( ignored ) ) {
                return true;
            }
        }

        return false;
    }
}

关于java - 将 Java/Android 堆栈跟踪分组到唯一的桶中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29063286/

相关文章:

java - 使用OpenGL和3D建模软件之间的区别

安卓适配器和上下文菜单

java - 使用变量调用资源

android - 为 TextView 使用 XML 布局

c++ - SIGSEGV 使用 gdb 时无法获取堆栈跟踪

c# - 包含 Namespace.ClassName.<MethodName>b__f 的堆栈跟踪是什么意思?

c++ - 在库 : symbol structure query 上使用 Breakpad

java - ActiveMQ 消息出队但未使用

java - 在独立的 Java 应用程序中使用 Spring 3 Autowiring

java - Lotus Notes 日历报告