在 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/