深入解析iOS日志库CocoaLumberjack

vtfy7345 8年前
   <p>我们在开发中经常需要打日志,iOS提供的NSLog只能在Xcode里面查看,这种方式有如下限制:</p>    <p>1、只有处于Debug模式下才能在Xcode看到日志,其他情况无能为力。测试、产品等同事在测试和体验App的时候由于日志没有记录到本地,对于一些无法复现或者复现路径很难的问题肯定束手无策。</p>    <p>2、发布到App Store的App,下载了这个App的用户出现了无法复现或者复现路径很难的问题,我们也只能干着急。</p>    <p><a href="/misc/goto?guid=4958839053506424160" rel="nofollow,noindex">CocoaLumberjack</a> 这个开源日志库就是为了解决上诉两个问题的。</p>    <h2><strong>一、什么是CocoaLumberjack?</strong></h2>    <p>CocoaLumberjack是iOS业界有名的github开源日志库,提供如下功能:</p>    <p>1、支持日志打印到Xcode控制台,打印到mac的console程序、打印到文件</p>    <p>2、日志功能支持关闭和打开,支持Error、Warning、Info、Debug、Verbose、All日志等级</p>    <p>3、支持基于DDLogFormatter协议自定义日志格式和继承DDAbstractLogger和协议DDLogger自定义logger</p>    <h2><strong>二、用法</strong></h2>    <p>用法很简单,具体参照github,这里贴一段简单的代码</p>    <pre>  <code class="language-objectivec">[DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode console    [DDTTYLogger sharedInstance].logFormatter = [[MyCustomDDLogFormatter alloc] init];    DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // File Logger    fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling    fileLogger.maximumFileSize = 50 * 1024 * 1024; //50MB    fileLogger.logFileManager.maximumNumberOfLogFiles = 7;    fileLogger.logFormatter = [[MyCustomDDLogFormatter alloc] init];    [DDLog addLogger:fileLogger];</code></pre>    <h2><strong>三、架构图</strong></h2>    <p><img src="https://simg.open-open.com/show/ff12f684d74be1b901e2dede5b5ab5fb.png"></p>    <p>梳理一下各个类的作用和关系:</p>    <p>1、DDLog,全局单例类。此类通过方法+ (void)addLogger:(id)logger保存了一系列继承于DDAbstractLogger的logger,通过_loggers属性保存logger,这些该类相当于一个服务分发器,外部调用DDLogxxx宏(比如DDLogDebug)实际上都会路由到DDLog的log方法,最终通过lt_log方法调用各个logger的logMessage方法实现日志打印。</p>    <p>2、DDAbstractLogger,实现了协议DDLogger,所有logger都继承于该类。</p>    <p>3、DDLogger协议,最重要的方法是- (void)logMessage:(DDLogMessage *)logMessage,所有继承于DDAbstractLogger的logger通过实现该方法进行日志的打印。通过logFormatter属性保存了日志格式化实例</p>    <p>4、DDLogFormatter,通过方法- (NSString *)formatLogMessage:(DDLogMessage *)logMessage格式化DDLogMessage对象</p>    <p>5、DDTTYLogger,通过该类把日志打印到Xcode控制台。</p>    <p>6、DDASLLogger,通过该类把日志打印到mac的console程序里面。</p>    <p>7、DDFileLogger,通过该类把日志保存到文件。</p>    <p>8、DDAbstractDatabaseLogger,通过实现该类把日志保存到数据库里面。</p>    <h2><strong>四、实现关键点:</strong></h2>    <p>1、如何实现日志打印不阻塞主线程?</p>    <p>对于每个继承于DDAbstractLogger的类比如DDFileLogger,内部会创建一个分发对象_loggerQueue,所有的logMessage方法都会运行在这个_loggerQueue里面。</p>    <p>DDLog类创建了一个串行的分发队列_loggingQueue,通过_loggingQueue把外部的DDLogxxx调用异步路由到lt_log方法里面;通过全局分发组对象dispatch_group_t _loggingGroup来等待外部的DDLogxxx调用分发到相应的logger打印结束;另外,通过信号量_queueSemaphore来控制queueLogMessage最大调用量,最大为1000,大于1000将排队等待。</p>    <p>2、DDFileLogger的工作机制</p>    <p>rollingFrequency和maximumFileSize如何控制单个日志文件?</p>    <p>CocoaLumberjack在访问最近创建的一个日志文件时,会根据回滚频率和日志文件大小决定是否需要创建新的日志文件,如果日志文件存活时间大于等于回滚频率或者日志文件大小大于等于最大大小,则回滚该日志文件(给该日志文件后缀加上kDDXAttrArchivedName),并且创建一个新的日志文件来存日志;否则继续用这个日志文件存日志。日志文件存活时间是以日志文件创建时间为基准的。</p>    <p>每次调用logMessage方法的时候都会调用maybeRollLogFileDueToSize检测日志文件大小是否超过maximumFileSize; 通过dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue)创建一个计时器,定时扫描文件来实现文件存活时间功能。</p>    <p>logFileManager属性maximumNumberOfLogFiles如何控制一系列日志文件。</p>    <p>通过kvo maximumNumberOfLogFiles 和logFilesDiskQuota调用deleteOldLogFiles来实现最多保持maximumNumberOfLogFiles个日志文件。</p>    <p>3、如何实现日志文件上传到自己的服务器?</p>    <p>后台下发一个命令字,打包压缩日志文件上传即可。也可以通过自己自定义logger来实现</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/d8bad0e2683c</p>    <p> </p>