iOS崩溃日志捕获,调试信息.. 捕获并通过电子邮件发送给开发团队。

56

最近我们遇到了一个情况,想要查看用户在设备上使用的应用程序的调试信息。因此,我正在寻找一种方法,在设备上查找日志,将其作为内联文本粘贴到邮件中,并允许用户发送它。

有什么想法吗?以下是问题: 1)在设备上找到调试日志 2)打开文件并将文件内容附加为内联文本到电子邮件中。 3)允许用户在下次应用程序启动时发送邮件。

谢谢!


1
iTunes实际上会自动为您完成这个操作。请检查您的iTunes Connect账户。 - Till
9个回答

67

感谢大家的所有建议。我把你们的解决方案合并成一个,以解决我的问题。这是我做出来的东西...当然,我没有编译代码,它只是半成品...但是一旦我在我的代码中实现它,我很快就会完善它。

将NSLog记录到文件中 如何将NSLog记录到文件中 LOG2FILE

#if TARGET_IPHONE_SIMULATOR == 0    
    NSArray *paths =  
    NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);        
    NSString *documentsDirectory = [paths objectAtIndex:0];    
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.log"];    
    freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
#endif

捕获崩溃并将其记录到文件中

首先,创建一个函数来处理错误并将其输出到控制台(以及任何其他您想要执行的操作):

void uncaughtExceptionHandler(NSException *exception) {    
    NSLog(@"CRASH: %@", exception);      
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);    
    // Internal error reporting
}

接下来,在您的应用程序委托中添加异常处理程序:

-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:  
(NSDictionary*)launchOptions
{   
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);    // Normal launch stuff
}

在info.plist中设置一个名为“Crashed”的变量,然后通过以下方式进行读写操作

- (void)readPlist
 {
      NSString *localizedPath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"plist"];        
      NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:localizedPath];

    NSString *crashed;
    crashed = [plistDict objectForKey:@"Crashed"];
}


- (void)writeToPlist
{
    NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

    [plistDict setValue:@"YES" forKey:@"Crashed"];
    [plistDict writeToFile:filePath atomically: YES];
}

应用程序启动后读取 info.plist 文件并提示用户提交崩溃日志。

{
    MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
    mailComposer.mailComposeDelegate = self;[mailComposer setSubject:@"Crash Log"];
    // Set up recipients
    NSArray *toRecipients = [NSArray arrayWithObject:@"first@example.com"]; 
    [mailComposer setToRecipients:toRecipients];
    // Attach the Crash Log..
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.log"];
    NSData *myData = [NSData dataWithContentsOfFile:logPath];
    [mailComposer addAttachmentData:myData mimeType:@"Text/XML" fileName:@"Console.log"];
    // Fill out the email body text
    NSString *emailBody = @"Crash Log";
    [mailComposer setMessageBody:emailBody isHTML:NO];
    [self presentModalViewController:mailComposer animated:YES];
}

NSString *localizedPath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"plist"]; - 实际上你不能将数据保存到mainBundle中的plist文件中。所以请使用文档文件夹。 - Dmitriy Kalachniuk
1
有趣的解决方案。为什么要尝试写入 plist,而不使用 NSUserDefaults 呢? - Houman
3
很遗憾,这种方法无法捕获所有崩溃。由于调用已释放对象而导致的崩溃将不会被捕获。我知道这是因为我自己曾经遇到过 :) - Duck
不要忘记 + canSendMail 方法。 - DanSkeel
如何在Swift中发送邮件?我遇到了“无法从本地函数捕获上下文形成C函数指针”的问题。http://stackoverflow.com/questions/32952611/how-to-use-nssetuncaughtexceptionhandler-to-show-exception-message-on-uiview-in同样的错误...请指导我。 - McDonal_11
显示剩余2条评论

51
  1. 如果要记录自己的数据,请使用Cocoalumberjack。它比NSLog快得多,并且可以动态地打开/关闭。它还提供将数据保存到文件的选项。NSLog会减慢您的应用程序并填充控制台日志。另外,一般情况下不要记录太多日志。当崩溃发生时,您无法安全地进行日志记录。因此,一旦确定了问题区域,请在那里添加更多日志,并尝试通过使用自动化测试框架(如KIF)来重现它。

  2. 要捕获崩溃报告,您应该使用基于开源框架PLCrashReporter的解决方案,它可以安全地捕获崩溃,即使您的应用程序已经在应用商店中!不建议像其他人建议的异常捕获方法,请查看这篇文章以了解原因!

    iTunes Connect也提供了查看一些崩溃报告的功能,但需要等待长达2周才能看到一些报告,而且远远不能看到所有报告,例如Camera+ developers指出的那样。所以最好使用自己的解决方案。

    PLCrashReporter将向您发送标准的苹果格式的崩溃报告,可供符号化使用,因此您知道在代码中发生了崩溃,包括行号。

    基于PLCrashReporter的一些解决方案包括:

    • QuincyKit:开源客户端+php服务器,基本崩溃分组,可以从您的Mac自动完成符号化(我是开发人员)
    • HockeyApp:付费服务,使用QuincyKit客户端,高级崩溃分组,完全在服务器上完成符号化(我是其中之一的开发人员)
    • Bugsense:免费服务,无符号化
    • AppBlade:如果使用25个设备或更少,则为免费服务,无符号化
    • Crashlytics:私有测试版,未知功能,他们的解决方案似乎基于PLCrashReporter
  3. 所提出的解决方案都可以自动在下次启动时发送数据,或者通过询问用户是否同意发送来进行。


