目前大部分iOS的小型開發團隊都不是很重視log,導致很多線上發生的或者使用者反饋的bug難以排查。對於App來說一個好的日誌系統可以幫助我們用最小的代價來排查一些疑難bug,我們唯一要做的就是在合適的位置列印日誌,記錄App的執行狀況。這樣做不僅僅可以讓我們在debug時檢視App執行日誌,還可以允許App通過某些方式將日誌檔案回傳供開發人員分析問題。
關於NSLog
蘋果提供的NSLog
是大多數開發者常用的日誌工具,但是NSLog
還是無法滿足我們對於Log的其他需求,如日誌分級、日誌持久化等。另外我們知道NSLog
其實並不是printf
的封裝而是ASL的高階封裝,蘋果在文件上也說明了NSLog
的設計目的是Logs an error message
,因此我們如果在開發中大量使用NSLog
,App的效能會變得非常糟糕。關於NSLog的效能問題可以參看這篇sunnyxx的文章【NSLog效率低下的原因及嘗試lldb斷點列印Log】
需求
- 獨立開關。比如我們希望在debug版本中開啟控制檯log和檔案log,在release版本中只開啟檔案log的功能。
- 可擴充套件性和靈活性。我們知道在iOS10以後蘋果建議使用
os.log
來取代NSLog
,我們希望有一個具有很強擴充套件性的log庫,使我們可以很輕易地將log底層實現替換為os.log
而不必改變原有的log程式碼。 - 自定義格式。可以自定義log輸出的標準格式,同時也不希望原有的log呼叫介面有大量改動。
- 日誌檢視工具。我們知道
NSLog
在Mac提供了Console.app
這樣的除錯工具,使得我們即使不在xcode的debug模式下也可以隨時檢視App的日誌。甚至,我們希望使用windows PC也可以在非debug模式下檢視log。 - 分級與過濾。log應該被劃分為不同等級,同時在debug和release下我們可以設定不同級別的過濾器,低階別的log可以被過濾掉。舉個?:假如有info、default、warning、error四個等級log,我們可能會希望在debug下輸出所有等級的log,而在release下只輸出warning和error等級的log。
輪子
先上輪子Coolog[Github]
Coolog設計之初就是為了解決上面所提到的這些需求。Coolog具有高度的可擴充套件性和靈活性,同時提供了一個瀏覽器的除錯工具。目前,Coolog還有很多需要完善的地方,包括瀏覽器除錯工具目前也只是一個demo,歡迎大家成為Coolog的Contributor。
架構
為了保證可擴充套件性和靈活性,Coolog包含了了生成器(COLLogger)、格式化協議與格式化器(COLFormatable和COLLogFormatter)、驅動器(COLLoggerDriver)、引擎(COLEngine)、管理者(COLLogManager),他們之間的關係可見下圖:
下面我們來一一說明它們各自的作用。
生成器 COLLogger
顧名思義,生成器負責最終log的生成,COLLogger
是一個協議。Coolog提供了三種生成器,分別是COLNSLogger
、COLConsoleLogger
、COLFileLogger
,這三個類都實現了COLLogger
協議中- (void)log:(NSString *)logString;
這個方法。在該方法中我們最終定義了這個型別的log最後的生成方法,log引擎會通過驅動器呼叫到該方法輸出log。
除了這三種生成器,也可以自己實現COLLogger
協議來自定義一個生成器。
格式化協議 COLFormatable
這個協議只有一個方法,這個方法定義了log的輸出格式。
- (NSString *)completeLogWithType:(COLLogType)type
tag:(NSString *)tag
message:(NSString *)message
date:(NSDate *)date;
複製程式碼
格式化器 COLLogFormatter
格式化器就是實現了COLFormatable
格式化協議的類,預設我們提供了與上述三種生成器對應的三種格式化器NSLogFormatter
、ConsoleFormatter
、FileFormatter
,他們分別對應原生NSLog、控制檯與瀏覽器工具log和檔案log。這個三個類我們使用了類族提供工廠方法完成初始化,在自定義格式化器時並不需要繼承COLLogFormatter
,是需要實現COLFormatable
協議即可。
驅動器 COLLoggerDriver
驅動器是一個容器,生成器與格式化器將作為依賴注入到驅動器中,同時驅動器負責log的級別的配置,實現log分級過濾。log型別分為5中:Error>Warning>Info>Default>Debug
,過濾的級別分為7級:LevelOff>LevelError>LevelWarning>LevelInfo>LevelDefault>LevelDebug>LevelAll
,低階別的log可能會被過濾,比如如果當前過濾級別為LevelInfo
,那麼將只有Error
、Warning
、Info
這三種型別的log會被輸出。最終驅動器將交由log引擎統一管理。
引擎 COLEngine
引擎負責管理所有的驅動器,由它負責啟動log,引擎可以隨時移除或者加入單個log驅動器,實現不同log的獨立開關。
使用
初始化
[[COLLogManager sharedInstance] setup];
[[COLLogManager sharedInstance] enableFileLog]; // 開啟檔案log
[[COLLogManager sharedInstance] enableConsoleLog]; // 開啟控制檯log
// [[COLLogManager sharedInstance] enableNSLog]; // 一般控制檯log和NSLog不同時開啟
// Debug下開啟所有級別log,Release下開啟Info級別以上的log
#ifdef DEBUG
[COLLogManager sharedInstance].level = COLLogLevelAll;
#else
[COLLogManager sharedInstance].level = COLLogLevelInfo;
#endif
複製程式碼
Log
CLogError(@"tag", @"%@", @"log content");
CLogE(@"%@", @"log content");
CLogWarning(@"tag", @"%@", @"log content");
CLogW(@"%@", @"log content");
CLogInfo(@"tag", @"%@", @"log content");
CLogI(@"%@", @"log content");
CLogDefault(@"tag", @"%@", @"log content");
CLog(@"%@", @"log content");
CLogDebug(@"tag", @"%@", @"log content");
CLogD(@"%@", @"log content");
複製程式碼
瀏覽器除錯
首先開啟瀏覽器除錯功能。
[[COLLogManager sharedInstance] enableRemoteConsole];
複製程式碼
將電腦和手機連到同一個wifi下,開啟瀏覽器訪問(http://coolog.oss-cn-hangzhou.aliyuncs.com/index.html?host=ws://[YourPhoneIPAddr]:9001/coolog,注意地址後面的引數[YourPhoneIPAddr]替換為手機的IP地址。
目前效果是下面這樣。
加入自定義的log方法
- 第一步:實現一個生成器
#import "COLLogger.h"
@interface MyLogger : NSObject <COLLogger>
@end
複製程式碼
#import "MyLogger.h"
#import <os/log.h>
@implementation MyLogger
@synthesize formatterClass = _formatterClass;
+ (instancetype)logger {
return [[MyLogger alloc] init];
}
// This is your own log method. It will be called by log engine.
- (void)log:(NSString *)logString {
//For example, here below uses os_log as its implementation.
os_log(OS_LOG_DEFAULT, "%{public}s", [logString UTF8String]);
}
@end
複製程式碼
- 第二步:實現一個格式化器
#import "COLLogFormatter.h"
@interface MyLogFormatter : NSObject <COLFormatable>
@end
複製程式碼
#import "MyLogFormatter.h"
@implementation MyLogFormatter
// The log's format depends on this method.
- (NSString *)completeLogWithType:(COLLogType)type tag:(NSString *)tag message:(NSString *)message date:(NSDate *)date {
return [NSString stringWithFormat:@"tag=[%@], type=[%zd], message=[%@], date=[%@]", tag, type, message, date];
}
@end
複製程式碼
- 第三步:例項化驅動器並加入到log引擎
COLLoggerDriver *myDriver = [[COLLoggerDriver alloc] initWithLogger:[MyLogger logger]
formatter:[[MyLogFormatter alloc] init]
level:COLLogLevelInfo];
[[COLLogManager sharedInstance].logEngine addDriver:myDriver];
複製程式碼
後記
Log是我們開發過程中容易忽略的一步,但它又是十分重要的一項工作,我們要學會如何在合適的位置記錄合適log,這對於我們復現和排查問題真的有很大的幫助。
目前Coolog還是很有很多不完善的地方,包括瀏覽器除錯工具也是一個初級的demo狀態,後續工作會放在log的效能優化和除錯工具的搜尋和過濾功能,包括除錯工具的UI也會進一步優化。