今天幾乎花了一整天時間去看這個庫,也算是有些心得,總結(jié)一下。
目錄:
- 總綱
- CocoaLumberjack項目架構(gòu)
- 自定義日志分類
- 日志過濾及格式
- 文件日志
1. 總綱
CocoaLumberjack是個非常好用的開源日志庫,做iOS的應(yīng)該多多少少都有接觸??赡苡行┤藢@個名字不是很熟悉的,但代碼上用的最多的日志命令DDLogInfo總還是見過的吧,它就是CocoaLumberjack提供的一個宏。
這篇文章主要看一下這個庫的架構(gòu),和一些實際應(yīng)用上的概念。為什么要看這些呢?原因有三:
- 了解除了平常的用
DDLogInfo,DDLogError打一些日志之外,它還可以幫我們干什么; - 如何去實現(xiàn)這些高級功能,比如配置自定義的日志框架,如何精確控制什么時候打什么日志,如何配置文件日志等等;
- 由于CocoaLumberjack是個開源項目,感興趣的讀者還可以在看完這篇文章之后去為開源社區(qū)貢獻(xiàn)自己的一份力量。
2. CocoaLumberjack架構(gòu)
總的來說,這個庫由四個部分組成:DDLog, DDLogger, DDLogMessage, DDLogFormatter。我們逐個分析一下。
2.1 DDLog與DDLogger

