OSLog与NSLog,OSLog的实践

一、OSLog与NSLog的区别

NSLog:

NSLog的文档,第一句话就说:Logs an error message to the Apple System Log facility.,所以首先,NSLog就不是设计作为普通的debug log的,而是error log;其次,NSLog也并非是printf的简单封装,而是Apple System Log(ASL)的封装。

ASL是啥?从官方手册上可以看到说明:

These routines provide an interface to the Apple System Log facility. They are intended to be a replacement for the syslog(3) API, which will continue to be supported for backwards compatibility.

意思就是ASL是个系统级别的log工具,syslog的替代版,提供了一系列强大的log功能。不过一般我们接触不到,NSLog就对它提供了高层次的封装,如这篇文档所提到的:

You can use two interfaces in OS X to log messages: ASL and Syslog. You can also use a number of higher-level approaches such as NSLog. However, because most daemons are not linked against Foundation or the Application Kit, the low-level APIs are often more appropriate

一些底层相关的守护进程(deamons)不会link如Foundation等高层框架,所以asl用在这儿正合适;而对于应用层的用NSLog。

OSLog:

苹果官方文档这样介绍:OSLog是一个统一的日志系统,在iOS 10中可用。macOS 10.12及以上版本,tvOS 10.0及以上版本,watchOS 3.0及以上版本。该系统将取代Apple system Logger (ASL)和Syslog api。

它相比以前的NSLog更加优越,苹果极力推荐使用新的日志系统。以前,日志消息被写到磁盘上的特定位置,比如/etc/system.log。统一日志系统将消息存储在内存和数据存储中,而不是写入基于文本的日志文件。

NSLog效率低的原因是NSLog做了两件事:

  • 1.-它将日志消息写入Apple System Logging (asl)设施。这允许日志消息显示在Console.app中。
  • 2.-它还检查应用程序的stderr流是否要去终端(比如当应用程序通过Xcode运行时)。如果是,它将日志消息写入stderr(这样它就会显示在Xcode控制台中)。

要向ASL设施发送日志消息,基本上需要打开到ASL守护进程的客户机连接并发送消息。BUT -每个线程必须使用单独的客户端连接。因此,为了线程安全,每次调用NSLog时,它都会打开一个新的asl客户端连接,发送消息,然后关闭连接。所以说,当这个过程出现N次时,消耗大量资源导致程序变慢也就不奇怪了。

OSLog相比NSlog的优点

1.新的日志系统,跨多个平台Mac,iOS,WachOS
2.相比以前的系统更加的高效
3.日志组织的更有条理。有了 Log levels(default info debug error fault)一些第三方的日志如也有类似功能, 日志有分类的功能
4.保护隐私功能,格式化信息
5.自动提供很多有用信息
6.可以通过控制台app查看日志
7.日志不是可读文本(用console 及相关命令行工具 log 可以查),但可以打包获取,分发
8.苹果提供了日志处理命令行工具
9.可以使用配置文件对日志进行配置

二、OSLog重要部分讲解

Log Levels

