在
Java或
Android中(例如通过ACRA)记录未处理异常的堆栈跟踪时,通常会将堆栈跟踪作为一个简单的长字符串.
现在,所有提供崩溃报告和分析的服务(例如Google Play开发者控制台,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.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;
- }
- }