123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- // Software License Agreement (BSD License)
- //
- // Copyright (c) 2010-2015, 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];
- }
- }
- _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, self.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, 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;
- dispatch_sync(globalLoggingQueue, ^{
- dispatch_sync(self.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(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;
- 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 */ 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(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
|