2017-12-31


// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2016, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
//   to endorse or promote products derived from this software without specific
//   prior written permission of Deusty, LLC.

// Disable legacy macros
#ifndef DD_LEGACY_MACROS
    #define DD_LEGACY_MACROS 0
#endif

#import "DDLog.h"

/**
 * This class provides an abstract implementation of a database logger.
 *
 * That is, it provides the base implementation for a database logger to build atop of.
 * All that is needed for a concrete database logger is to extend this class
 * and override the methods in the implementation file that are prefixed with "db_".
 **/
@interface DDAbstractDatabaseLogger : DDAbstractLogger {
    
@protected
    NSUInteger _saveThreshold;
    NSTimeInterval _saveInterval;
    NSTimeInterval _maxAge;
    NSTimeInterval _deleteInterval;
    BOOL _deleteOnEverySave;
    
    BOOL _saveTimerSuspended;
    NSUInteger _unsavedCount;
    dispatch_time_t _unsavedTime;
    // 时间控制的
    dispatch_source_t _saveTimer;
    dispatch_time_t _lastDeleteTime;
    dispatch_source_t _deleteTimer;
}


// 漂亮,每多久记录一次的好方法
/**
 * Specifies how often to save the data to disk.
 * Since saving is an expensive operation (disk io) it is not done after every log statement.
 * These properties allow you to configure how/when the logger saves to disk.
 *
 * A save is done when either (whichever happens first):
 *
 * - The number of unsaved log entries reaches saveThreshold
 * - The amount of time since the oldest unsaved log entry was created reaches saveInterval
 *
 * You can optionally disable the saveThreshold by setting it to zero.
 * If you disable the saveThreshold you are entirely dependent on the saveInterval.
 *
 * You can optionally disable the saveInterval by setting it to zero (or a negative value).
 * If you disable the saveInterval you are entirely dependent on the saveThreshold.
 *
 * It's not wise to disable both saveThreshold and saveInterval.
 *
 * The default saveThreshold is 500.
 * The default saveInterval is 60 seconds.
 **/


// threshold 是临界值的意思,用来批量设置,棒棒的

@property (assign, readwrite) NSUInteger saveThreshold;

/**
 *  See the description for the `saveThreshold` property
 */
@property (assign, readwrite) NSTimeInterval saveInterval;

/**
 * It is likely you don't want the log entries to persist forever.
 * Doing so would allow the database to grow infinitely large over time.
 *
 * The maxAge property provides a way to specify how old a log statement can get
 * before it should get deleted from the database.
 *
 * The deleteInterval specifies how often to sweep for old log entries.
 * Since deleting is an expensive operation (disk io) is is done on a fixed interval.
 *
 * An alternative to the deleteInterval is the deleteOnEverySave option.
 * This specifies that old log entries should be deleted during every save operation.
 *
 * You can optionally disable the maxAge by setting it to zero (or a negative value).
 * If you disable the maxAge then old log statements are not deleted.
 *
 * You can optionally disable the deleteInterval by setting it to zero (or a negative value).
 *
 * If you disable both deleteInterval and deleteOnEverySave then old log statements are not deleted.
 *
 * It's not wise to enable both deleteInterval and deleteOnEverySave.
 *
 * The default maxAge is 7 days.
 * The default deleteInterval is 5 minutes.
 * The default deleteOnEverySave is NO.
 **/
@property (assign, readwrite) NSTimeInterval maxAge;

/**
 *  See the description for the `maxAge` property
 */
@property (assign, readwrite) NSTimeInterval deleteInterval;

/**
 *  See the description for the `maxAge` property
 */
@property (assign, readwrite) BOOL deleteOnEverySave;

/**
 * Forces a save of any pending log entries (flushes log entries to disk).
 **/
- (void)savePendingLogEntries;

/**
 * Removes any log entries that are older than maxAge.
 **/
- (void)deleteOldLogEntries;

@end




// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2016, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
//   to endorse or promote products derived from this software without specific
//   prior written permission of Deusty, LLC.

#import "DDAbstractDatabaseLogger.h"
#import <math.h>


#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif

@interface DDAbstractDatabaseLogger ()

- (void)destroySaveTimer;
- (void)destroyDeleteTimer;

@end

#pragma mark -

@implementation DDAbstractDatabaseLogger

- (instancetype)init {
    if ((self = [super init])) {
        _saveThreshold = 500;
        _saveInterval = 60;           // 60 seconds
        _maxAge = (60 * 60 * 24 * 7); //  7 days
        _deleteInterval = (60 * 5);   //  5 minutes
    }

    return self;
}

