深入浅出iOS深海NSLog

iOS实录9:iOS开发中的日志工具迭代

2017-05-03  本文已影响761人  南华coder

[这是第9篇]

导语: 日志输出不仅仅是NSLog的简单使用,它对定位开发中的问题,收集用户的使用习惯有着很重要的作用。下面根据在项目中的实践,介绍我在iOS开发中对日志工具的迭代过程。

一、日志工具V1.0####

1、日志工具V1.0的主要目标#####
2、日志工具V1.0的实现#####

在项目的pch文件中,定义如下:
#if DEBUG
#define QSLOG(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, ##VA_ARGS);
#else
#define QSLOG(fmt,...) {}
#endif

3、日志工具V1.0的使用#####
QSLOG(@"嘻嘻嘻");
QSLOG(@"嘻嘻嘻%@嘻嘻嘻", @"哈哈哈");   //含参数

对应的输出是:

2017-05-02 16:06:50.098872 QSUseLogUtilDemo[276:28201] -[ViewController viewDidLoad] [Line 57] 嘻嘻嘻
2017-05-02 16:06:50.098932 QSUseLogUtilDemo[276:28201] -[ViewController viewDidLoad] [Line 58] 嘻嘻嘻哈哈哈嘻嘻嘻
4、日志工具V1.0的不足#####

二、日志工具V2.0####

1、日志工具V2.0的主要目标#####
2、日志工具V2.0的实现#####

1)在项目的pch文件中,定义如下(和日志工具V1.0一样)
#if DEBUG
#define QSLOG(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, ##VA_ARGS);
#else
#define QSLOG(fmt,...) {}
#endif

2)定义QSOldLogUtil类,日志工具类

//QSOldLogUtil.h
@interface QSOldLogUtil : NSObject

+ (void)openRedirectLogToDoc;

+ (NSString *)logContent;

@end

//QSOldLogUtil.m
@implementation QSOldLogUtil
+ (void)openRedirectLogToDoc{

    NSString *logFilePath = [self logFilePath];
    NSData *data = [NSData dataWithContentsOfFile:logFilePath];
    if ([data length] > 1000 * 1000) {
        //文件大小超过1MB,删除文件(iphone中文件大小进制1000,不是1024)
        [[NSFileManager defaultManager] removeItemAtPath:logFilePath error:nil];
    }

    //标准输出文件stdout,标准错误输出文件stderr
    freopen([logFilePath cStringUsingEncoding:NSUTF8StringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSUTF8StringEncoding], "a+", stderr);
    QSLOG(@"\n\n********************************\n\n");
}

+ (NSString *)logContent{

    NSError *error;
    NSString *content = [NSString stringWithContentsOfFile:[self logFilePath] encoding:NSUTF8StringEncoding error:&error];
    if (!error) {
        return content;
    }
    return error.description;
}

#pragma mark - private methods
+ (NSString *)logFilePath{

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [paths objectAtIndex:0];
    NSString *fileName = [NSString stringWithFormat:@"appName-log.log"];
    return [docDir stringByAppendingPathComponent:fileName];
}
@end

** 3)在AppDelegate中添加如下代码**

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    BOOL isConnectToXcode = isatty(STDOUT_FILENO);
    if(!isConnectToXcode) {
        //isConnectToXcode为NO,认为设备没有连接Xcode,调试日志重定向输出到文件
        [QSOldLogUtil openRedirectLogToDoc];
   }

   // ...
   return YES;
}

:我们在真机没有连接Xcode的情形下,将日志保存在文件中,日志文件大小限制是1MB,(iphone中文件大小进制1000,不是1024),超过1MB就删除后,从头开始写,否则接着上次的地方写。

3、日志工具V2.0的使用#####
QSLOG(@"嘻嘻嘻");
QSLOG(@"嘻嘻嘻%@嘻嘻嘻", @"哈哈哈");   //含参数

非release版本下,真机连接Xcode情况下,日志输出到Xcode控制台;不连接Xcode情况下,日志输出到文件。输出格式和日志工具V1.0的输出格式一样(QSLog的宏没有改变)

3、日志工具V2.0的不足#####

日志工具V2.0存在时间很短,是一个极其短暂的过度版本。主要原因有两个:

三、日志工具V3.0####

1、日志工具V3.0的主要目标#####
2、日志工具V3.0的实现#####

日志工具V3.0是基于CocoaLumberjack库实现的,主要实现的功能有:

1) 通过cocoapods导入CocoaLumberjack,在Podfile中添加

target "QSUseLogUtilDemo" do
  # 其他第三方库....
  pod 'CocoaLumberjack'
end

2) 在pch文件中设置全局ddLogLevel,修改QSLog的宏
#ifdef DEBUG
static const long ddLogLevel = DDLogLevelDebug;
#else
static const long ddLogLevel = DDLogLevelOff; //无日志
#endif

#define QSLOG(fmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug, 0, nil, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__);

说明1:DDLogFlag是一次log的等级, 而DDLogLevel是log输出等级, 如果一次log的等级,低于这个Logger的level,就不会打log.

typedef NS_OPTIONS(NSUInteger, DDLogFlag){
    DDLogFlagError      = (1 << 0),
    DDLogFlagWarning    = (1 << 1),
    DDLogFlagInfo       = (1 << 2),
    DDLogFlagDebug      = (1 << 3),
    DDLogFlagVerbose    = (1 << 4)
};

