zoukankan      html  css  js  c++  java
  • iOS异常日志记录与展现功能

     

    iOS异常日志记录与展现功能 

    在平常的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:@"========异常错误报告========
    name:%@
    reason:
    %@
    callStackSymbols:
    %@",name,reason,[arr componentsJoinedByString:@"
    "]];
        DDLogError(@"%@
    
    ",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:@"========异常错误报告========
    name:%@
    reason:
    %@
    callStackSymbols:
    %@",name,reason,[arr componentsJoinedByString:@"
    "]];
        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 AppDelegate
    
    static 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 *test=@[@"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)];
            myLabel.text=@"日记列表";
            [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
    复制代码

    7:如果有第三方其它记录异常的使用,最好用DEBUG模式本地记录异常日志,不然日志只会被记录在一个地方

    复制代码
        //友盟统计加载
        [HYBUMAnalyticsHelper UMAnalyticStart];
        
        #ifdef DEBUG
        //日志框架(放在其它SDK下面 MyExceptionHandler)
        [MyExceptionHandler setDefaultHandler];
        #else
        
        #endif
    
        [MyFileLogger sharedManager];
    复制代码

    这样便可以实现不管在哪个页面出出异常都可以进行记录,因为实例比较小,如果要源代码可以留下邮箱统一进行发送;

  • 相关阅读:
    14_java之变量|参数|返回值|修饰符
    NYOJ 202 红黑树 (二叉树)
    NYOJ 138 找球号(二) (哈希)
    NYOJ 136 等式 (哈希)
    NYOJ 133 子序列 (离散化)
    NYOJ 129 树的判定 (并查集)
    NYOJ 117 求逆序数 (树状数组)
    NYOJ 93 汉诺塔 (数学)
    HDU 2050 折线分割平面 (数学)
    天梯赛L2-008 最长对称子串 (字符串处理)
  • 原文地址:https://www.cnblogs.com/LiLihongqiang/p/5796839.html
Copyright © 2011-2022 走看看