iOS进阶之基础iOS调试架构

CocoaLumberjack——简单好用的日志框架

2017-06-21  本文已影响819人  独木舟的木

CocoaLumberjack——简单好用的日志框架

README.md

Lumberjack logo

CocoaLumberjack 是一个适用于Mac和iOS的快速、简单,而且功能强大且灵活的日志框架

如何开始

通过 CocoaPods 安装 Swift 版本
platform :ios, '8.0'

# You need to set target when you use CocoaPods 1.0.0 or later.
target 'SampleTarget' do 
  use_frameworks!
  pod 'CocoaLumberjack/Swift'
end

注意: Swift 子空间中包含了所有的 Objectice-C 代码和 Swift 代码,所以这是足够的。
For more details about how to use Swift with Lumberjack, see this conversation.

Swift 用法

If you installed using CocoaPods or manually:

import CocoaLumberjack
DDLog.add(DDTTYLogger.sharedInstance) // TTY = Xcode console
DDLog.add(DDASLLogger.sharedInstance) // ASL = Apple System Logs

let fileLogger: DDFileLogger = DDFileLogger() // File Logger
fileLogger.rollingFrequency = TimeInterval(60*60*24)  // 24 hours
fileLogger.logFileManager.maximumNumberOfLogFiles = 7
DDLog.add(fileLogger)

...

DDLogVerbose("Verbose");
DDLogDebug("Debug");
DDLogInfo("Info");
DDLogWarn("⚠️");
DDLogError("Error");
通过 CocoaPods 安装 Objectice-C 版本
platform :ios, '7.0'
pod 'CocoaLumberjack'
Obj-C 用法

如果你把 Lumberjack 作为框架导入,你可以 @import CocoaLumberjack

否则, #import <CocoaLumberjack/CocoaLumberjack.h>

[DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode 控制台
[DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs 苹果系统日志

DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // 本地文件日志
fileLogger.rollingFrequency = 60 * 60 * 24; // 每24小时创建一个新文件
fileLogger.logFileManager.maximumNumberOfLogFiles = 7; // 最多允许创建7个文件
[DDLog addLogger:fileLogger];

...

DDLogVerbose(@"Verbose");
DDLogDebug(@"Debug");
DDLogInfo(@"Info");
DDLogWarn(@"⚠️");
DDLogError(@"Error");
使用 Carthage (iOS 8+) 安装

Carthage is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods.

To install with Carthage, follow the instruction on Carthage

Cartfile

github "CocoaLumberjack/CocoaLumberjack"

CocoaLumberjack 3

迁移到 3.x

特性

Lumberjack 是 快速 & 简单, 而且强大 & 灵活的.

它在概念上与其他流行的日志记录框架类似,如log4j,它是专为 Objective-C 设计的,并且利用了诸如多线程,GCD(如果可用),无锁原子操作(lockless atomic operations)和动态性质的 Objective-C runtime。

Lumberjack 更快速

在大多数情况下,它比 NSLog 快一个数量级。

Lumberjack 是简单的

当应用程序启动时,它几乎只需要一行代码来配置 lumberjack。 然后只需用 DDLog 语句替换您的 NSLog 语句即可。 (并且 DDLog 宏与 NSLog 具有完全相同的格式和语法,因此它非常简单。)

Lumberjack 是强大的

一条日志语句可以被发送到多个记录器上,这意味着你可以同时登录到文件和控制台。 想要更多? 创建自己的记录器(很简单),并通过网络发送您的日志。 或者到数据库或分布式文件系统。 限制它的只有天空(The sky is the limit)。

Lumberjack 是灵活的:

配置你需要记录的日志。 更改每个文件的日志级别(便于完美调试)。更改每个记录器的日志级别(详细的控制台,但简明的日志文件)。 更改每个 Xcode配置的日志级别(详细调试,但简明扼要)。 将日志语句从发行版本中编译出来。 自定义应用程序的日志级别数量。 添加适合你自己的日志。 在运行时动态更改日志级别。 选择如何以及何时希望日志文件滚动。 将日志文件上传到中央服务器。 压缩归档日志文件以节省磁盘空间...

这个框架就是为你准备的如果:

文档

配置需求

当前版本的 Lumberjack 需要:

向后兼容性

沟通

作者

Collaborators

License

项目结构

⚠️ 光看 README.md 文档其实是完全不够的,我发现实际项目中使用时还是有很多注意点,最好详细看看 Documentation 里面的文章。

使用此框架前,打印日志是这么做的


记录日志一般做法是在 Release 模式下禁用 NSLog,比如在 .pch 文件中,通过对环境的判断,对 NSLog 做不同的处理。因此,在不使用 CocoaLumberjack 之前,我的.pch 文件中是这样写的:

/****************** 打印日志 ******************/
#ifdef DEBUG
#define HQString [NSString stringWithFormat:@"%s", __FILE__].lastPathComponent
#define HQLog(...) printf("%s: %s 第%d行:\n %s\n\n", [[NSString hql_stringDate] UTF8String], [HQString UTF8String] , __LINE__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String]);
#else
#define  HQLog(...);
#endif  /* DEBUG */

还需要为 NSString 类写一个范畴类方法输出时间:

+ (NSString *)hql_stringDate {
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"YYYY-MM-dd hh:mm:ss"];
    NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
    return dateString;
}

项目中使用CocoaLumberjack


使用 CocoaLumberjack 分为三步:

  1. 添加 CocoaLumberjack 文件到项目中;
  2. 链接配置 CocoaLumberjack
  3. 将你的 NSLog 语句转换为使用 CocoaLumberjack 宏;

添加 CocoaLumberjack 文件到项目中

Cocoapods

platform :ios, '8.0'
pod 'CocoaLumberjack'

链接配置 CocoaLumberjack

  1. 引入头文件
#define LOG_LEVEL_DEF ddLogLevel
#import <CocoaLumberjack/CocoaLumberjack.h>
  1. 设置全局状态 Log 级别
static const DDLogLevel ddLogLevel = DDLogLevelDebug;
  1. 以上两个步骤在 .pch 中设置如下:
/** 打印日志 */
#define LOG_LEVEL_DEF ddLogLevel
#import <CocoaLumberjack/CocoaLumberjack.h>

#ifdef DEBUG
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
#else
static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif  /* DEBUG */
  1. AppDelegate.m文件
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // DDTTYLogger,你的日志语句将被发送到Xcode控制台
    [DDLog addLogger:[DDTTYLogger sharedInstance]];

    // DDASLLogger,你的日志语句将被发送到苹果文件系统、你的日志状态会被发送到 Console.app
    [DDLog addLogger:[DDASLLogger sharedInstance]];

    // DDFileLogger,你的日志语句将写入到一个文件中,默认路径在沙盒的Library/Caches/Logs/目录下,文件名为bundleid+空格+日期.log。
    DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
    fileLogger.rollingFrequency = 60 * 60 * 24; // 刷新频率为24小时
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7; // 保存一周的日志,即7天
    [DDLog addLogger:fileLogger];

    // 产生Log
    DDLogVerbose(@"Verbose");   // 详细日志
    DDLogDebug(@"Debug");       // 调试日志
    DDLogInfo(@"Info");         // 信息日志
    DDLogWarn(@"Warn");         // 警告日志
    DDLogError(@"Error");       // 错误日志

    return YES;
}

运行程序,可以在Xocde控制台看到:

使用前后的变化:

// Convert from this:
NSLog(@"Broken sprocket detected!");
NSLog(@"User selected file:%@ withSize:%u", filePath, fileSize);

// To this:
DDLogError(@"Broken sprocket detected!");
DDLogVerbose(@"User selected file:%@ withSize:%u", filePath, fileSize);

正如你所看到的那样,DDLog 宏与 NSLog 具有完全相同的语法。

CocoaLumberjack 的 Log 样式

  1. DDLog(整个框架的基础)
  2. DDASLLogger(发送日志到苹果的日志系统,以便它们显示在Console.app上)
  3. DDTTYLoyger(发送日志到Xcode控制台)
  4. DDFIleLoger(把日志写入本地文件)

Log级别

你需要考虑用哪种日志级别,CocoaLumberjack 有5种 DDLogFlag

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

DDLogLevel 用来过滤每条Log

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

例如,如果你将日志级别设置为 DDLogLevelInfo,那么你会看到 DDLogLevelErrorDDLogLevelWarningDDLogLevelInfo语句。

你也可以自定义Log级别或者每个级别的名字或者在单纯的级别上增加一些高级用法


我们也可以为Debug和Release模式设置不同的Log级别:

Xcode 4+ :

#ifdef DEBUG 
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
#else 
static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif

