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

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

如何开始

通过 CocoaPods 安装

通过 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 用法

如果你通过 CocoaPods 或者手动方式导入框架:

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 版本

对于 Objective-C,请使用以下命令:

platform :ios, '8.0'

target 'SampleTarget' do
    pod 'CocoaLumberjack'
end

Objectice-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(@"Warn");
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 配置的日志级别(详细调试,但简明扼要)。 将日志语句从发行版本中编译出来。 自定义应用程序的日志级别数量。 添加适合你自己的日志。 在运行时动态更改日志级别。 选择如何以及何时希望日志文件滚动。 将日志文件上传到中央服务器。 压缩归档日志文件以节省磁盘空间...

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

  • 你正在寻找一种方法来追踪不可再现的错误,这些错误在该领域不断涌现。
  • 你对 iPhone 上的超短控制台日志感到沮丧。
  • 你正在寻求在支持性和稳定性方面将应用提升到一个新的水平。
  • 你正在为你的应用程序(Mac或iPhone)寻找企业级日志记录解决方案。

文档

配置需求

当前版本的 Lumberjack 需要:

  • Xcode 11 or later
  • Swift 5.0 or later
  • iOS 8 or later
  • OS X 10.10 or later
  • WatchOS 3 or later
  • TVOS 9 or later

向后兼容性

  • for Xcode 7.3 and Swift 2.3, use the 2.4.0 version
  • for Xcode 7.3 and Swift 2.2, use the 2.3.0 version
  • for Xcode 7.2 and 7.1, use the 2.2.0 version
  • for Xcode 7.0 or earlier, use the 2.1.0 version
  • for Xcode 6 or earlier, use the 2.0.x version
  • for OS X < 10.7 support, use the 1.6.0 version

沟通

  • If you need help, use Stack Overflow. (Tag 'lumberjack')
  • If you'd like to ask a general question, use Stack Overflow.
  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

作者

Collaborators

License

CocoaLumberjack is available under the BSD license. See the LICENSE file.

项目结构

💡 光看以上官方的 README 文档其实是完全不够的,我发现实际项目中使用时还是有很多注意点,最好详细看看 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

Getting started with the CocoaLumberjack framework.

项目中集成并使用 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 {

    if (@available(iOS 10.0, *)) {
        // Uses os_log
        [DDLog addLogger:[DDOSLogger sharedInstance]];
    } else {
        // TTY = Xcode 控制台
        // 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];

    // 日常使用时,打印日志方法如下
    DDLogVerbose(@"Verbose");   // 详细日志
    DDLogDebug(@"Debug");       // 调试日志
    DDLogInfo(@"Info");         // 信息日志
    DDLogWarn(@"Warn");         // 警告日志
    DDLogError(@"Error");       // 错误日志

    return YES;
}

运行程序,查看 Xocde 控制台日志:

💡 你会发现控制台日志打印了两遍,那是因为在配置中我们同时添加了 Xcode 控制台日志和 DDASLLogger 日志,开发中注释掉配置的 DDASLLogger 日志即可。

在项目中使用 CocoaLumberjack 框架前后的变化:

// 原生的 NSLog 方法:
NSLog(@"Broken sprocket detected!");
NSLog(@"User selected file:%@ withSize:%u", filePath, fileSize);

// 集成了 CocoaLumberjack 框架之后的用法:
DDLogError(@"Broken sprocket detected!");
DDLogVerbose(@"User selected file:%@ withSize:%u", filePath, fileSize);

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

CocoaLumberjack 的日志类型

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

CocoaLumberjack 的日志级别

你需要考虑使用哪种日志级别,CocoaLumberjack 一共提供了 5 种 DDLogFlag

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

DDLogLevel 用来过滤每条日志

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模式设置不同的日志级别:

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 关联的每个记录器和级别的列表。

自定义日志格式

官方文档自定义日志的formatter格式

// .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];

更多文档详情请查看 CocoaLumberjack Documentation

遇到的问题

解决方法

参考 Getting started with the CocoaLumberjack framework.

#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 */

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343