- (void)dealloc {
    [self destroySaveTimer];
    [self destroyDeleteTimer];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Override Me
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)db_log:(DDLogMessage *)logMessage {
    // Override me and add your implementation.
    //
    // Return YES if an item was added to the buffer.
    // Return NO if the logMessage was ignored.

    return NO;
}

- (void)db_save {
    // Override me and add your implementation.
}

- (void)db_delete {
    // Override me and add your implementation.
}

- (void)db_saveAndDelete {
    // Override me and add your implementation.
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Private API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 用来执行保存操作
- (void)performSaveAndSuspendSaveTimer {
    //保存了
    if (_unsavedCount > 0) {
        if (_deleteOnEverySave) {
            [self db_saveAndDelete];
        } else {
            [self db_save];
        }
    }
    // 将没保存设置为0
    // 将没保存的数量设置为0
    _unsavedCount = 0;
    _unsavedTime = 0;

    
    // 如果_有savetime这个线程,并且线程没有被挂起
    // 就把这个线程挂起
    if (_saveTimer && !_saveTimerSuspended) {
        dispatch_suspend(_saveTimer);
        _saveTimerSuspended = YES;
    }
}

// 删除数据
- (void)performDelete {
    if (_maxAge > 0.0) {
        [self db_delete];

        _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Timers
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)destroySaveTimer {
    if (_saveTimer) {
        
        // 用来cancel 线程
        dispatch_source_cancel(_saveTimer);
        // 将_save 重置
        if (_saveTimerSuspended) {
            // Must resume a timer before releasing it (or it will crash)
            dispatch_resume(_saveTimer);
            _saveTimerSuspended = NO;
        }

        #if !OS_OBJECT_USE_OBJC
        dispatch_release(_saveTimer);
        #endif
        _saveTimer = NULL;
    }
}

// 更新timer 的正确方法
- (void)updateAndResumeSaveTimer {
    if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0.0)) {
        
        // 使用timeinterval 的正确方法
        uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
        // 设置每一次的savetime 为从unsavedtime 开始每隔interval save 一次
        dispatch_time_t startTime = dispatch_time(_unsavedTime, interval);

        // 使用dispatch_source 开始了
        dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC);
        
        // 开始save timer 这个source
        if (_saveTimerSuspended) {
            dispatch_resume(_saveTimer);
            _saveTimerSuspended = NO;
        }
    }
}

- (void)createSuspendedSaveTimer {
    
    //  初始化sourcetimer
    if ((_saveTimer == NULL) && (_saveInterval > 0.0)) {
        _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
        // 漂亮, 什么都不用多写
        dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
                                                            [self performSaveAndSuspendSaveTimer];
                                                        } });
        // 使用savetimer suspend 来 做标志位
        _saveTimerSuspended = YES;
    }
}

- (void)destroyDeleteTimer {
    if (_deleteTimer) {
        dispatch_source_cancel(_deleteTimer);
        #if !OS_OBJECT_USE_OBJC
        dispatch_release(_deleteTimer);
        #endif
        _deleteTimer = NULL;
    }
}

- (void)updateDeleteTimer {
    if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
        uint64_t interval = (uint64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
        dispatch_time_t startTime;
        // 我去标准化流程 😂
        if (_lastDeleteTime > 0) {
            startTime = dispatch_time(_lastDeleteTime, interval);
        } else {
            startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
        }

        dispatch_source_set_timer(_deleteTimer, startTime, interval, 1ull * NSEC_PER_SEC);
    }
}

- (void)createAndStartDeleteTimer {
    if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
        _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);

        if (_deleteTimer != NULL) {
            dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
                                                                  [self performDelete];
                                                              } });

            [self updateDeleteTimer];

            if (_deleteTimer != NULL) {
                dispatch_resume(_deleteTimer);
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Configuration
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSUInteger)saveThreshold {
    // The design of this method is taken from the DDAbstractLogger implementation.
    // For extensive documentation please refer to the DDAbstractLogger implementation.

    // Note: The internal implementation MUST access the colorsEnabled variable directly,
    // This method is designed explicitly for external access.
    //
    // Using "self." syntax to go through this method will cause immediate deadlock.
    // This is the intended result. Fix it by accessing the ivar directly.
    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.

    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
    // 来吧 耦合吧
    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

    __block NSUInteger result;

    // 设置都是async, sync是用来读取的,
    // 然后再使用当前的logger queue
    dispatch_sync(globalLoggingQueue, ^{
        dispatch_sync(self.loggerQueue, ^{
            result = _saveThreshold;
        });
    });

    return result;
}

