標籤:
在平常的APP開發過程中經常碰到程式遇到異常閃退的問題,通過日誌可以把相關的詳細錯誤資訊進行記錄,本執行個體要記錄不管在哪個頁面出錯都要進行記錄,這邊使用到的日誌記錄外掛程式CocoaLumberjack,以文本的形式記錄錯誤資訊,然後再去讀取各個文本的內容進行展示;當然現在有很多第三方的外掛程式比如友盟也已經整合錯誤記錄的功能;
如下:
1:封裝DDLogger的類
MyFileLogger.h檔案#import <Foundation/Foundation.h>#import <CocoaLumberjack.h>@interface MyFileLogger : NSObject@property (nonatomic, strong, readwrite) DDFileLogger *fileLogger;+(MyFileLogger *)sharedManager;@end
MyFileLogger.m檔案#import "MyFileLogger.h"@implementation MyFileLogger#pragma mark - Inititlization- (instancetype)init{ self = [super init]; if (self) { [self configureLogging]; } return self;}#pragma mark 單例模式static MyFileLogger *sharedManager=nil;+(MyFileLogger *)sharedManager{ static dispatch_once_t once; dispatch_once(&once, ^{ sharedManager=[[self alloc]init]; }); return sharedManager;}#pragma mark - 配記日誌類型- (void)configureLogging{#ifdef DEBUG [DDLog addLogger:[DDASLLogger sharedInstance]]; [DDLog addLogger:[DDTTYLogger sharedInstance]];#endif [DDLog addLogger:self.fileLogger];}#pragma mark - 初始設定檔案記錄類型- (DDFileLogger *)fileLogger{ if (!_fileLogger) { DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling fileLogger.logFileManager.maximumNumberOfLogFiles = 7; _fileLogger = fileLogger; } return _fileLogger;}@end
這邊是設定24小時進行記錄一個檔案
2:出進異常進行記錄
MyExceptionHandler.h檔案#import <Foundation/Foundation.h>#import <CocoaLumberjack.h>@interface MyExceptionHandler : NSObject+ (void)setDefaultHandler;+ (NSUncaughtExceptionHandler *)getHandler;+ (void)TakeException:(NSException *) exception;@end
#import "MyExceptionHandler.h"void UncaughtExceptionHandler(NSException * exception){ NSArray * arr = [exception callStackSymbols]; NSString * reason = [exception reason]; NSString * name = [exception name]; NSString * url = [NSString stringWithFormat:@"========異常錯誤報表========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[arr componentsJoinedByString:@"\n"]]; DDLogError(@"%@\n\n",url);}@implementation MyExceptionHandler+ (void)setDefaultHandler{ NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);}+ (NSUncaughtExceptionHandler *)getHandler{ return NSGetUncaughtExceptionHandler();}+ (void)TakeException:(NSException *)exception{ NSArray * arr = [exception callStackSymbols]; NSString * reason = [exception reason]; NSString * name = [exception name]; NSString * url = [NSString stringWithFormat:@"========異常錯誤報表========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[arr componentsJoinedByString:@"\n"]]; DDLogError(@"%@",url);}@end
這個檔案也是當出現異常會執行
3:AppDelegate配置的內容
AppDelegate.h檔案內容#import <UIKit/UIKit.h>#import <DDLog.h>#import <CocoaLumberjack.h>#import "MyExceptionHandler.h"#import "MyFileLogger.h"@interface AppDelegate : UIResponder <UIApplicationDelegate>@property (strong, nonatomic) UIWindow *window;@property (nonatomic, strong) MyFileLogger *logger;@end
AppDelegate.m檔案:#import "AppDelegate.h"@interface AppDelegate ()@end@implementation AppDelegatestatic const int ddLogLevel = LOG_LEVEL_VERBOSE;- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //初始化 [MyExceptionHandler setDefaultHandler]; self.logger=[[MyFileLogger alloc]init]; NSString *path = NSHomeDirectory();//主目錄 NSLog(@"當前項目的路徑:%@",path); return YES;}- (void)applicationWillResignActive:(UIApplication *)application {}- (void)applicationDidEnterBackground:(UIApplication *)application {}- (void)applicationWillEnterForeground:(UIApplication *)application {}- (void)applicationDidBecomeActive:(UIApplication *)application {}- (void)applicationWillTerminate:(UIApplication *)application {}@end
這邊重點是設定DDLOG的記錄等級ddLogLevel,以及上面兩個檔案的初始化[MyExceptionHandler setDefaultHandler];self.logger=[[MyFileLogger alloc]init];
實現上面的代碼已經能夠記錄異常的內容;
4:建立一個錯誤的異常代碼
- (void)viewDidLoad { [super viewDidLoad]; NSArray *[email protected][@"123",@"444"]; id testID=test[5];}
執行到這段代碼便記錄異常的內容,接著進行內容的展示,是以一個列表展示每個記錄檔,然後一個詳細頁面進行展示;
5:日誌列表
loggerTableViewController.h檔案#import <UIKit/UIKit.h>#import <CocoaLumberjack.h>#import "AppDelegate.h"#import "LoggerDetailViewController.h"@interface loggerTableViewController : UIViewController@end
#import "loggerTableViewController.h"#define BLSRecyclingRecordViewController_CellIdentifier @"MyTablecell"@interface loggerTableViewController ()<UITableViewDataSource, UITableViewDelegate>@property (strong, nonatomic) UITableView *myTableView;@property (nonatomic, strong) NSDateFormatter *dateFormatter;@property (nonatomic, weak) DDFileLogger *fileLogger;@property (nonatomic, strong) NSArray *logFiles;@end@implementation loggerTableViewController- (void)viewDidLoad { [super viewDidLoad]; //載入記錄檔 AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; _fileLogger = delegate.logger.fileLogger; [self loadLogFiles]; if (!_myTableView) { _myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height) style:UITableViewStyleGrouped]; _myTableView.showsVerticalScrollIndicator = NO; _myTableView.showsHorizontalScrollIndicator=NO; _myTableView.dataSource = self; _myTableView.delegate = self; [_myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:BLSRecyclingRecordViewController_CellIdentifier]; [self.view addSubview:_myTableView]; }}//讀取日誌的檔案個數- (void)loadLogFiles{ self.logFiles = self.fileLogger.logFileManager.sortedLogFileInfos;}//時間格式- (NSDateFormatter *)dateFormatter{ if (_dateFormatter) { return _dateFormatter; } _dateFormatter = [[NSDateFormatter alloc] init]; [_dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; return _dateFormatter;}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning];}#pragma mark - Table view data source- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ if (section==0) { return 40; } return 10;}- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ return 1;}- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 2;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ if (section == 0) { return self.logFiles.count; } return 1;}- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ UIView *headView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, 30)]; if (section==0) { UILabel *myLabel=[[UILabel alloc]initWithFrame:CGRectMake(10, 10, [[UIScreen mainScreen] bounds].size.width, 30)]; [email protected]"日記列表"; [headView addSubview:myLabel]; } return headView;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:BLSRecyclingRecordViewController_CellIdentifier]; if (indexPath.section == 0) { DDLogFileInfo *logFileInfo = (DDLogFileInfo *)self.logFiles[indexPath.row]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = indexPath.row == 0 ? NSLocalizedString(@"當前", @"") : [self.dateFormatter stringFromDate:logFileInfo.creationDate]; cell.textLabel.textAlignment = NSTextAlignmentLeft; } else { cell.accessoryType = UITableViewCellAccessoryNone; cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.text = NSLocalizedString(@"清理舊的記錄", @""); } return cell;}#pragma mark - Table view delegate- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (indexPath.section == 0) { DDLogFileInfo *logFileInfo = (DDLogFileInfo *)self.logFiles[indexPath.row]; NSData *logData = [NSData dataWithContentsOfFile:logFileInfo.filePath]; NSString *logText = [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]; LoggerDetailViewController *detailViewController = [[LoggerDetailViewController alloc] initWithLog:logText forDateString:[self.dateFormatter stringFromDate:logFileInfo.creationDate]]; [self.navigationController pushViewController:detailViewController animated:YES]; } else { for (DDLogFileInfo *logFileInfo in self.logFiles) { //除了當前 其它進行清除 if (logFileInfo.isArchived) { [[NSFileManager defaultManager] removeItemAtPath:logFileInfo.filePath error:nil]; } } [self loadLogFiles]; [self.myTableView reloadData]; }}@end
這邊把表格分成兩部分,一部分是記錄檔的列表,以及一個清除功能,清除功能主要是對先前的檔案進行刪除的操作,讀取日誌的個數及日誌時間,日誌詳細內容
DDLogFileInfo
6:異常的詳細資料頁面
LoggerDetailViewController.h代碼#import <UIKit/UIKit.h>@interface LoggerDetailViewController : UIViewController- (id)initWithLog:(NSString *)logText forDateString:(NSString *)logDate;@end
LoggerDetailViewController.m檔案#import "LoggerDetailViewController.h"@interface LoggerDetailViewController ()@property (nonatomic, strong) NSString *logText;@property (nonatomic, strong) NSString *logDate;@property (nonatomic, strong) UITextView *textView;@end@implementation LoggerDetailViewController- (void)viewDidLoad { [super viewDidLoad]; self.textView = [[UITextView alloc] initWithFrame:self.view.bounds]; self.textView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; self.textView.editable = NO; self.textView.text = self.logText; [self.view addSubview:self.textView];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}- (id)initWithLog:(NSString *)logText forDateString:(NSString *)logDate{ self = [super initWithNibName:nil bundle:nil]; if (self) { _logText = logText; _logDate = logDate; self.title = logDate; } return self;}@end
這樣便可以實現不管在哪個頁面出出異常都可以進行記錄,因為執行個體比較小,如果要原始碼可以留下郵箱統一進行發送;
IOS異常日誌記錄與展現功能