DDLog源碼解析一:框架結(jié)構(gòu)

導(dǎo)語:

DDLog邮偎,即CocoaLumberjack是iOS開發(fā)用的最多的日志框架,出自大神Robbie Hanson之手(還有諸多知名開源框架如 XMPPFramework费就、 CocoaAsyncSocket,都是即時(shí)通信領(lǐng)域很基礎(chǔ)應(yīng)用很多的框架)川队。了解DDLog的源碼將有助于我們更好的輸出代碼中的日志信息力细,便于定位問題,也能對(duì)我們?cè)跁鴮懽约旱娜罩究蚣芑蛘咂渌K時(shí)有所啟發(fā)固额。

此系列文章將分為以下幾篇:
- DDLog源碼解析一:框架結(jié)構(gòu)
- DDLog源碼解析二:設(shè)計(jì)初衷
- DDLog源碼解析三:FileLogger

引言:為什么需要DDLog眠蚂?

我們?cè)趇OS入門階段最早能通過代碼得到的反饋,可能就是打印日志斗躏,那時(shí)我們通常會(huì)遇到第一個(gè)朋友:NSLog逝慧,這是iOS系統(tǒng)的默認(rèn)打印日志的方式。當(dāng)我們?cè)诔跫?jí)開發(fā)階段NSLog已經(jīng)足夠好啄糙,幫我們留下必要信息便于定位問題馋艺。

但隨著App復(fù)雜度的增加,調(diào)試起來變得麻煩迈套,NSLog的性能也漸漸成為了瓶頸捐祠,我們也開始有了一些個(gè)性的需求,比如想把某一類日志信息用紅色顯示在控制臺(tái)桑李,比如寫在文件中的日志只寫我們認(rèn)為很關(guān)鍵模塊的部分...... 這時(shí)候NSLog已經(jīng)無法滿足我們的需求踱蛀,我們會(huì)發(fā)現(xiàn)除了自己造輪子窿给,就只能找輪子了,幸好有DDLog率拒。

需求:DDLog能干什么崩泡?

功能上的需求可能包括:

  • 把日志寫到Xcode臺(tái)上
  • 把日志寫到文件里
  • 把日志寫到iOS系統(tǒng)日志中
  • 把日志規(guī)定多個(gè)級(jí)別,我們的日志可以歸類到不同級(jí)別猬膨;
  • 根據(jù)日志級(jí)別角撞,我們可以只輸出某個(gè)級(jí)別的日志;
  • 根據(jù)日志級(jí)別勃痴,我們可以對(duì)某些級(jí)別日志加顏色顯示谒所;
  • 對(duì)某個(gè)類設(shè)定日志級(jí)別;
  • ......

但是沛申,具備這些功能后劣领,我們可能就要關(guān)注三個(gè)指標(biāo):
準(zhǔn)確!
快速铁材!
安全尖淘!
準(zhǔn)確是最基本的,我們要保證日志如實(shí)的記錄我們記錄的東西著觉,內(nèi)容和日志順序村生、時(shí)間等都是正確的; 快速也是比較重要的點(diǎn)饼丘,試想我們的高清視頻通話的功能在通話時(shí)梆造,如果實(shí)時(shí)打印出很多信息并且寫到文件中,如果性能不過關(guān)葬毫,就可能會(huì)影響視頻通話的效果镇辉;安全范圍很寬泛,除了記錄內(nèi)容的線程安全外贴捡,最直接的就是不對(duì)app造成過大侵犯忽肛,比如寫到文件中內(nèi)容過多,將導(dǎo)致app大小劇增烂斋,對(duì)于手機(jī)容量有限的用戶將造成很大體驗(yàn)上的影響屹逛。

而DDLog的設(shè)計(jì)上考慮了這幾點(diǎn),所以我們有必要解析一下DDLog在哪些方面的設(shè)計(jì)來滿足這些需求:

正文

想知道DDLog如何此般強(qiáng)大汛骂,我們首先對(duì)DDLog的框架進(jìn)行解析罕模,先看下官方的框架示意圖(已經(jīng)與代碼部分不符合,但不影響理解):

框架結(jié)構(gòu)