我们还可以为每种 logger 记录器设置不同的日志级别:

如果你需要为每个 logger 记录器使用不同的日志级别(即,如果你有自定义记录器,如Crashlytics记录器,它不应该记录 Info 或 Debug),你就可以使用 [DDLog addLogger:withLevel:]方法轻松实现。

[DDLog addLogger:[DDASLLogger sharedInstance] withLevel:DDLogLevelInfo];
[DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelDebug];

你仍然可以使用旧的类方法+ addLogger:,该方法使用 DDLogLevelVerbose 作为默认值,它不会排除任何日志。

你可以通过 [DDLog allLoggersWithLevel]方法检索与 DDLog 关联的每个记录器和级别的列表。


自定义日志格式

// .h
#import <Foundation/Foundation.h>
#import <DDLog.h>

@interface HQLCustomFormatter : NSObject <DDLogFormatter> {
    int atomicLoggerCount;
    NSDateFormatter *threadUnsafeDateFormatter;
}

@end

// .m
#import "HQLCustomFormatter.h"
#import <libkern/OSAtomic.h>

static NSString *const KdateFormatString = @"yyyy/MM/dd HH:mm:ss";

@implementation HQLCustomFormatter

- (NSString *)stringFromDate:(NSDate *)date {
    int32_t loggerCount = OSAtomicAdd32(0, &atomicLoggerCount);
    
    if (loggerCount <= 1) {
        // Single-threaded mode.
        
        if (threadUnsafeDateFormatter == nil) {
            threadUnsafeDateFormatter = [[NSDateFormatter alloc] init];
            [threadUnsafeDateFormatter setDateFormat:KdateFormatString];
        }
        
        return [threadUnsafeDateFormatter stringFromDate:date];
    } else {
        // Multi-threaded mode.
        // NSDateFormatter is NOT thread-safe.
        
        NSString *key = @"MyCustomFormatter_NSDateFormatter";
        
        NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
        NSDateFormatter *dateFormatter = [threadDictionary objectForKey:key];
        
        if (dateFormatter == nil) {
            dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setDateFormat:KdateFormatString];
            
            [threadDictionary setObject:dateFormatter forKey:key];
        }
        
        return [dateFormatter stringFromDate:date];
    }
}

#pragma mark - DDLogFormatter

- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
    NSString *logLevel; // 日志等级
    switch (logMessage->_flag) {
        case DDLogFlagError    : logLevel = @"Error";   break;
        case DDLogFlagWarning  : logLevel = @"Warning"; break;
        case DDLogFlagInfo     : logLevel = @"Info";    break;
        case DDLogFlagDebug    : logLevel = @"Debug";   break;
        default                : logLevel = @"Verbose"; break;
    }
    
    NSString *dateAndTime = [self stringFromDate:(logMessage.timestamp)]; // 日期和时间
    NSString *logFileName = logMessage -> _fileName; // 文件名
    NSString *logFunction = logMessage -> _function; // 方法名
    NSUInteger logLine = logMessage -> _line;        // 行号
    NSString *logMsg = logMessage->_message;         // 日志消息
    
    // 日志格式:日期和时间 文件名 方法名 : 行数 <日志等级> 日志消息
    return [NSString stringWithFormat:@"%@ %@ %@ : %lu <%@> %@", dateAndTime, logFileName, logFunction, logLine, logLevel, logMsg];
}

- (void)didAddToLogger:(id <DDLogger>)logger {
    OSAtomicIncrement32(&atomicLoggerCount);
}

- (void)willRemoveFromLogger:(id <DDLogger>)logger {
    OSAtomicDecrement32(&atomicLoggerCount);
}

@end

最后,自定义好日志之后记得添加

    [DDTTYLogger sharedInstance].logFormatter = [[HQLCustomFormatter alloc] init]; // 自定义日志
    [DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode console
    [DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs
    
    DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // File Logger
    fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
    [DDLog addLogger:fileLogger];

更多详细文档:

遇到的问题

解决方法:

#define LOG_LEVEL_DEF ddLogLevel // 要先设置宏定义
#import <CocoaLumberjack/CocoaLumberjack.h>

#ifdef DEBUG
static const DDLogLevel ddLogLevel = DDLogLevelVerbose; // ddLogLevel 注意大小写要一致
#else
static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif  /* DEBUG */

参考

上一篇下一篇

猜你喜欢

热点阅读