可以将要在Xcode 控制台打印的日志写在沙盒,最后导出分享,进行问题分析。
正式版本不建议使用,避免增加用户内存。配合解决顽固 Bug 可以通过该方法收集打印日志
.h头文件
@interface LogManager : NSObject
+ (FSLogManager *)shareInstance;
- (void)redirectNSlogToDocumentFolder;
- (NSString *)logDirPath;
- (void)clearAllLog;
- (float)sizeOfLogs;
@end
.m实现文件
#import "LogManager.h"
#import <UIKit/UIKit.h>
#define LOG_TIME_FORMAT @"yyyy-MM-dd HH:mm:ss.SSS"
#define LOG_QUEUE_ID "log_queue"
static LogManager *manager = nil;
/// 是否运行收集日志,如果为 YES , 将不会在日志控制台打印。
static BOOL const kAllowSaveLog = YES;
@implementation LogManager
+ (LogManager *)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[LogManager alloc] init];
if (kAllowSaveLog) {
[self setDefaultUncaughtExceptionHandler];
}
});
return manager;
}
- (void)clearAllLog {
NSArray<NSString *> *allPath = [self allLogPath];
if (allPath.count == 0) {
return;
}
[allPath enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:obj error:&error];
}];
}
- (void)redirectNSlogToDocumentFolder{
if (kAllowSaveLog == NO) {
return;
}
UIDevice *device = [UIDevice currentDevice];
if ([[device model] isEqualToString:@"Simulator"]) {
return;
}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *dateString = [formatter stringFromDate:[NSDate date]];
NSString *documentDirectory = [self logDirPath];
NSString *fileName = [NSString stringWithFormat:@"%@-log.txt",dateString];
NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
// Delete existing files
[[NSFileManager defaultManager] removeItemAtPath:logFilePath error:nil];
//Enter the log into the file (所有的打印都会存在该文件)
freopen([logFilePath cStringUsingEncoding:NSUTF8StringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}
- (float)sizeOfLogs{
NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:[self logDirPath]];
NSString *pname;
int64_t s=0;
while (pname = [direnum nextObject]){
NSDictionary *currentdict=[direnum fileAttributes];
NSString *filesize=[NSString stringWithFormat:@"%@",[currentdict objectForKey:NSFileSize]];
NSString *filetype=[currentdict objectForKey:NSFileType];
if([filetype isEqualToString:NSFileTypeDirectory]) continue;
s=s+[filesize longLongValue];
}
return s*1.0;
}
- (NSString *)logDirPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *logPath = [documentDirectory stringByAppendingPathComponent:@"Logs"];
BOOL isDir = NO;
BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:logPath isDirectory:&isDir];
if (isExist && isDir) {
} else {
NSError * createDirError;
[[NSFileManager defaultManager] createDirectoryAtPath:logPath withIntermediateDirectories:YES attributes:nil error:&createDirError];
}
return logPath;
}
- (NSArray<NSString *> *)allLogPath {
NSArray<NSString *> *items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self logDirPath] error:nil];
NSMutableArray<NSString *> *paths = [NSMutableArray array];
NSString *dirPath = [self logDirPath];
[items enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[paths addObject:[dirPath stringByAppendingPathComponent:obj]];
}];
return paths;
}
#pragma mark -设置crash
+ (void)setDefaultUncaughtExceptionHandler
{
NSSetUncaughtExceptionHandler (&chUncaughtExceptionHandler);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
}
#pragma mark -获取崩溃日志
void chUncaughtExceptionHandler(NSException *exception)
{
NSLog(@"Exception info --> %@",exception);
}
void SignalHandler(int signal)
{
//拦截signal
}
@end
分享
收集到的日志可以通过云端上传,也可以直接通过系统原生分享。
/// items 可以传文件路径path。
+ (void)shareItems:(NSArray *)items fromController:(UIViewController *)controller cancel:(void(^)(void))cancel completion:(void(^)(NSError * _Nullable error))completion {
if (items.count == 0) {
return;
}
//初始化:
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
activityVC.completionWithItemsHandler = ^(UIActivityType _Nullable activityType, BOOL completed, NSArray * _Nullable returnedItems, NSError * _Nullable activityError) {
// 如果取消, completed返回 NO
if (completed) {
if (completion) {
completion(activityError);
}
} else {
if (cancel) {
cancel();
}
}
};
//禁掉不用的服务
activityVC.excludedActivityTypes = @[UIActivityTypePrint,UIActivityTypeAssignToContact];
[controller presentViewController:activityVC animated:YES completion:nil];
}