【问题标题】:Firebase Crashlytics MacOS - all crashes under AppKitFirebase Crashlytics MacOS - AppKit 下的所有崩溃
【发布时间】:2021-08-01 15:19:05
【问题描述】:

我有一个移植到 MacOS 的 iOS 应用程序。该应用程序将 Firebase 用于 Crashlytics。到目前为止,我通过为该目标创建单独的 Mac 目标和单独的 Firebase 项目,成功地配置了一切。问题是我在控制台中看到的 MacOS 项目的崩溃都在“AppKit”下。示例:

AppKit | -[NSApplication _crashOnException:] + 106

信息量不是很大,是吗...现在,如果我检查崩溃然后转到“键”,我仍然可以获得崩溃异常:

crash_info_entry_0 | Crashing on exception: *** -[__NSCFCalendar rangeOfUnit:startDate:interval:forDate:]: date cannot be nil

但是所有不同的崩溃都归类在 AppKit 崩溃下,所以它不是很有帮助。

我意识到这个问题是由于 AppKit 在默认情况下捕获 MacOS 上的所有异常的默认行为。是否有更好的方法来设置适用于 MacOS 的 Crashlytics,以便获得更精细的报告,例如在 iOS 和其他平台上?

【问题讨论】:

    标签: ios firebase macos crashlytics


    【解决方案1】:

    经过大量研究,我发现没有完美的解决方案。我尝试覆盖 NSApplication 并将其设置为 NSPrincipalClass,甚至实现了 Sentry - 没有成功。但我找到了一种绕过 AppKit 的方法,使用方法 swizzling 和 FIRExceptionModel。

    注意:首先,要让 Firebase Crashlytics 在 MacOS 上运行,您需要在 AppDelegate 的 didFinishLaunchingWithOptions 中添加以下内容:

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults registerDefaults:@{@"NSApplicationCrashOnExceptions" : @"YES"}];
    

    然后你需要创建一个 NSApplication 的类别并调配方法 _crashOnException:

    #import <objc/runtime.h>
    #import "NSApplication+CrashReport.h"
    #import <FIRCrashlytics.h>
    
    @implementation NSApplication (CrashReport)
    
    +(void)load {
        static dispatch_once_t once_token;
        dispatch_once(&once_token,  ^{
            SEL crashOnExceptionSelector = @selector(_crashOnException:); // Ignore 'Undeclared selector' warning.
            SEL crashOnExceptionReporterSelector = @selector(reported__crashOnException:);
            Method originalMethod = class_getInstanceMethod(self, crashOnExceptionSelector);
            Method extendedMethod = class_getInstanceMethod(self, crashOnExceptionReporterSelector);
            method_exchangeImplementations(originalMethod, extendedMethod);
        });
    }
    
    - (void)reported__crashOnException:(NSException*)exception {
        NSArray<NSString*> *stacktrace = [exception callStackSymbols];
        [[FIRCrashlytics crashlytics]setCustomValue:stacktrace forKey:@"mac_os_stacktrace"];
        FIRExceptionModel *errorModel = [FIRExceptionModel exceptionModelWithName:exception.name reason:exception.reason];
        // The below stacktrace is hardcoded as an example, in an actual solution you should parse the stacktrace array entries.
        errorModel.stackTrace = @[
            [FIRStackFrame stackFrameWithSymbol:@"This stacktrace is fabricated as a proof of concept" file:@"Hello from Serge" line:2021],
            [FIRStackFrame stackFrameWithSymbol:@"__exceptionPreprocess" file:@"CoreFoundation" line:250],
            [FIRStackFrame stackFrameWithSymbol:@"objc_exception_throw" file:@"libobjc.A.dylib" line:48],
            [FIRStackFrame stackFrameWithSymbol:@"-[__NSCFCalendar rangeOfUnit:startDate:interval:forDate:]" file:@"CoreFoundation" line:453]
        ];
        // Note: ExceptionModel will always be reported as a non-fatal.
        [[FIRCrashlytics crashlytics] recordExceptionModel:errorModel];
        [self reported__crashOnException:exception];
    }
    
    @end
    

    此代码作为要点:https://gist.github.com/sc941737/c0c4542401ce203142c93ddc9b05eb1f

    这意味着异常不会被报告为崩溃,而是非致命事件。所以我建议设置一个额外的自定义键来更容易地过滤非致命的崩溃。有关详细信息,请参阅 Firebase 文档:https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=ios

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-20
      • 2019-04-18
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-29
      • 2021-06-30
      相关资源
      最近更新 更多