如上圖所示,DDLog是整個庫的入口,我們平時用的DDLogLevel等宏就是直接調(diào)用DDLog的接口。一個DDLog包含著一個或多個DDLogger,比如常用的DDTTYLogger,DDASLLogger等等。當(dāng)我們調(diào)用DDLogInfo的時候,DDLog會把已有的DDLogger全部遍歷一遍,對于每個DDLogger都會調(diào)用其logMessage接口。當(dāng)然細(xì)心的讀者會意識到,這些操作不可能同步(SYNC)進(jìn)行。而且如果要保證日志的相對順序,必然會給DDLogger分配一個專屬的線性隊列(SERIAL_QUEUE)。詳情可以參考筆者之前寫的關(guān)于GCD的介紹。
2.2 DDLogger,DDLogFormatter與DDLogMessage
這三者之間的關(guān)系,用圖像解釋反而更加容易迷惑,還是用文字吧。
先闡述一下整個流程。DDLog調(diào)用DDLogger的時候,傳入的是一個DDLogMessage實例。這個實例除了日志內(nèi)容本身之外,還包含了關(guān)于這個日志的輔助信息,主要包括日志等級(level),環(huán)境(context),隊列(queueLabel),標(biāo)簽(tag),時間戳(timestamp)等等,他們的用法下面會有詳細(xì)介紹。接著,DDLogger里面如果有DDLogFormatter的話,會先調(diào)用DDLogFormatter處理日志格式,對日志進(jìn)行過濾等等操作,然后把日志打到Logger對應(yīng)的位置。
這里要注意,每個DDLogger最多只能有一個DDLogFormatter實例,可以通過setLogFormatter:設(shè)置。但是,如果有需要同時應(yīng)用多個DDLogFormatter的話,可以使用框架內(nèi)置的一個DDMultiFormatter把需要用的整合到一起。這樣就使得用戶自定義formatter非常的方便。這里要注意兩點:
-
DDMultiFormatter應(yīng)用formatter時,是按照其添加的順序逐個依次線性地進(jìn)行的,所以往DDMultiFormatter上添加formatter時一定要注意添加的順序; - 雖然
DDLogFormatter名字上叫做Formatter,它的功能并不局限于改變?nèi)罩镜母袷?。你還可以用它來過濾日志等等。
2.3 DDLogMessage相關(guān)屬性
DDLogMessage屬性的作用是給DDLogFormatter足夠的信息去修改日志格式以及進(jìn)行過濾篩選等操作。這里貼一下DDLogMessage的屬性列表:
@interface DDLogMessage : NSObject <NSCopying>
{
// Direct accessors to be used only for performance
@public
NSString *_message;
DDLogLevel _level;
DDLogFlag _flag;
NSInteger _context;
NSString *_file;
NSString *_fileName;
NSString *_function;
NSUInteger _line;
id _tag;
DDLogMessageOptions _options;
NSDate *_timestamp;
NSString *_threadID;
NSString *_threadName;
NSString *_queueLabel;
}
大部分屬性都相對比較容易理解,這里想著重梳理一下context和level。截止至寫作日期,Github上的文檔還沒有更新,所以如果你去讀文檔的話,會感到非常混亂。CocoaLumberjack自從v2.x.x以來,社區(qū)大幅度地改變了對這兩個屬性的定義。
按照文檔所說,用戶可以通過在自己的頭文件里#define自定義日志等級。但是如果你去試試就會發(fā)現(xiàn)根本行不通。原因是自從2.x版本以后,日志等級的定義方式由原來的宏改成了枚舉(NS_ENUM)。這幾乎杜絕了用戶自定義日志等級。
3. 自定義日志分類
那么用戶想在原有的幾個等級上添加更詳細(xì)的分類怎么辦呢?沒錯,用 context。CocoaLumberjack默認(rèn)context的值都是0. 如果你想添加新的分類,比如說DDLogNetwork,你需要做的就是定義自己的context 值。
#define LOG_CONTEXT_NETWORK 1
然后定義自己的宏:
#define DDLogNetworkInfo(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo, LOG_CONTEXT_NETWORK, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
這樣的好處在于對日志等級和日志環(huán)境有了一個比較清晰的界定,也給了用戶更多的自主權(quán)。比如說用戶可以很方便地定義DDLogNetworkWarning和DDLogNetworkError, 等等。而不用像文檔里面所說的先定義LOG_FLAG然后再用它來定義LOG_LEVEL之類復(fù)雜的操作。
4. 日志過濾及改變?nèi)罩靖袷?/h3>
按照上面所說的流程,日志過濾應(yīng)該是DDLogFormatter的職責(zé)。沒錯,CocoaLumberjack自帶了幾個DDLogFormatter,包括DDContextWhitelistFilterLogFormatter, DDDispatchQueueLogFormatter等。前者可以通過設(shè)置context白名單過濾日志,適用于比如只想讓當(dāng)前Logger打某個環(huán)境值的日志。最常用的情況就是希望自定義一個新的分類,然后只把該分類的日志都打到一個文件里面。用法如下:
DDContextWhitelistFilterLogFormatter *contextFilter = [[DDContextWhitelistFilterLogFormatter alloc] init];
[contextFilter addToWhitelist: LOG_LEVEL_NETWORK];
[[DDTTYLogger sharedInstance] setLogFormatter:contextFilter];
[[DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelInfo];
這兩個formatter只是個用法示例,用戶完全可以根據(jù)自己的需要依樣畫葫蘆,自定義格式化工具和過濾,然后加進(jìn)指定的DDLogger。
5. 文件日志
CocoaLumberjack自帶了一個文件日志類:DDFileLogger。如果不需要自定義日志路徑的話,用法比較簡單:
DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
[DDLog addLogger:fileLogger withLevel:DDLogLevelInfo];
默認(rèn)日志路徑在:
/app/path/Library/Cached/。
如果需要自定義日志路徑的話,需要稍微多幾行代碼:
NSString *path = "/Some/Path";
id<DDLogFileManager> logFileManager = [[DDLogFileManagerDefault alloc] initWithLogsDirectory:path];
logFileManager.maximumNumberOfLogFiles = 5; //可自行調(diào)整
DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
fileLogger.rollingFrequency = 24*60*60; // 一天,可自行調(diào)整
[DDLog addLogger:fileLogger withLevel:DDLogLevelInfo];
寫在最后
今天大概就先寫到這里,希望能幫到有需要的人。如果有什么疑問歡迎在下面回復(fù)提出,筆者將盡量及時作出回復(fù)。