本文將主要對(duì)上圖中幾個(gè)重要的類(DDLog帘瞭、DDLogger淑掌、DDAbstractLogger、DDTTYLogger蝶念、DDOSLogger抛腕、DDFileLogger芋绸、DDASLLogger)及其之間的關(guān)系進(jìn)行分析,DDLog主要是通過四種logger分別提供給開發(fā)者四個(gè)方面日志輸出的能力担敌,對(duì)應(yīng)于上面的順序依次是
DDTTYLogger:寫到Xcode控制臺(tái)摔敛、
DDOSLogger:寫到iOS10之后的系統(tǒng)日志、
DDFileLogger:寫到文件中全封、
DDASLLogger:寫到iOS10之前的系統(tǒng)日志马昙,
而DDLog類是對(duì)這四種logger進(jìn)行管理,統(tǒng)一處理日志的輸出的問題
刹悴。其余類包含fomatter(自定義輸出日志的格式和內(nèi)容)之類的處理等行楞,本文不做解析。

下圖是我整理后的圖:


image.png

DDLogger

這個(gè)協(xié)議主要定義了logger一些通用的行為:

@protocol DDLogger <NSObject>
- (void)logMessage:(DDLogMessage *)logMessage NS_SWIFT_NAME(log(message:));
@property (nonatomic, strong) id <DDLogFormatter> logFormatter;

@optional
- (void)didAddLogger;
- (void)didAddLoggerInQueue:(dispatch_queue_t)queue;
- (void)willRemoveLogger;
- (void)flush;
@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggerQueue;
@property (nonatomic, readonly) NSString *loggerName;
@end

DDAbstractLogger

作為遵守了DDLogger協(xié)議的基類颂跨,主要是通過一些屬性和方法敢伸,描述子類一些通用的行為和能力:

@interface DDAbstractLogger : NSObject <DDLogger>
{
    @public
    id <DDLogFormatter> _logFormatter;
    dispatch_queue_t _loggerQueue;
}

@property (nonatomic, strong, nullable) id <DDLogFormatter> logFormatter;
@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE) dispatch_queue_t loggerQueue;
@property (nonatomic, readonly, getter=isOnGlobalLoggingQueue)  BOOL onGlobalLoggingQueue;
@property (nonatomic, readonly, getter=isOnInternalLoggerQueue) BOOL onInternalLoggerQueue;
@end

此基類的通用init方法中定義了子類都要使用的串行隊(duì)列:

_loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
// loggerQueueName由各個(gè)子類名字構(gòu)成

void *key = (__bridge void *)self;
 void *nonNullValue = (__bridge void *)self;

dispatch_queue_set_specific(_loggerQueue, key, nonNullValue, NULL);

需要注意的是扯饶,子類init再調(diào)用這個(gè)基類的init方法時(shí)恒削,實(shí)際self是相應(yīng)的子類,這樣就會(huì)根據(jù)不同子類生成不同的_loggerQueue尾序,并且通過dispatch_get_specific和dispatch_queue_set_specific一對(duì)好基友來標(biāo)識(shí)識(shí)別每個(gè)隊(duì)列钓丰。

- (NSString *)loggerName {
    return NSStringFromClass([self class]);
}

- (BOOL)isOnGlobalLoggingQueue {
    return (dispatch_get_specific(GlobalLoggingQueueIdentityKey) != NULL);
}

- (BOOL)isOnInternalLoggerQueue {
    void *key = (__bridge void *)self;

    return (dispatch_get_specific(key) != NULL);
}

DDLog

真正的BOSS,管理各個(gè)logger的add和remove每币,并暴露各種記錄日志的log方法携丁,這一步將在[下一節(jié)](DDLog源碼解析二:線程)詳細(xì)解析,主要是線程的保護(hù)機(jī)制兰怠,這里的線程保護(hù)機(jī)制包括并不限于:保證log語句按順序記錄下來梦鉴,保證每個(gè)logger的添加、移除和level的改變等機(jī)制都能立刻再后面的log語句中生效揭保,如何保證各個(gè)logger中最終記錄的下來的日志是相同的(不會(huì)發(fā)生某一個(gè)logger的日志比其他的多幾條)......

注意肥橙,由于initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用,并且只會(huì)調(diào)用一次秸侣,在DDLog的類存筏、子類或?qū)嵗锌赡苡玫紻DLog中定義的這些資源,這里DDLog將相關(guān)公用的資源申請(qǐng)放在 類方法 +(void)initialize中味榛,保證DDLog在第一次使用時(shí)就已經(jīng)申請(qǐng)好公用資源椭坚。