统一日志系统使用了几个日志级别,它们对应于应用程序可能需要捕获的不同类型的消息,并定义消息何时保存到数据存储中,以及消息保存多长时间。系统为每个级别实现标准行为。可以使用日志命令行工具或自定义配置文件覆盖此行为(请参阅调试时自定义日志行为)。

  • default
    默认级别的消息最初存储在内存缓冲区中。在不更改配置的情况下,它们将被压缩并随着内存缓冲区的填充移动到数据存储区。它们会一直保留到超过存储配额,此时,最古老的消息将被清除。使用此级别捕获可能导致失败的信息。

  • info
    信息级消息最初存储在内存缓冲区中。如果不进行配置更改,则不会将它们移动到数据存储区,并在内存缓冲区填充时清除它们。但是,当发生错误或错误时,它们会在数据存储中捕获。当信息级别的消息被添加到数据存储中时,它们将一直保留在那里,直到超过存储配额,此时,最古老的消息将被清除。使用此级别捕获对故障排除可能有帮助但不是必需的信息。

  • debug
    调试级别的消息只在通过配置更改启用调试日志记录时在内存中捕获。根据配置的持久性设置清除它们。此级别记录的消息包含在开发期间或排除特定问题时可能有用的信息。调试日志记录用于开发环境,而不是发布软件。

  • error
    错误级别的消息总是保存在数据存储中。它们会一直保留到超过存储配额,此时,最古老的消息将被清除。错误级消息用于报告流程级错误。如果存在活动对象,则此级别的日志记录将捕获整个流程链的信息。

  • fault
    故障级消息总是保存在数据存储中。它们会一直保留到超过存储配额,此时,最古老的消息将被清除。故障级消息仅用于捕获系统级或多进程错误。如果存在活动对象,则此级别的日志记录将捕获整个流程链的信息。

保护隐私功能,格式化信息

要格式化日志消息,请使用标准的NSString或printf格式字符串,如清单4所示。有关格式化规则,请参阅字符串格式说明符。

image.png
// 代码示例
os_log("now build-in %{time_t}d ",Int(Date().timeIntervalSince1970))
os_log("now %@ ", NSDate())
os_log("uuid_t %@ ",NSUUID())
os_log("self %@ ",self)
os_log("string %{public}s ","string")
os_log("iec-bytes %{iec-bytes}d ",1024)

// 结果
2022-10-29 15:09:07.724062+0800 SwiftDemo[5317:849193] now build-in 2022-10-29 15:09:07+0800
2022-10-29 15:09:07.724151+0800 SwiftDemo[5317:849193] now Sat Oct 29 15:09:07 2022
2022-10-29 15:09:07.724187+0800 SwiftDemo[5317:849193] uuid_t 7779AA5E-6C78-4D01-8C80-588E0191CDF0
2022-10-29 15:09:07.724216+0800 SwiftDemo[5317:849193] self <SwiftDemo.ViewController: 0x10550ba80>
2022-10-29 15:09:07.724257+0800 SwiftDemo[5317:849193] string string
2022-10-29 15:09:07.724270+0800 SwiftDemo[5317:849193] iec-bytes 1 KiB

三、OSLog的实践

1.基本用法

// 输出一个default-level 信息
os_log("This is a log message.")
// 输出一个info-level 信息
os_log("This is additional info that may be helpful for troubleshooting.", log: OSLog.default, type: .info)
// 输出一个自定义子系统,级别为debug-level 信息
let customLog = OSLog(subsystem: "com.your_company.your_subsystem_name.plist", category: "your_category_name")
os_log("This is info that may be helpful during development or debugging.", log: customLog, type: .debug)
// 示例与结果
let myLog : OSLog = OSLog(subsystem:"mySubsystem", category:"myCategory")
let date = Date()
os_log("测试default-date-%@", date as CVarArg)
os_log(.debug, log: myLog, "测试debug-date-%@", date as CVarArg)
os_log(.error, log: myLog, "测试error-date-%@", date as CVarArg)
os_log(.info, log: myLog, "测试info-date-%@", date as CVarArg)
os_log(.fault, log: myLog, "测试fault-date-%@", date as CVarArg)

// 结果:
2022-10-29 14:02:17.336726+0800 SwiftDemo[41978:4599820] 测试default-date-Sat Oct 29 14:02:17 2022
2022-10-29 14:02:17.336798+0800 SwiftDemo[41978:4599820] [myCategory] 测试debug-date-Sat Oct 29 14:02:17 2022
2022-10-29 14:02:17.336850+0800 SwiftDemo[41978:4599820] [myCategory] 测试error-date-Sat Oct 29 14:02:17 2022
2022-10-29 14:02:17.336911+0800 SwiftDemo[41978:4599820] [myCategory] 测试info-date-Sat Oct 29 14:02:17 2022
2022-10-29 14:02:17.337007+0800 SwiftDemo[41978:4599820] [myCategory] 测试fault-date-Sat Oct 29 14:02:17 2022