- (void)setSaveThreshold:(NSUInteger)threshold {
    dispatch_block_t block = ^{
        @autoreleasepool {
            // 这里有个细节
            // 先更改save的数值,在使用保存函数
            if (_saveThreshold != threshold) {
                _saveThreshold = threshold;

                // Since the saveThreshold has changed,
                // we check to see if the current unsavedCount has surpassed the new threshold.
                //
                // If it has, we immediately save the log.

                if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
                    [self performSaveAndSuspendSaveTimer];
                }
            }
        }
    };

    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
    // For documentation please refer to the DDAbstractLogger implementation.

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        // 漂亮,强制使用 queue 检查
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
        // 这里所有的操作都在大的loggerqueue中,而且,小的async, 大的也是async
        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}

- (NSTimeInterval)saveInterval {
    // The design of this method is taken from the DDAbstractLogger implementation.
    // For extensive documentation please refer to the DDAbstractLogger implementation.

    // Note: The internal implementation MUST access the colorsEnabled variable directly,
    // This method is designed explicitly for external access.
    //
    // Using "self." syntax to go through this method will cause immediate deadlock.
    // This is the intended result. Fix it by accessing the ivar directly.
    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.

    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");

    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

    __block NSTimeInterval result;
    // 只要是设置,就使用sync
    dispatch_sync(globalLoggingQueue, ^{
        dispatch_sync(self.loggerQueue, ^{
            result = _saveInterval;
        });
    });

    return result;
}

- (void)setSaveInterval:(NSTimeInterval)interval {
    dispatch_block_t block = ^{
        @autoreleasepool {
            // C99 recommended floating point comparison macro
            // Read: isLessThanOrGreaterThan(floatA, floatB)
            // 最佳实践
            if (/* saveInterval != interval */ isle99issgreater(_saveInterval, interval)) {
                _saveInterval = interval;

                // There are several cases we need to handle here.
                //
                // 1. If the saveInterval was previously enabled and it just got disabled,
                //    then we need to stop the saveTimer. (And we might as well release it.)
                //
                // 2. If the saveInterval was previously disabled and it just got enabled,
                //    then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
                //
                // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
                //
                // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
                //    (Plus we might need to do an immediate save.)

                if (_saveInterval > 0.0) {
                    if (_saveTimer == NULL) {
                        // Handles #2
                        //
                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
                        // if a save is needed the timer will fire immediately.

                        [self createSuspendedSaveTimer];
                        [self updateAndResumeSaveTimer];
                    } else {
                        // Handles #3
                        // Handles #4
                        //
                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
                        // if a save is needed the timer will fire immediately.

                        [self updateAndResumeSaveTimer];
                    }
                } else if (_saveTimer) {
                    // Handles #1

                    [self destroySaveTimer];
                }
            }
        }
    };

    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
    // For documentation please refer to the DDAbstractLogger implementation.

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");

        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}

- (NSTimeInterval)maxAge {
    // The design of this method is taken from the DDAbstractLogger implementation.
    // For extensive documentation please refer to the DDAbstractLogger implementation.

    // Note: The internal implementation MUST access the colorsEnabled variable directly,
    // This method is designed explicitly for external access.
    //
    // Using "self." syntax to go through this method will cause immediate deadlock.
    // This is the intended result. Fix it by accessing the ivar directly.
    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.

    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");

    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

    __block NSTimeInterval result;

    dispatch_sync(globalLoggingQueue, ^{
        dispatch_sync(self.loggerQueue, ^{
            result = _maxAge;
        });
    });

    return result;
}

- (void)setMaxAge:(NSTimeInterval)interval {
    dispatch_block_t block = ^{
        @autoreleasepool {
            // C99 recommended floating point comparison macro
            // Read: isLessThanOrGreaterThan(floatA, floatB)

            if (/* maxAge != interval */ islessgreater(_maxAge, interval)) {
                NSTimeInterval oldMaxAge = _maxAge;
                NSTimeInterval newMaxAge = interval;

                _maxAge = interval;

                // There are several cases we need to handle here.
                //
                // 1. If the maxAge was previously enabled and it just got disabled,
                //    then we need to stop the deleteTimer. (And we might as well release it.)
                //
                // 2. If the maxAge was previously disabled and it just got enabled,
                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
                //
                // 3. If the maxAge was increased,
                //    then we don't need to do anything.
                //
                // 4. If the maxAge was decreased,
                //    then we should do an immediate delete.

                BOOL shouldDeleteNow = NO;

                if (oldMaxAge > 0.0) {
                    if (newMaxAge <= 0.0) {
                        // Handles #1

                        [self destroyDeleteTimer];
                    } else if (oldMaxAge > newMaxAge) {
                        // Handles #4
                        shouldDeleteNow = YES;
                    }
                } else if (newMaxAge > 0.0) {
                    // Handles #2
                    shouldDeleteNow = YES;
                }

                if (shouldDeleteNow) {
                    [self performDelete];

                    if (_deleteTimer) {
                        [self updateDeleteTimer];
                    } else {
                        [self createAndStartDeleteTimer];
                    }
                }
            }
        }
    };

    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
    // For documentation please refer to the DDAbstractLogger implementation.

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");

        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}