+ (void)initialize {
    static dispatch_once_t DDLogOnceToken;
    
    dispatch_once(&DDLogOnceToken, ^{        
        _loggingQueue = dispatch_queue_create("cocoa.lumberjack", NULL);
        _loggingGroup = dispatch_group_create();
        
        void *nonNullValue = GlobalLoggingQueueIdentityKey; // Whatever, just not null
        dispatch_queue_set_specific(_loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL);
        
        _queueSemaphore = dispatch_semaphore_create(DDLOG_MAX_QUEUE_SIZE);
 
        _numProcessors = MAX([NSProcessInfo processInfo].processorCount, (NSUInteger) 1);
    });
}

這里申請(qǐng)的_queueSemaphore、_loggingQueue搏色、_loggingGroup都是下一節(jié)將重點(diǎn)分析的部分善茎。

DDFileLogger

DDFileLogger繼承自DDAbstractLogger,在實(shí)例化時(shí)將調(diào)用DDAbstractLogger的init方法频轿,從而得到自己的_loggerQueue巾表,并實(shí)現(xiàn)了自己的logMessage方法和其他文件處理相關(guān)方法汁掠,第三節(jié)將具體介紹。

@interface DDFileLogger : DDAbstractLogger <DDLogger> {
    DDLogFileInfo *_currentLogFileInfo;
}

DDASLLogger

DDASLLogger繼承自DDAbstractLogger集币,主要功能是將日志寫到ASL中考阱,代碼邏輯簡(jiǎn)單,但需要了解ASL相關(guān)api才能了解清楚鞠苟,本文不做解析乞榨。

DDOSLogger

DDOSLogger繼承自DDAbstractLogger,主要功能是將日志寫到os_log中当娱,代碼邏輯簡(jiǎn)單吃既,但需要了解os_log相關(guān)api才能了解清楚,本文不做解析跨细。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹦倚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冀惭,更是在濱河造成了極大的恐慌震叙,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件散休,死亡現(xiàn)場(chǎng)離奇詭異媒楼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)戚丸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門划址,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人限府,你說我怎么就攤上這事夺颤。” “怎么了胁勺?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵世澜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我姻几,道長(zhǎng)宜狐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任蛇捌,我火速辦了婚禮抚恒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘络拌。我一直安慰自己俭驮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著混萝,像睡著了一般遗遵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逸嘀,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天车要,我揣著相機(jī)與錄音,去河邊找鬼崭倘。 笑死翼岁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的司光。 我是一名探鬼主播琅坡,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼残家!你這毒婦竟也來了榆俺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤坞淮,失蹤者是張志新(化名)和其女友劉穎茴晋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碾盐,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晃跺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年揩局,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毫玖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌盯,死狀恐怖付枫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驰怎,我是刑警寧澤阐滩,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站县忌,受9級(jí)特大地震影響掂榔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜症杏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一装获、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厉颤,春花似錦穴豫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秤涩。三九已至,卻和暖如春司抱,著一層夾襖步出監(jiān)牢的瞬間筐眷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工习柠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浊竟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓津畸,卻偏偏與公主長(zhǎng)得像振定,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肉拓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,756評(píng)論 25 707
  • 附上原文作者連接:作者:金誠(chéng) 一.榜單介紹 排行榜包括四大類: 單一框架:僅提供路由后频、網(wǎng)絡(luò)層、UI層暖途、通信層或其他...
    這個(gè)美嘉不姓陳閱讀 2,252評(píng)論 1 35
  • 學(xué)習(xí)筆記/2個(gè)快速做海報(bào)的網(wǎng)站
    谷小香閱讀 157評(píng)論 0 1
  • 改變畫板大小 》選擇左邊工具欄的畫板工具 Tab鍵 》可將工具欄與條板隱藏或再次顯現(xiàn) 空格鍵 》類似捉手工具 CT...
    葡萄紫耶閱讀 582評(píng)論 0 0
  • 無論明不明白卑惜,我已經(jīng)不是神仙了,我只明白一件事驻售,愛一個(gè)人是那么的痛苦 — 紫霞仙子 《大話西游》 已經(jīng)有很長(zhǎng)一段時(shí)...
    銀劍小王子閱讀 295評(píng)論 0 2