【问题标题】:How to effectively group non fatal exceptions in Crashlytics (Fabrics)?如何在 Crashlytics (Fabrics) 中有效地对非致命异常进行分组?
【发布时间】:2018-05-19 03:51:39
【问题描述】:

我们在应用中使用 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 仪表板中,但它在单个选项卡中显示所有崩溃。这些可能是相同或不同 react-native 组件的不同崩溃。

因此,我们无法找出特定崩溃的实例。需要手动遍历每个崩溃实例。

我猜它需要创建异常的类的名称,在本例中是 PlatformNativeModuleCallExceptionHandler。 我尝试创建自己的自定义异常类,但这也没有帮助。

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 我已经在我对这个问题的回答中发布了一个解决方法。
    • 查看我的答案,如果您将原始异常设置为异常的原因,它应该可以按预期工作。
    【解决方案2】:

    我通过为异常设置自定义堆栈跟踪来解决此问题。 new Exception(exceptionMessage) 将在那里创建异常,我们所做的是抛出一个异常,在 catch 中调用我的 handleException() 对应的异常消息,并在 exceptionMessage 中提供实际堆栈跟踪。一些解析和 exceptionMessage 可用于使用exception.setStackTrace() 设置新创建的异常的堆栈跟踪。实际上,这在我的项目中是必需的,只是因为它是跨语言的,对于常规项目,只需传递在感兴趣的地方抛出并捕获的异常就可以了。

    【讨论】:

      【解决方案3】:

      我发现最好的方法是手动切断堆栈跟踪的共享部分:

      
      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】:

        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); } }
        

        顺便说一句,Crashlytics 将忽略异常中的消息字符串。

        【讨论】:

          【解决方案5】:

          我刚才正在研究这个,因为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"),则两者将组合在一起,因为行和方法没有改变。

          【讨论】:

          • 什么是@Override public void handleException(Exception e) 找不到任何信息哪个类是父类?
          • @VLeonovs 我只是指问题中粘贴的代码 sn-p。
          猜你喜欢
          • 2020-03-11
          • 2019-01-13
          • 2017-06-05
          • 1970-01-01
          • 2018-03-08
          • 1970-01-01
          • 2018-05-13
          • 1970-01-01
          • 2012-04-22
          相关资源
          最近更新 更多