typedef NS_ENUM(NSUInteger, DDLogLevel){
    DDLogLevelOff       = 0,
    DDLogLevelError     = (DDLogFlagError),
    DDLogLevelWarning   = (DDLogLevelError   | DDLogFlagWarning),
    DDLogLevelInfo      = (DDLogLevelWarning | DDLogFlagInfo),
    DDLogLevelDebug     = (DDLogLevelInfo    | DDLogFlagDebug),
    DDLogLevelVerbose   = (DDLogLevelDebug   | DDLogFlagVerbose),
    DDLogLevelAll       = NSUIntegerMax
};

说明2: ddLogLevel很重要,指定日志的显示类型(LogLevel 用来过滤每条Log,低于LogLevel等级,不会被输出),在release情况下,ddLogLevel 为DDLogLevelOff,不会有日志输出;在非release情况下,QSLOG使用的日志级别是DDLogFlagDebug,达到ddLogLevel 指定的DDLogLevelDebug输出等级,可以被输出。因此,在非release情况下NSLOG是真正输出的,在release情况下NSLOG是没有实际输出的。

3)输入日志格式类QSLogFormatter (只展示重要代码)
实现DDLogFormatter接口协议,指定日志输出格式

  //指定日志输出格式
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage{

    NSString *timeStr = [self.dateFormatter stringFromDate:logMessage.timestamp];
    NSString *flagStr = QSLogFlagToString(logMessage.flag);
    NSString *formatStr = [NSString stringWithFormat:@"%@ %@ %@ %@ line:%ld %@ %@",timeStr,flagStr,logMessage.queueLabel,logMessage.fileName,(long)logMessage.line,logMessage.function,logMessage.message];
    return formatStr;
}
@end

4)日志工具类QSLogUtil类(只展示重要代码)

+ (void)setupConfig{

    //添加控制台输出Logger
    [[DDTTYLogger sharedInstance] setLogFormatter:[[QSLogFormatter alloc] init]];
    [DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:ddLogLevel]; // TTY = Xcode console
//  [DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs

    #if DEBUG
    fileLogger = [[DDFileLogger alloc] init];
    fileLogger.logFormatter = [[QSLogFormatter alloc] init];
    fileLogger.rollingFrequency = 0;
    fileLogger.maximumFileSize = 1000 * 1000;  //限制1MB
    [DDLog addLogger:fileLogger withLevel:ddLogLevel];   //日志文件
    QSLOG(@"********************************\n");
#endif
}

+ (NSString *)logContent{
    return [NSString stringWithContentsOfFile:[self logFilePath] encoding:NSUTF8StringEncoding error:nil];
}

+ (NSString *)logFilePath {
    return [[fileLogger currentLogFileInfo] filePath];
}

4)日志视图显示类QSLogView类(只展示重要代码)

+ (void)show{

    QSLogView *logView = [[QSLogView alloc]init];
    NSArray *rootVCViewSubViews = [[UIApplication sharedApplication].delegate window].rootViewController.view.subviews;
    for (UIView *logView in rootVCViewSubViews) {
        if ([logView isKindOfClass:[QSLogView class]]) {
            return;
        }
    }
    [[((NSObject <UIApplicationDelegate> *)([UIApplication sharedApplication].delegate)) window].rootViewController.view addSubview:logView];
}

+ (void)close {

    NSArray *rootVCViewSubViews=[[UIApplication sharedApplication].delegate window].rootViewController.view.subviews;
    for (QSLogView *view in rootVCViewSubViews) {
        if ([view isKindOfClass:[QSLogView class]]) {
            QSLogView *logView = (QSLogView *)view;
            [logView removeFromSuperview];
        }
    }
}

//更新屏幕帧数
- (void)timerFire:(CADisplayLink *)link{

    if (_lastTime == 0) {
        _lastTime = link.timestamp;
        return;
    }

    _count++;
    NSTimeInterval delta = link.timestamp - _lastTime;
    if (delta < 1) return;
    _lastTime = link.timestamp;
    float fps = _count / delta;
    _count = 0;

    NSString *fpsString = [NSString stringWithFormat:@"[%d FPS]",(int)round(fps)];
    if (!self.showLogBtn.hidden) {
        [self.showLogBtn setTitle:[NSString stringWithFormat:@"[显示]%@",fpsString] forState:UIControlStateNormal];
    }

    if (!self.closeLogBtn.hidden) {
        [self.closeLogBtn setTitle:[NSString stringWithFormat:@"[关闭]%@",fpsString] forState:UIControlStateNormal];
    }
}
3、日志工具V3.0的使用#####

1)为了在DEBUG环境下开启日志记录和日志显示View,需要在appDelegate中添加如下代码

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    #if DEBUG
        [QSLogUtil openLog];  //开启日志记录
    #endif

    //self.window的初始化和指定rootViewController

    [self.window makeKeyWindow];

#if DEBUG
    [QSLogView show];   //显示日志记录视图
#endif

    return YES;
}

2)平时的输出日志还是使用QSLOG,和以前没有任何区别

QSLOG(@"嘻嘻嘻");
QSLOG(@"嘻嘻嘻%@嘻嘻嘻", @"哈哈哈");   //含参数
4、日志工具V3.0使用的效果图 (DEBUG模式下,Release不会有日志输出)#####

1)日志记录视图入口开启

日志记录视图入口开启.png 此时的控制台输出内容是: 控制台输出日志内容.png

2)QSLogView显示日志

源码直通车QSUseLogUtilDemo
可以的话,欢迎star

上一篇 下一篇

猜你喜欢

热点阅读