知识点记录

105 阅读1分钟

DDLogger

1.isArchived在fileInfo中如何保存这个状态? 这里利用系统API<sys/xattr.h>的setxattr方法将该flag直接保存在文件描述中,getxattr用来获取,removexattr用于删除flag。

2.容错处理

@try {
    NSFileHandle *handle = [self lt_currentLogFileHandle];
    [handle seekToEndOfFile];
    [handle writeData:data];
} @catch (NSException *exception) {
    exception_count++;
    if (exception_count <= 10) {
        NSLogError(@"DDFileLogger.logMessage: %@", exception);
        if (exception_count == 10) {
            NSLogError(@"DDFileLogger.logMessage: Too many exceptions -- will not log any more of them.");
        }
    }
}

3.避免多次响应

static BOOL implementsDeprecatedWillLog = NO;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
implementsDeprecatedWillLog = [self respondsToSelector:@selector(willLogMessage)];
});

if (implementsDeprecatedWillLog) {
    [self willLogMessage];
} else {
    [self willLogMessage:_currentLogFileInfo];
}

4.同时还利用消息转发,将过期方法转移至 dummyMethod 避免 unrecognized selector sent to instance crash 即在执行当前系统不支持的api时,将消息转发到一个存在的空方法中

- (void)dummyMethod {}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(willLogMessage) || aSelector == @selector(didLogMessage)) {
        // Ignore calls to deprecated methods.
        return [self methodSignatureForSelector:@selector(dummyMethod)];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if (anInvocation.selector != @selector(dummyMethod)) {
        [super forwardInvocation:anInvocation];
    }
}

5.buffer机制
DDFileLogger+Buffering.h bufferProxy 拦截了这两个方法 logMessage 和 flush,其余的都通过 message forwarding 转回 filgLogger了。
会不断地将 data 写入 buffer,如果满了就写入 log file 并 flushBuffer。通过这种方式,有效的减小了文件写入的 I/O 操作。

6.在执行前需要进行 loggingQueue 和 loggerQueue 的检查 loggerQueue,logger本身的操作,由于日志打印均为异步操作,所以会为每个 logger 分配一个 dispatch_queue_t。如果 logger 未提供 loggerQueue,那么 DDLog 为根据你所指定的 loggerName 主动为你生成。

loggingQueue,ddlog初始化的时候创建,用于异步打印日志,logMessage。 _loggingQueue 全局的 log queue 用于保证 FIFO 的操作顺序,所有 logger 会通过它来顺序执行各 logger 的 logMessage: 。 _loggingGroup 由于每个 logger 添加时候都配置了对应的 log queue。因此,loggers 之间的记录行为是并发执行的。而 dispatch group 可以同步所有 loggers 的操作,确保记录行为顺利完成。 _queueSemaphore 防止所使用的队列过爆。由于大多数记录都是异步操作,因此,可能遭到恶意线程大量的增加 log 影响正常的记录行为。最大限制数为 DDLOG_MAX_QUEUE_SIZE (1000),也就是说当队列数超过限制,则会主动阻塞线程,以待执行队列降至安全水平。

例如:在大型循环中随意添加日志语句时会发生过💥。

7.@autoreleasepool的大量使用
这种用法可以避免在短时间内大量积攒autorelease对象导致内存暴涨,即@autoreleasepool中的内容在代码块结束后便会释放内存。而默认的autoreleasepool的释放时机会比较靠后。

8.线程死锁问题如下代码,dispatch_get_specific(GlobalLoggingQueueIdentityKey)的判断,针对同步执行代码,做了处理,避免在同一个队列中同步执行任务(导致死锁 )。类似于main thread 执行了 dispatch_sync 开启了 main queue 的同步等待

dispatch_block_t logBlock = ^{
        // We're now sure we won't overflow the queue.
        // It is time to queue our log message.
       @autoreleasepool {
            [self lt_log:logMessage];
        }
    };
    if (asyncFlag) {
        dispatch_async(_loggingQueue, logBlock);
    } else if(dispatch_get_specific(GlobalLoggingQueueIdentityKey)) {
        // We've logged an error message while on the logging queue...
        logBlock();
    } else {
        dispatch_sync(_loggingQueue, logBlock);
    }
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}

需要强调一点,logger在内部最好直接访问 FORMATTER VARIABLE ,如果需要的话。一旦使用 self. 可能会导致线程死锁。如下代码,如果self.logFormatter的方式访问,相当于sync的方式在loggerQueue队列中执行操作,类似上述8种的主线程死锁问题.

dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

__block id <DDLogFormatter> result;

dispatch_sync(globalLoggingQueue, ^{
    dispatch_sync(self->_loggerQueue, ^{
        result = self->_logFormatter;
    });
});
return result;