將log信息記錄到控制臺(tái),文件或遠(yuǎn)程服務(wù)器被廣泛用于幾乎任何類型的軟件開發(fā)。它是調(diào)試的最簡單的形式之一。當(dāng)調(diào)試或試圖理解一個(gè)復(fù)雜的邏輯,不管語言,日志系統(tǒng)都能夠很容易,且快速為開發(fā)和維護(hù)帶來便利。在iOS中,在正常的debug情況下當(dāng)然可以通過NSLog來打印,但是當(dāng)程序正式上線,程序閃退等意外發(fā)生的時(shí)候,這時(shí)候要是有記錄用戶行為的日志被記錄下來,那么能夠更容易分析錯(cuò)誤。這里介紹一個(gè)iOS上一個(gè)第三方日志庫CocoaLumberjack。正如它在Github 上介紹的那樣
CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for Mac and iOS.
為什么不用NSLog
你可能不知道NSLog會(huì)使你的程序變慢。NSLog首先就不是設(shè)計(jì)作為普通的debug log的,而是error log。并且每一次NSLog的打印都需要和蘋果系統(tǒng)日志(ASL)進(jìn)行一次連接,然后在打印結(jié)束后,斷開這次連接。總結(jié)下NSLog有如下幾個(gè)缺點(diǎn)
-
NSLog太不靈活 -
NSLog太慢 -
NSLog沒有等級(jí)制度log
其中最為重要一點(diǎn)就是NSLog缺少成為日志系統(tǒng)所必須的log級(jí)別。這就是本文所要介紹的CocoaLumberjack的優(yōu)點(diǎn),并且CocoaLumberjack能夠很容易的自定義自己的log等級(jí)
CocoaLumberjack
CocoaLumberjack能夠快速的給你提供如下的的功能:
- 跟蹤在程序中不斷出現(xiàn)的不可復(fù)制的bug
- 為你的應(yīng)用程序?qū)ふ移髽I(yè)級(jí)的日志解決方案。
CocoaLumberjack的使用很簡單,只需要簡單將Lumberjack文件添加到你的項(xiàng)目中,配置完后使用所定義的宏就好了。
配置CocoaLumberjack
整個(gè)配置很簡單,只需在applicationDidFinishLaunching后添加如下兩句代碼,這樣就使CocoaLumberjack定義的宏DDLog具有NSLog的功能(ASL和控制臺(tái))
[DDLog addLogger:[DDASLLogger sharedInstance]]; // 向ASL 發(fā)送log
[DDLog addLogger:[DDTTYLogger sharedInstance]]; // 向控制臺(tái)發(fā)送
對(duì)于調(diào)試代碼來說,只需要打開向控制臺(tái)發(fā)送輸出結(jié)果就好,即打開下面那個(gè)logger。CocoaLumberjack我覺得最酷的就是可以異步輸出到文件,并且能定期的發(fā)送到遠(yuǎn)程服務(wù)器,或者緩存到達(dá)一定的size發(fā)送到制定的服務(wù)器,而且使用也很簡單,只需要單獨(dú)的設(shè)置。
fileLogger = [[DDFileLogger new];
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
[DDLog addLogger:fileLogger];
上面的代碼告訴應(yīng)用程序要在系統(tǒng)上保持一周的日志文件,是不是簡單而又功能強(qiáng)大。
DDLog四種級(jí)別
DDLog默認(rèn)有四種級(jí)別,分別為:
DDlogErrorDDlogWarnDDlogInfoDDlogVerbose
DDLogLevel定義了全局的logger等級(jí),DDLogFlag 是打log時(shí)設(shè)定的log等級(jí),CocoaLumberjack會(huì)比較兩者,如果flag低于level,則不會(huì)打log.針對(duì)不同的flag。這兩個(gè)在后面自定義自己的日志系統(tǒng)中,有很大的作用。
DDlogVerbose
這個(gè)級(jí)別最低的東東,一般的來說,在系統(tǒng)實(shí)際運(yùn)行過程中,一般都是不輸出的。因此這個(gè)級(jí)別的信息,可以隨意的使用,任何覺得有利于在調(diào)試時(shí)更詳細(xì)的了解系統(tǒng)運(yùn)行狀態(tài)的東東,比如變量的值等等,都輸出來看看也無妨。當(dāng)然,在每一個(gè) Debug 調(diào)用之前,一定要加上 If 判斷。
DDlogInfo
這個(gè)應(yīng)該用來反饋系統(tǒng)的當(dāng)前狀態(tài)給最終用戶的,所以,在這里輸出的信息,應(yīng)該對(duì)最終用戶具有實(shí)際意義,也就是最終用戶要能夠看得明白是什么意思才行。從某種角度上說,Info 輸出的信息可以看作是軟件產(chǎn)品的一部分(就像那些交互界面上的文字一樣),所以需要謹(jǐn)慎對(duì)待,不可隨便。
DDlogError DDlogWarn
警告、錯(cuò)誤應(yīng)該都在系統(tǒng)運(yùn)行時(shí)檢測(cè)到了一個(gè)不正常的狀態(tài).
自定義log系統(tǒng)
對(duì)于構(gòu)建自己的log系統(tǒng),一般需要以下實(shí)現(xiàn)幾個(gè)方面
- 可以設(shè)定 Log 等級(jí)
- 可以積攢到一定量的 log 后,一次性發(fā)送給服務(wù)器,絕對(duì)不能打一個(gè) Log 就發(fā)一次
- 可以一定時(shí)間后,將未發(fā)送的 log 發(fā)送到服務(wù)器
- 可以在 App 切入后臺(tái)時(shí)將未發(fā)送的 log 發(fā)送到服務(wù)器
下面基于Lumberjack,實(shí)現(xiàn)自己的log系統(tǒng)。
自定義Log等級(jí)
如果覺得Lumberjack默認(rèn)的等級(jí)分的不夠細(xì),Lumberjack支持自定義log等級(jí),只需要自定義我們自己的頭文件,然后替代DDlog.h來引入到項(xiàng)目中去。等級(jí)的詳細(xì)程度可以根據(jù)自己的需求,首先需要undefine Lumberjack自帶的log,然后自定義自己的等級(jí)和flag。最后定義自己格式的宏,來替代DDLog和NSLog
#import "DDLog.h"
// First undefine the default stuff we don't want to use.
#undef LOG_FLAG_ERROR
#undef LOG_FLAG_WARN
#undef LOG_FLAG_INFO
#undef LOG_FLAG_DEBUG
#undef LOG_FLAG_VERBOSE
#undef LOG_LEVEL_ERROR
#undef LOG_LEVEL_WARN
#undef LOG_LEVEL_INFO
#undef LOG_LEVEL_DEBUG
#undef LOG_LEVEL_VERBOSE
#undef LOG_ERROR
#undef LOG_WARN
#undef LOG_INFO
#undef LOG_DEBUG
#undef LOG_VERBOSE
#undef DDLogError
#undef DDLogWarn
#undef DDLogInfo
#undef DDLogDebug
#undef DDLogVerbose
#undef DDLogCError
#undef DDLogCWarn
#undef DDLogCInfo
#undef DDLogCDebug
#undef DDLogCVerbose
// Now define everything how we want it
#define LOG_FLAG_FATAL (1 << 0) // 0...000001
#define LOG_FLAG_ERROR (1 << 1) // 0...000010
#define LOG_FLAG_WARN (1 << 2) // 0...000100
#define LOG_FLAG_NOTICE (1 << 3) // 0...001000
#define LOG_FLAG_INFO (1 << 4) // 0...010000
#define LOG_FLAG_DEBUG (1 << 5) // 0...100000
#define LOG_LEVEL_FATAL (LOG_FLAG_FATAL) // 0...000001
#define LOG_LEVEL_ERROR (LOG_FLAG_ERROR | LOG_LEVEL_FATAL ) // 0...000011
#define LOG_LEVEL_WARN (LOG_FLAG_WARN | LOG_LEVEL_ERROR ) // 0...000111
#define LOG_LEVEL_NOTICE (LOG_FLAG_NOTICE | LOG_LEVEL_WARN ) // 0...001111
#define LOG_LEVEL_INFO (LOG_FLAG_INFO | LOG_LEVEL_NOTICE) // 0...011111
#define LOG_LEVEL_DEBUG (LOG_FLAG_DEBUG | LOG_LEVEL_INFO ) // 0...111111
#define LOG_FATAL (ddLogLevel & LOG_FLAG_FATAL )
#define LOG_ERROR (ddLogLevel & LOG_FLAG_ERROR )
#define LOG_WARN (ddLogLevel & LOG_FLAG_WARN )
#define LOG_NOTICE (ddLogLevel & LOG_FLAG_NOTICE)
#define LOG_INFO (ddLogLevel & LOG_FLAG_INFO )
#define LOG_DEBUG (ddLogLevel & LOG_FLAG_DEBUG )
// third define Formmater
#define DDLogFatal(frmt, ...) SYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_FATAL, 0, frmt, ##__VA_ARGS__)
#define DDLogError(frmt, ...) SYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
#define DDLogWarn(frmt, ...) ASYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
#define DDLogNotice(frmt, ...) ASYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_NOTICE, 0, frmt, ##__VA_ARGS__)
#define DDLogInfo(frmt, ...) ASYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
#define DDLogDebug(frmt, ...) ASYNC_LOG_OBJC_MAYBE(ddLogLevel, LOG_FLAG_DEBUG, 0, frmt, ##__VA_ARGS__)
#define DDLogCFatal(frmt, ...) SYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_FATAL, 0, frmt, ##__VA_ARGS__)
#define DDLogCError(frmt, ...) SYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
#define DDLogCWarn(frmt, ...) ASYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
#define DDLogCNotice(frmt, ...) ASYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_NOTICE, 0, frmt, ##__VA_ARGS__)
#define DDLogCInfo(frmt, ...) ASYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
#define DDLogCDebug(frmt, ...) ASYNC_LOG_C_MAYBE(ddLogLevel, LOG_FLAG_DEBUG, 0, frmt, ##__VA_ARGS__)
自定義log格式
CocoaLumberjack 允許自定義log的消息結(jié)構(gòu)。自定義一個(gè) LogFormatter的類, 遵從DDLogFormatter 協(xié)議,重寫 formatLogMessage 方法,這個(gè)方法返回值是NSString,就是最終 log 的消息體字符串。而輸入?yún)?shù) logMessage 是由 logger 發(fā)的一個(gè) DDLogMessage對(duì)象,包含了一些必要的信息。以下是一個(gè)簡單的實(shí)現(xiàn),輸出時(shí)間和flag格式,和log消息
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
NSString *flag = stringByLogFlag(logMessage->_flag);
NSString *dateAndTime = [_dataFormatter stringFromDate:logMessage->_timestamp];
return [NSString stringWithFormat:@"%@ %@ %@", dateAndTime, flag, logMessage->_message];
}
總結(jié)
CocoaLumberjack 使構(gòu)建自己的log系統(tǒng)變得十分簡單,只需要定義level和log消息格式就好了。