2.在控制台app中的展示

image.png

3.os_log与NSlog效率对比

// 单条线程测试时间对比
private func timeTest() {
        let startNSLog = CFAbsoluteTimeGetCurrent()
        for i in 0..<10000 {
            NSLog("nslog---%d", i)
        }
        let endNSLog = CFAbsoluteTimeGetCurrent()

        let startOSLog = CFAbsoluteTimeGetCurrent()
        for i in 0..<10000 {
            os_log("oslog---%d",i)
        }
        let endOSLog = CFAbsoluteTimeGetCurrent()

        print("NSLogTime:%f",endNSLog-startNSLog)
        print("OSLogTime:%f",endOSLog-startOSLog)
}

// 结果
NSLogTime:%f 0.7270380258560181
OSLogTime:%f 0.5310770273208618

NSLogTime:%f 0.6149619817733765
OSLogTime:%f 0.4749699831008911

NSLogTime:%f 0.7736960649490356
OSLogTime:%f 0.5272040367126465

// 多条线程测试时间对比
private func timeTest() {
        let queue = DispatchQueue(label: "myQueue", attributes: [.concurrent])
        let startNSLog = CFAbsoluteTimeGetCurrent()
        for i in 0..<5000 {
            NSLog("nslog---%d", i)
        }
        queue.async {
            for i in 0..<5000 {
                queue.sync {
                    NSLog("nslog---%d", i)
                }
            }
            let endNSLog = CFAbsoluteTimeGetCurrent()
            print("NSLogTime:%f \n",endNSLog-startNSLog)
        }

        let startOSLog = CFAbsoluteTimeGetCurrent()
        for i in 0..<5000 {
            os_log("oslog---%d",i)
        }
        queue.async {
            for i in 0..<5000 {
                DispatchQueue.global().sync {
                    os_log("oslog---%d",i)
                }
            }
            let endOSLog = CFAbsoluteTimeGetCurrent()
            print("OSLogTime:%f",endOSLog-startOSLog)
        }
}

// 结果
NSLogTime:%f 1.227236032485962
OSLogTime:%f 0.8558480739593506

NSLogTime:%f 1.2800310850143433
OSLogTime:%f 0.9496610164642334

NSLogTime:%f 1.2216260433197021
OSLogTime:%f 0.9060399532318115

四、第三方log工具:CocoaLumberjack

CocoaLumberjack是适用于Mac和iOS的快速,简单,功能强大且灵活的日志记录框架。

关于CocoaLumberjack,github参考https://github.com/CocoaLumberjack/CocoaLumberjack

1.Logger

CocoaLumberjack内置了以下几种Logger。
DDASLLogger:将日志写入到控制台.app中。在iOS10开始过时
DDTTYLogger:将日志写入到Xcode控制台。
DDFileLogger:很容易理解,是将log写入到文件中。
DDOSLogger:在iOS10开始使用,在将Log输出到 控制台.app 和 Xcode控制台。跟NSLog的输出方式一致。当然,经过处理之后,性能会比直接使用NSLog要好。

而我们常用的NSLog会将日志写入到控制台.app和Xcode控制台。 所以,想要替换NSLog,官方推荐的做法是:

在iOS10及以上系统版本,使用DDOSLogger。
在iOS10以下版本,使用DDASLLogger+DDTTYLogger。

2.代码实践

private func ddLogTest() {
//        DDLog.add(DDOSLogger.sharedInstance, with: .info)
        DDLog.add(DDOSLogger.sharedInstance)

        DDLogError("测试-error")
        DDLogWarn("测试-warning")
        DDLogInfo("测试-info")
        DDLogDebug("测试-debug")
        DDLogVerbose("测试-verbose")
    }

