如何在Crashlytics(Fabrics)中有效地分组非致命异常?

时间:2017-12-05 13:08:00

标签: android react-native crashlytics twitter-fabric crashlytics-android

我们在我们的应用中使用Crashlytics作为崩溃报告工具。 对于Android本机崩溃,它工作正常并正确分组崩溃。 我们的应用程序在react-native中也没有很少的组件。对于这些组件中发生的崩溃,我们会捕获它们,然后将它们作为非致命异常记录到Crashlytics。

public class PlatformNativeModuleCallExceptionhandler implements 
NativeModuleCallExceptionHandler {
@Override
public void handleException(Exception e) {
    try {
        .
        .
        .
        Crashlytics.logException(new Exception(exceptionString));
    } catch (Exception ex) {}
}

Crashlytics仪表板中记录了崩溃,但它显示了单个选项卡中的所有崩溃。这些可能是相同或不同反应原生组件的不同崩溃。

enter image description here

由于这个原因,我们无法找到特定崩溃的实例。需要手动完成崩溃的每个实例。

我想它需要创建异常的类的名称,在本例中为 PlatformNativeModuleCallExceptionHandler 。 我尝试创建自己的自定义异常类,但这也无济于事。

有人知道我们如何更好地将非致命异常分组在这里吗? 所有类似的崩溃应该与它们的总实例组合在一起。

5 个答案:

答案 0 :(得分:3)

Crashlytics使用该方法和崩溃行号来对崩溃进行分组,因此如果您有一个针对所有非致命因素的异常处理程序方法,它们将被组合在一起。目前没有解决方法。

答案 1 :(得分:2)

根据生成异常的行号对Crashlytics进行分组,并使用异常类型对其进行标记。如果您知道所有类型的例外,则可以在不同的行上生成每个例外。您还可以将字符串映射到自定义异常类型,以便在Crashlytics中更容易识别它们。

以下是一个例子:

public void crashlyticsIsGarbage(String exceptionString) {
    Exception exception = null;
    switch(exceptionString) {
        case "string1": exception = new String1Exception(exceptionString);
        case "string2": exception = new String2Exception(exceptionString);
        case "string3": exception = new String3Exception(exceptionString);
        case "string4": exception = new String4Exception(exceptionString);
        default: exception = new Exception(exceptionString);
    }
    Crashlytics.logException(exception);
}

class String1Exception extends Exception { String1Exception(String exceptionString) { super(exceptionString); } }
class String2Exception extends Exception { String2Exception(String exceptionString) { super(exceptionString); } }
class String3Exception extends Exception { String3Exception(String exceptionString) { super(exceptionString); } }
class String4Exception extends Exception { String4Exception(String exceptionString) { super(exceptionString); } }
BTW,Crashlytics将忽略Exception中的消息字符串。

答案 2 :(得分:2)

我通过将自定义堆栈跟踪设置为异常来解决此问题。 new Exception(exceptionMessage)会自己在其中创建异常,我们所做的是抛出一个异常,该异常在catch中称为我的handleException()的对应对象,并带有exceptionMessage中提供的实际堆栈跟踪。某些解析和exceptionMessage可用于使用exception.setStackTrace()对新创建的异常设置堆栈跟踪。实际上,这在我的项目中是必需的,只是因为它是跨语言的,对于常规项目,只需传递引发并捕获到感兴趣的位置的异常就行了。

答案 3 :(得分:2)

我发现执行此操作的最佳方法是手动将stacktrace的共享部分切掉:


private fun buildCrashlyticsSyntheticException(message: String): Exception {
  val stackTrace = Thread.currentThread().stackTrace
  val numToRemove = 8
  val lastToRemove = stackTrace[numToRemove - 1]
  // This ensures that if the stacktrace format changes, we get notified immediately by the app
  // crashing (as opposed to silently mis-grouping crashes for an entire release).
  check(lastToRemove.className == "timber.log.Timber" && lastToRemove.methodName == "e",
    { "Got unexpected stacktrace: $stackTrace" })
  val abbreviatedStackTrace = stackTrace.takeLast(stackTrace.size - numToRemove).toTypedArray()
  return SyntheticException("Synthetic Exception: $message", abbreviatedStackTrace)
}

class SyntheticException(
  message: String,
  private val abbreviatedStackTrace: Array<StackTraceElement>
) : Exception(message) {
  override fun getStackTrace(): Array<StackTraceElement> {
    return abbreviatedStackTrace
  }
}

通过这种方式可以将消息Timber.e("Got a weird error $error while eating a taco")参数化,并且该行的所有呼叫都将分组在一起。

显然,numToRemove将需要根据触发非致命事件的确切机制进行更改。

答案 4 :(得分:0)

我刚才正在研究这个问题,因为documentation says

  

记录的异常按异常类型和消息分组。

     

警告:   开发人员应避免在“异常”消息字段中使用唯一值,例如用户ID,产品ID和时间戳。在这些字段中使用唯一值将导致要创建的问题的基数很高。在这种情况下,Crashlytics将限制报告您应用程序中记录的错误。相反,应该将唯一值添加到日志和自定义键。

但是我的经历与众不同。根据我的发现,Alexizamerican said in his answer是正确的,但有一点警告:

按创建异常的方法和行将问题分组,但需要注意的是,此处是考虑到异常的根本原因。

从根本上讲,我的意思是:

public static Throwable getRootCause(Throwable throwable) {
    Throwable cause = throwable;
    while (cause.getCause() != null) {
        cause = cause.getCause();
    }
    return cause;
}

因此,如果您这样做:

@Override
public void handleException(Exception e) {
    // ...
    Crashlytics.logException(e);
}

那应该将异常正确地分组在一起。

此外,如果您这样做:

@Override
public void handleException(Exception e) {
    // ...
    Crashlytics.logException(new Exception(exceptionString, e));
}

这也将正确地将异常分组,因为它将查看e或其原因,或诸如此类的原因,依此类推,直到到达没有其他原因的异常为止,并且查看堆栈跟踪的创建位置。

最后,与what miguel said不同,根据我的经验,异常类型或消息完全不会影响分组。如果您在特定方法的特定行上有FooException("foo"),并且用BarException("bar")替换了它,则由于行和方法没有改变,两者将被分组在一起。