- (NSTimeInterval)deleteInterval {
    // The design of this method is taken from the DDAbstractLogger implementation.
    // For extensive documentation please refer to the DDAbstractLogger implementation.

    // Note: The internal implementation MUST access the colorsEnabled variable directly,
    // This method is designed explicitly for external access.
    //
    // Using "self." syntax to go through this method will cause immediate deadlock.
    // This is the intended result. Fix it by accessing the ivar directly.
    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.

    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");

    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

    __block NSTimeInterval result;

    dispatch_sync(globalLoggingQueue, ^{
        dispatch_sync(self.loggerQueue, ^{
            result = _deleteInterval;
        });
    });

    return result;
}

- (void)setDeleteInterval:(NSTimeInterval)interval {
    dispatch_block_t block = ^{
        @autoreleasepool {
            // C99 recommended floating point comparison macro
            // Read: isLessThanOrGreaterThan(floatA, floatB)

            if (/* deleteInterval != interval */ islessgreater(_deleteInterval, interval)) {
                _deleteInterval = interval;

                // There are several cases we need to handle here.
                //
                // 1. If the deleteInterval was previously enabled and it just got disabled,
                //    then we need to stop the deleteTimer. (And we might as well release it.)
                //
                // 2. If the deleteInterval was previously disabled and it just got enabled,
                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
                //
                // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
                //
                // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
                //    (Plus we might need to do an immediate delete.)

                if (_deleteInterval > 0.0) {
                    if (_deleteTimer == NULL) {
                        // Handles #2
                        //
                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
                        // if a delete is needed the timer will fire immediately.

                        [self createAndStartDeleteTimer];
                    } else {
                        // Handles #3
                        // Handles #4
                        //
                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
                        // if a save is needed the timer will fire immediately.

                        [self updateDeleteTimer];
                    }
                } else if (_deleteTimer) {
                    // Handles #1

                    [self destroyDeleteTimer];
                }
            }
        }
    };

    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
    // For documentation please refer to the DDAbstractLogger implementation.

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");

        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}

- (BOOL)deleteOnEverySave {
    // The design of this method is taken from the DDAbstractLogger implementation.
    // For extensive documentation please refer to the DDAbstractLogger implementation.

    // Note: The internal implementation MUST access the colorsEnabled variable directly,
    // This method is designed explicitly for external access.
    //
    // Using "self." syntax to go through this method will cause immediate deadlock.
    // This is the intended result. Fix it by accessing the ivar directly.
    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.

    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");

    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];

    __block BOOL result;

    dispatch_sync(globalLoggingQueue, ^{
        dispatch_sync(self.loggerQueue, ^{
            result = _deleteOnEverySave;
        });
    });

    return result;
}

- (void)setDeleteOnEverySave:(BOOL)flag {
    dispatch_block_t block = ^{
        _deleteOnEverySave = flag;
    };

    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
    // For documentation please refer to the DDAbstractLogger implementation.

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");

        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Public API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)savePendingLogEntries {
    dispatch_block_t block = ^{
        @autoreleasepool {
            [self performSaveAndSuspendSaveTimer];
        }
    };

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_async(self.loggerQueue, block);
    }
}

- (void)deleteOldLogEntries {
    dispatch_block_t block = ^{
        @autoreleasepool {
            [self performDelete];
        }
    };

    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_async(self.loggerQueue, block);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark DDLogger
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)didAddLogger {
    // If you override me be sure to invoke [super didAddLogger];

    [self createSuspendedSaveTimer];

    [self createAndStartDeleteTimer];
}

- (void)willRemoveLogger {
    // If you override me be sure to invoke [super willRemoveLogger];

    [self performSaveAndSuspendSaveTimer];

    [self destroySaveTimer];
    [self destroyDeleteTimer];
}

- (void)logMessage:(DDLogMessage *)logMessage {
    if ([self db_log:logMessage]) {
        BOOL firstUnsavedEntry = (++_unsavedCount == 1);

        if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
            [self performSaveAndSuspendSaveTimer];
        } else if (firstUnsavedEntry) {
            _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
            [self updateAndResumeSaveTimer];
        }
    }
}

- (void)flush {
    // This method is invoked by DDLog's flushLog method.
    //
    // It is called automatically when the application quits,
    // or if the developer invokes DDLog's flushLog method prior to crashing or something.

    [self performSaveAndSuspendSaveTimer];
}

@end


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

推荐阅读更多精彩内容