iOS一個靈活可擴充套件的開源Log庫

RyanLeeLY發表於2018-05-22

目前大部分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),他們之間的關係可見下圖:

Architecture

下面我們來一一說明它們各自的作用。

生成器 COLLogger

顧名思義,生成器負責最終log的生成,COLLogger是一個協議。Coolog提供了三種生成器,分別是COLNSLoggerCOLConsoleLoggerCOLFileLogger,這三個類都實現了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格式化協議的類,預設我們提供了與上述三種生成器對應的三種格式化器NSLogFormatterConsoleFormatterFileFormatter,他們分別對應原生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,那麼將只有ErrorWarningInfo這三種型別的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地址。

目前效果是下面這樣。

BrowserTool

加入自定義的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也會進一步優化。

相關文章