// 结果
2022-10-29 15:45:42.128931+0800 SwiftDemo[5368:859965] 测试-error
2022-10-29 15:45:42.129108+0800 SwiftDemo[5368:859966] 测试-warning
2022-10-29 15:45:42.129137+0800 SwiftDemo[5368:859966] 测试-info
2022-10-29 15:45:42.129158+0800 SwiftDemo[5368:859966] 测试-debug
2022-10-29 15:45:42.129176+0800 SwiftDemo[5368:859966] 测试-verbose

// 若打开 DDLog.add(DDOSLogger.sharedInstance, with: .info) 代码,结果如下,只会输出级别在info及以上的log
2022-10-29 15:46:45.208849+0800 SwiftDemo[5372:860630] 测试-error
2022-10-29 15:46:45.209026+0800 SwiftDemo[5372:860627] 测试-warning
2022-10-29 15:46:45.209051+0800 SwiftDemo[5372:860627] 测试-info

3.DDFileLogger写入日志文件

如果我们需要做日志文件的写入和读取,那么DDFileLogger是一个很好用的工具,只需添加DDFileLogger就可以将日志记录到文件里面了,跟添加DDOSLogger一样。

// 示例
private func ddLogTest() {
        DDLog.add(DDOSLogger.sharedInstance, with: .info)

        let fileLogger = DDFileLogger.init()
        fileLogger.rollingFrequency = 60 * 60
        DDLog.add(fileLogger)

        DDLogError("测试-error")
        DDLogWarn("测试-warning")
        DDLogInfo("测试-info")
        DDLogDebug("测试-debug")
        DDLogVerbose("测试-verbose")

        let path = fileLogger.currentLogFileInfo?.filePath ?? ""
        let readStr = try? String.init(contentsOfFile: path, encoding: .utf8)

        print("读取出来的log是:\n\(readStr ?? "")")
    }

// 结果
2022-10-29 16:09:24.470125+0800 SwiftDemo[45369:4723867] 测试-error
2022-10-29 16:09:24.509890+0800 SwiftDemo[45369:4723867] 测试-warning
2022-10-29 16:09:24.509973+0800 SwiftDemo[45369:4723864] 测试-info
读取出来的log是:
2022/10/29 08:09:24:470  测试-error
2022/10/29 08:09:24:510  测试-warning
2022/10/29 08:09:24:510  测试-info
2022/10/29 08:09:24:510  测试-debug
2022/10/29 08:09:24:510  测试-verbose

我们也能通过路径找到这个Log文件,文件是text可以直接点开查看

image.png

4.DDFileLogger扩展

log文件的属性自定义

根据项目需求,我们可能需要对log文件做很多的自定义设置。 默认情况下,log文件在多次启动的时候是会重用的,24小时内将log写入到同一个文件中,当文件大小超过1MB或者创建时间超过24小时,会新生成一个log文件,后面的log会写入到新的文件中。文件的个数超过5个的时候,会删除旧的文件。logs文件夹大小超过20M的时候,也会删除旧的文件以释放磁盘空间。

CocoaLumberjack本身提供了这些属性让我们自定义

    DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
    //重用log文件,不要每次启动都创建新的log文件(默认值是NO)
    fileLogger.doNotReuseLogFiles = NO;
    //log文件在24小时内有效,超过时间创建新log文件(默认值是24小时)
    fileLogger.rollingFrequency = 60*60*24;
    //log文件的最大3M(默认值1M)
    fileLogger.maximumFileSize = 1024*1024*3;
    //最多保存7个log文件(默认值是5)
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
    //log文件夹最多保存10M(默认值是20M)
    fileLogger.logFileManager.logFilesDiskQuota = 1014*1024*20;

    //添加文件写入logger
    [DDLog addLogger:fileLogger];
Log文件夹与文件名的获取

