728 lines
20 KiB
Objective-C
Executable File
728 lines
20 KiB
Objective-C
Executable File
#import "DDAbstractDatabaseLogger.h"
|
|
#import <math.h>
|
|
|
|
/**
|
|
* Welcome to Cocoa Lumberjack!
|
|
*
|
|
* The project page has a wealth of documentation if you have any questions.
|
|
* https://github.com/robbiehanson/CocoaLumberjack
|
|
*
|
|
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
|
* https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
|
|
**/
|
|
|
|
#if ! __has_feature(objc_arc)
|
|
#warning 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
|
|
|
|
- (id)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];
|
|
}
|
|
|
|
unsavedCount = 0;
|
|
unsavedTime = 0;
|
|
|
|
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)
|
|
{
|
|
dispatch_source_cancel(saveTimer);
|
|
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;
|
|
}
|
|
}
|
|
|
|
- (void)updateAndResumeSaveTimer
|
|
{
|
|
if ((saveTimer != NULL) && (saveInterval > 0.0) && (unsavedTime > 0.0))
|
|
{
|
|
uint64_t interval = (uint64_t)(saveInterval * NSEC_PER_SEC);
|
|
dispatch_time_t startTime = dispatch_time(unsavedTime, interval);
|
|
|
|
dispatch_source_set_timer(saveTimer, startTime, interval, 1.0);
|
|
|
|
if (saveTimerSuspended)
|
|
{
|
|
dispatch_resume(saveTimer);
|
|
saveTimerSuspended = NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)createSuspendedSaveTimer
|
|
{
|
|
if ((saveTimer == NULL) && (saveInterval > 0.0))
|
|
{
|
|
saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
|
|
|
|
dispatch_source_set_event_handler(saveTimer, ^{ @autoreleasepool {
|
|
|
|
[self performSaveAndSuspendSaveTimer];
|
|
|
|
}});
|
|
|
|
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 * 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, 1.0);
|
|
}
|
|
}
|
|
|
|
- (void)createAndStartDeleteTimer
|
|
{
|
|
if ((deleteTimer == NULL) && (deleteInterval > 0.0) && (maxAge > 0.0))
|
|
{
|
|
deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
|
|
|
|
if (deleteTimer != NULL) {
|
|
dispatch_source_set_event_handler(deleteTimer, ^{ @autoreleasepool {
|
|
|
|
[self performDelete];
|
|
|
|
}});
|
|
|
|
[self updateDeleteTimer];
|
|
|
|
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;
|
|
|
|
dispatch_sync(globalLoggingQueue, ^{
|
|
dispatch_sync(loggerQueue, ^{
|
|
result = saveThreshold;
|
|
});
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)setSaveThreshold:(NSUInteger)threshold
|
|
{
|
|
dispatch_block_t block = ^{ @autoreleasepool {
|
|
|
|
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
|
|
{
|
|
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
|
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
|
|
|
dispatch_async(globalLoggingQueue, ^{
|
|
dispatch_async(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;
|
|
|
|
dispatch_sync(globalLoggingQueue, ^{
|
|
dispatch_sync(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 */ islessgreater(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(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(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(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(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(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(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(loggerQueue, block);
|
|
});
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Public API
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)savePendingLogEntries
|
|
{
|
|
dispatch_block_t block = ^{ @autoreleasepool {
|
|
|
|
[self performSaveAndSuspendSaveTimer];
|
|
}};
|
|
|
|
if ([self isOnInternalLoggerQueue])
|
|
block();
|
|
else
|
|
dispatch_async(loggerQueue, block);
|
|
}
|
|
|
|
- (void)deleteOldLogEntries
|
|
{
|
|
dispatch_block_t block = ^{ @autoreleasepool {
|
|
|
|
[self performDelete];
|
|
}};
|
|
|
|
if ([self isOnInternalLoggerQueue])
|
|
block();
|
|
else
|
|
dispatch_async(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
|