前言
最近剛接手一個(gè)新項(xiàng)目,被分派到一個(gè)日志功能的需求,我們的需求是把日志記錄到本地,然后讓用戶上傳到服務(wù)端,我分別嘗試了用NSLogger和CocoaLumberjack完成便锨,下面說下具體過程。
過程
** NSLogger**
github地址
NSLogger的導(dǎo)入十分簡單我碟,以下是github上的Installation
Step 1. Download the NSLogger desktop app on your Mac.
Step 2. Add the NSLogger framework to your project.
Step 3. There is no step 3…
NSLogger在mac端有一個(gè)專門查看日志的客戶端軟件放案,可在github上下載。
而在客戶端中保存的日志格式為.rawnsloggerdata矫俺。LoggerSetBufferFile這句代碼讓你設(shè)置日志保存的位置吱殉。如下:
NSString *cacheDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *logPath = [cacheDirectory stringByAppendingPathComponent:@"log.rawnsloggerdata"];
LoggerSetBufferFile(NULL, (__bridge CFStringRef)logPath);
導(dǎo)入框架后,為了方便調(diào)用厘托,可以按照自己的想法先定義宏定義
#define NSLog(...) LogMessageF(__FILE__, __LINE__, __FUNCTION__, @"NSLog", 0, __VA_ARGS__)
下面只要在你想打印日志的地方加入NSLog()即可友雳。
需要注意的是:當(dāng)電腦和測試機(jī)處于同一個(gè)局域網(wǎng)內(nèi),且NSLogger Viewer開啟時(shí)铅匹,NSLogger Viewer會(huì)將手機(jī)客戶端運(yùn)行時(shí)的日志實(shí)時(shí)顯示押赊,而此時(shí)這部分日志是不會(huì)寫入手機(jī)客戶端本地的,只有NSLogger Viewer沒開啟監(jiān)控包斑,日志才會(huì)寫入本地流礁,這點(diǎn)要注意!B薹帷神帅!
另外,NSLogger支持多種數(shù)據(jù)類型萌抵,如文件找御,圖片,文字等等绍填,有需要同學(xué)可以深入研究下萎坷。
因?yàn)镹SLogger只能把日志保存.rawnsloggerdata格式,并且只能在mac上用相應(yīng)客戶端查看沐兰,不符合我們的需求,于是蔽挠,我用了另一個(gè)框架-CocoaLumberjack住闯。
** CocoaLumberjack**
github地址
導(dǎo)入框架可參考這篇文章,寫得不錯(cuò)瓜浸,下面也根據(jù)我自身的需求大概說下:
首先說下CocoaLumberjack的日志級(jí)別:
LOG_LEVEL_ERROR:如果設(shè)置為LOG_LEVEL_ERROR,僅僅能看到Error相關(guān)的日志輸出比原。
LOG_LEVEL_WARN:如果設(shè)置為LOG_LEVEL_WARN插佛,能看到Error、Warn相關(guān)的日志輸出量窘。
LOG_LEVEL_INFO:如果設(shè)置為LOG_LEVEL_INFO雇寇,能夠看到Error、Warn蚌铜、Info相關(guān)的日志輸出锨侯。
LOG_LEVEL_DEBUG:如果設(shè)置為LOG_LEVEL_DEBUG,能夠看到Error/Warn/Info/Debug相關(guān)的日志輸出冬殃。
LOG_LEVEL_VERBOSE:如果設(shè)置為LOG_FLAG_VERBOSE囚痴,能夠看到Error/Warn/Info/Debug/Verbose相關(guān)日志輸出。
LOG_LEVEL_OFF:不輸出日志
CocoaLumberjack記錄日志有幾種方式审葬,可以根據(jù)自己的需求選擇是否導(dǎo)入
DDASLLogger(發(fā)送日志語句到蘋果的日志系統(tǒng)深滚,以便它們顯示在Console.app上)
DDTTYLoyger(發(fā)送日志語句到Xcode控制臺(tái),如果可用)
DDFIleLoger(把日志語句發(fā)送至文件)
因?yàn)槲抑恍枰讶罩景l(fā)送控制臺(tái)和記錄在文件涣觉,所以我只導(dǎo)入了DDTTYLoyger和DDFIleLoger
在pch頭文件設(shè)置全局日志等級(jí)
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
DDLogLevel 定義了全局的 log 等級(jí)痴荐,DDLogFlag 是我們打 log 時(shí)設(shè)定的 log 等級(jí),CocoaLumberjack 會(huì)比較兩者官册,如果 flag 低于 level生兆,則不會(huì)打 log:
//在appdelegate中初始化
//發(fā)送日志語句到Xcode控制臺(tái)
[DDLog addLogger:[DDTTYLogger sharedInstance]];
//把日志語句發(fā)送至文件
DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
[DDLog addLogger:fileLogger withLevel:DDLogLevelError];
在實(shí)際應(yīng)用中我定義了兩個(gè)不同日志等級(jí)的宏
//INFO級(jí)別
#define HZLogInfo(format, ...) DDLogInfo((@"[%@][Line:%d]:" format), [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, ## __VA_ARGS__)
//Error級(jí)別
#define HZLogError(format, ...) DDLogError((@"[%@][Line:%d]:" format), [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, ## __VA_ARGS__)
解釋下上面代碼:
我設(shè)置全局日志等級(jí)為DDLogLevelVerbose,可打印Error/Warn/Info/Debug/Verbose相關(guān)等級(jí)的日志攀隔,當(dāng)然這里還可以根據(jù)debug和release設(shè)置不同的等級(jí)皂贩。
另外,導(dǎo)入DDTTYLogger沒設(shè)置level昆汹,這里會(huì)默認(rèn)打印全部等級(jí)日志明刷,而DDFileLogger我設(shè)置了DDLogLevelError等級(jí),所以只有error等級(jí)日志才能寫入文件满粗。
所以我定義的兩句宏:
HZLogInfo寫入的是INFO級(jí)別的日志辈末,所以這類日志只能顯示在控制臺(tái),而不能寫入文件映皆。
HZLogError是ERROR級(jí)別日志挤聘,所以這類日志不僅顯示在控制臺(tái),還可以寫入文件捅彻。
當(dāng)然各位可以根據(jù)自己需求而定组去,CocoaLumberjack記錄的本地日志格式為.log格式,上傳到服務(wù)器時(shí)不需特定在mac端觀看步淹,滿足我們的需求从隆。
另外诚撵,我的需求中還需要捕獲異常并寫入日志,這篇異常捕捉的文章寫得十分詳細(xì)键闺,推薦給大家寿烟。要注意的是,使用 NSUncaughtExceptionHandler 捕獲 NSException辛燥,后注冊(cè)的NSUncaughtExceptionHandler會(huì)把前面注冊(cè)的handler覆蓋筛武,在我們的項(xiàng)目中就有這樣的問題。正確的做法是利用NSGetUncaughtExceptionHandler拿到上一個(gè)注冊(cè)的Handler挎塌,處理完自己的Handler后再把exception傳給上一個(gè)Handler徘六。示例如下
static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//保存之前的Handler
previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
// 將下面C函數(shù)的函數(shù)地址當(dāng)做參數(shù)
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
return YES;
}
void UncaughtExceptionHandler(NSException *exception){
// 錯(cuò)誤reason
NSString *reason = [exception reason];
// exception name
NSString *name = [exception name];
//異常的堆棧信息
NSArray *stackArray = [exception callStackSymbols];
//拼接錯(cuò)誤信息
NSString *exceptionInfo = [NSString stringWithFormat:@" Exception reason: %@\nException name: %@\nException stack:%@", reason , name , stackArray];
//在此處理我們自己的Handler
//調(diào)用之前注冊(cè)的Handler
previousUncaughtExceptionHandler(exception);
}
NSSetUncaughtExceptionHandler()函數(shù)指針的惡意覆蓋的問題就解決了。參考文章
結(jié)束
學(xué)習(xí)之路勃蜘,與君共勉硕噩。