有些时候我们想要获取到log文件所在的路径,用来进行一些操作。比如将log文件读取出来查看,或者将log文件上传到服务器去。这些路径可以从fileLogger对象里面能够获取到。

//logs文件夹路径
DDLogInfo(@"logsDirectory=%@",fileLogger.logFileManager.logsDirectory);
//logs文件夹的所有log文件路径
DDLogInfo(@"sortedLogFilePaths=%@",fileLogger.logFileManager.sortedLogFilePaths);
//当前活跃的log文件路径
DDLogInfo(@"currentFilePath=%@",fileLogger.currentLogFileInfo.filePath);
log文件存放目录自定义

默认的Log文件存放在沙盒的Library/Caches/Logs目录中,如果想自定义存放位置,可以在创建DDFileogger的时候可以进行设置。

//修改Logs文件夹的位置
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *baseDir = paths.firstObject;
    NSString *logsDirectory = [baseDir stringByAppendingPathComponent:@"SQLog/Logs"];
    DDLogFileManagerDefault *defaultManager = [[DDLogFileManagerDefault alloc] initWithLogsDirectory:logsDirectory];

    DDFileLogger *fileLogger = [[DDFileLogger alloc] initWithLogFileManager:defaultManager];
log文件名自定义

默认的文件名是这样的<bundle identifier><date><time>.log,例如liepin.SwiftDemo 2022-10-31--06-31-04-667.log,这里的时间是差了8小时的

修改日志文件名称官方也作了说明:

*If you wish to change default filename, you can override following two methods.
*- newLogFileName method would be called on new logfile creation.
*- isLogFile method would be called to filter log files from all other files in logsDirectory.
*You have to parse given filename and return YES if it is logFile.

意思就是如果想要改变文件名称需要重写newLogFileName和isLogFile这两个方法。新建一个子类继承DDLogFileManagerDefault,重写方法,然后使用时用这个子类即可(以下只当例子,具体自己定义):

class CustomClass: DDLogFileManagerDefault {
    
    override var newLogFileName: String {
        return "newName"
    }
    
    override func isLogFile(withName fileName: String) -> Bool {
        return true
    }
    
}


private func ddLogTest() {
        DDLog.add(DDOSLogger.sharedInstance, with: .info)
        
        let manager = CustomClass.init()
        let fileLogger = DDFileLogger.init(logFileManager: manager, completionQueue: nil)
        fileLogger.rollingFrequency = 60 * 60
        DDLog.add(fileLogger)
}

结果:


image.png

参考资料:

os.log模块介绍:https://cloud.tencent.com/developer/article/1993775

NSLog详解:<u>http://t.zoukankan.com/mumoozhu-p-4495260.html</u>

日志效率参考:<u>https://www.jianshu.com/p/3e3804d6d60d</u>

官方问题反馈:<u>https://developer.apple.com/forums/thread/82736?page=2</u>

CocoaLumberjack:https://github.com/CocoaLumberjack/CocoaLumberjack

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容

  • 本文是< > 第十五篇学习笔记. iOS10之前获取 NSLog 的日志 NSLog 其实就是一个 C 函数,函数...
    forping阅读 1,206评论 0 0
  • 本文的原始发布地址在我的个人博客 https://yohunl.com/iosri-zhi-huo-qu-he-s...
    yohunl阅读 5,728评论 2 42
  • 套接字地址结构 ipv4套接字地址结构 POSIX定义如下: sin_len字段,是由处理来自不同协议族的套接字地...
    FengyunSky阅读 564评论 0 1
  • 首先,我们去搜索一下 Apple 关于 NSLog 的文档。我们会发现 NSLog 方法中调用的是 NSLogv ...
    jing091111阅读 1,207评论 0 3
  • 前言 NSLog 作为 iOS开发常用的调试和日志打印方法,大家都是很熟悉了,开源社区也为我们贡献了很多非常优秀的...
    madaoCN阅读 1,433评论 0 2