文章目录
前言
全量日志
就是app的运行日志打印等等。有时候光凭Crash
日志并不能找到并解决问题,如果有Crash
时App
的日志输出,则会事半功倍。
CocoaLumberjack
是OSX
和iOS
平台优秀的全量日志
抓取第三方库。github链接
此篇文章更着重于分析其实现以及结构组成。
日志重定向
我们通过日志重定向可以进行将控制台的输出日志存储到文件中
- (void)redirectLogToDocumentFolder
{
// 获取沙盒路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentDirectory = [paths objectAtIndex:0];
// 获取打印输出文件路径
NSString *fileName = [NSString stringWithFormat:@"myData.log"];
NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
// 先删除已经存在的文件
NSFileManager *defaultManager = [NSFileManager defaultManager];
[defaultManager removeItemAtPath:logFilePath error:nil];
// 将NSLog的输出重定向到文件,因为C语言的printf打印是往stdout打印的,这里也把它重定向到文件
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+", stderr);
}
这样的坏处是,当重定向之后,控制台不再打印日志输出了,虽然我们可以判断xcode是否连接,然后再进行重定向,来解决连接xcode调试的问题。
但是还是有不足,就是当你的日志输出你只想自己看到,而不想影响控制台的输出时,显然重定向不能做到,我们需要自己的日志输出入口,同时还要能监听到系统的日志输出,而不影响控制台的日志。
CocoaLumberjack
就可以帮我们实现这个功能。一般来说我们需要创建3个logger
,分别是
ASL
用于记录系统的日志输出,这个输出xcode有些
不会打印出来TTY
控制台会打印的日志输出File
写入文件中,存为log文件,然后上传,便于分析问题。
Lumberjack组成
传统的NSLog()
函数将它的输出指向两个地方:
- 苹果系统日志
ASL (Apple System Logs)
StdErr
(如果StdErr
是一个TTY
),所以日志语句显示在Xcode
控制台
Capture 捕捉
DDASLLogCapture
为CocoaLumberjack
中唯一的一个Capture
类,用与捕获ASL
日志
Logger 输出
logger
用于输出日志,有
DDASLLogger
用于输出到ASL
DDOSLogger
是iOS 10
之后公开的日志输出方式,用于取代ASL
,你可以在官方文档 中查看接口DDTTYLogger
该类为终端输出
或Xcode控制台
输出提供一个日志记录器DDFileLogger
用于将日志输出到文件中,我们一般存储日志文件之后进行压缩。
要实现替换 NSLog()
的功能,您可以简单地添加DDASLLogger
和一个DDTTYLogger
。
但是,如果您选择使用文件记录器(DDFileLogger)
(以获得更快的性能),
你可以选择只使用一个文件记录器(DDFileLogger)
和一个tty记录器(DDTTYLogger)
。
message and formatter 消息以及格式化
DDLogMessage
是封装的消息实体
DDLogFormatter
是对输出的字符串格式化的类别
你可以对照CocoaLumberjack
源码中的Demos
进行更好的理解
里面各种场景都很有参考意义。
ASL 日志系统
ASL (Apple system logger)
是苹果公司自己实现的一套输出日志的接口。
通过DDASLLogger.m
文件,我们了解到captureAslLogs
做了捕捉日志输出的功能
+ (void)captureAslLogs {
@autoreleasepool
{
/*
We use ASL_KEY_MSG_ID to see each message once, but there's no
obvious way to get the "next" ID. To bootstrap the process, we'll
search by timestamp until we've seen a message.
*/
struct timeval timeval = {
.tv_sec = 0
};
gettimeofday(&timeval, NULL);
unsigned long long startTime = (unsigned long long)timeval.tv_sec;
__block unsigned long long lastSeenID = 0;
/*
syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message)
through the notify API when it saves messages to the ASL database.
There is some coalescing - currently it is sent at most twice per
second - but there is no documented guarantee about this. In any
case, there may be multiple messages per notification.
Notify notifications don't carry any payload, so we need to search
for the messages.
*/
int notifyToken = 0; // Can be used to unregister with n