BugSense目前在设备上提供符号化。 - Nick Toumpelis
@Kerni,你如何在你的机器上使用QuincyKit进行自动符号化(必须是Mac吗)? - Roberto
@Roberto 是的,必须使用Mac电脑。QuincyKit有一个修改过的符号化脚本、启动代理等捆绑在一起。请查看read me文件以了解如何设置:https://github.com/TheRealKerni/QuincyKit/blob/develop/README.markdown - Kerni

5

如果你需要在 Swift 下进行日志记录和分析,可以使用 SwiftyBeaver。它是一个功能齐全的日志平台,包括开源的 Swift 2 和 Objective-C 框架、加密云存储和 Mac 应用程序。

网站:https://swiftybeaver.com

框架(支持):https://github.com/SwiftyBeaver/SwiftyBeaver

免责声明:我是创始人。


这个能用来将日志推送到服务器吗?或者集成到Google文档中? - Ccr
是的,它可以用于将日志推送到服务器。它甚至拥有自己的端到端加密日志平台。 - Sebastian
@sebastian,这是一个非常酷的框架。您能否确认一下它可以记录多少用户(设备)的日志并同步到云端?如果任何商店应用程序使用此框架,则必须在收集日志数据之前获得用户同意,对吗?最后,我们如何确保将日志同步到云端不会减慢应用程序的速度? - Anand

5
这是一个可以及时捕获崩溃的解决方案,与崩溃日志相比,它提供更加人性化易读的代码信息。虽然它缺少一些崩溃日志的内容,但正如Till所说,你应该能够访问那些内容。
从另一个关于Xcode 4.2总是在崩溃后返回主页面的SO问题中,答案使用了这种方法,你可以扩展它来跟踪崩溃。
在AppDelegate中实现自己的异常处理程序。
// on load
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

void uncaughtExceptionHandler(NSException *exception) {
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    // Internal error reporting
}

更新 我进行了一些回溯,Zane Claes在问题Xcode 4.2 debug doesn't symbolicate stack call中提供了这个解决方案。

他在第二条评论中提供了一个通用的解决方案。"我发现将崩溃日志写入文件并提示用户在下一次启动时提交(仅在发布模式下,以不妨碍调试)非常有用。这让我得到了很好的错误报告......而且用户知道他们的问题正在得到解决"。我明白并不是每个人都愿意向用户提出这个要求,但有些超级用户会乐意提供帮助。

当然,您可以包括一个“永远不再显示此提示”按钮,以便人们不会因为报告机制而感到沮丧。

或者, 您可以使用信息联系服务器(不确定它是否会工作,因为它正在崩溃,但保存它并偶尔尝试使用详细信息POST到服务器)


+1 很好知道 :-) 但在崩溃之前,Jacob 将无法获取日志文件内容。 - Kay

3

我一直在使用Crittercism来自动化这个过程。它适用于测试和生产环境。


1
BugSense为iOS提供崩溃报告服务。除了提供完全符号化的堆栈跟踪外,BugSense还为您的崩溃提供分析,涵盖您所有的应用程序。
我认为它比电子邮件更好,因为当您的应用程序变得流行时,您需要手动管理所有这些电子邮件,而BugSense会自动处理。但是,BugSense也是开源的,因此您可以按照自己的方式修改其内部并添加任何其他功能。
除此之外,您可以免费让我们为您工作:如果您有一个关于一个很酷的新功能的想法,您希望我们拥有,我们将会实现它——前提是我们也认为它很酷。
免责声明:我编写了BugSense-iOS.framework的代码。

0

请参考如何查看iPhone .app文件中的NSLog语句中Ryan的答案,使用苹果提供的免费工具。

但这仍然不是一个方便的解决方案。如果你能负担得起新的构建,你应该在应用程序内部更改日志记录。Jano在如何将NSLog记录到文件中中提出了一些非常好的想法。特别是选项2应该不需要太多的努力。

总的来说,我建议在项目开始时无论使用什么编程语言,都将本地日志记录设施隐藏在门面或类似设计后面。


-1
如果您使用TestFlight和他们的SDK,这是自动化的。这是一个非常好的系统。但仅适用于测试构建。

https://testflightapp.com/sdk/


1
这已不再可用,因为苹果在2015年收购了Testflight。 - pedrouan

-1

我已经使用以下代码来捕获调试日志 - Swift 4.1

var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let fileName = "Logfile.txt"
let logFilePath = (documentsDirectory as NSString).appendingPathComponent(fileName)
freopen(logFilePath.cString(using: String.Encoding.ascii)!, "a+", stderr)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接