Subversion Repositories Mobile Apps.GyroMouse

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. #import "DDAbstractDatabaseLogger.h"
  2. #import <math.h>
  3.  
  4. /**
  5.  * Welcome to Cocoa Lumberjack!
  6.  *
  7.  * The project page has a wealth of documentation if you have any questions.
  8.  * https://github.com/CocoaLumberjack/CocoaLumberjack
  9.  *
  10.  * If you're new to the project you may wish to read the "Getting Started" wiki.
  11.  * https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
  12. **/
  13.  
  14. #if ! __has_feature(objc_arc)
  15. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  16. #endif
  17.  
  18. @interface DDAbstractDatabaseLogger ()
  19. - (void)destroySaveTimer;
  20. - (void)destroyDeleteTimer;
  21. @end
  22.  
  23. #pragma mark -
  24.  
  25. @implementation DDAbstractDatabaseLogger
  26.  
  27. - (id)init
  28. {
  29.     if ((self = [super init]))
  30.     {
  31.         saveThreshold = 500;
  32.         saveInterval = 60;           // 60 seconds
  33.         maxAge = (60 * 60 * 24 * 7); //  7 days
  34.         deleteInterval = (60 * 5);   //  5 minutes
  35.     }
  36.     return self;
  37. }
  38.  
  39. - (void)dealloc
  40. {
  41.     [self destroySaveTimer];
  42.     [self destroyDeleteTimer];
  43.    
  44. }
  45.  
  46. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  47. #pragma mark Override Me
  48. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  49.  
  50. - (BOOL)db_log:(DDLogMessage *)logMessage
  51. {
  52.     // Override me and add your implementation.
  53.     //
  54.     // Return YES if an item was added to the buffer.
  55.     // Return NO if the logMessage was ignored.
  56.    
  57.     return NO;
  58. }
  59.  
  60. - (void)db_save
  61. {
  62.     // Override me and add your implementation.
  63. }
  64.  
  65. - (void)db_delete
  66. {
  67.     // Override me and add your implementation.
  68. }
  69.  
  70. - (void)db_saveAndDelete
  71. {
  72.     // Override me and add your implementation.
  73. }
  74.  
  75. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  76. #pragma mark Private API
  77. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  78.  
  79. - (void)performSaveAndSuspendSaveTimer
  80. {
  81.     if (unsavedCount > 0)
  82.     {
  83.         if (deleteOnEverySave)
  84.             [self db_saveAndDelete];
  85.         else
  86.             [self db_save];
  87.     }
  88.    
  89.     unsavedCount = 0;
  90.     unsavedTime = 0;
  91.    
  92.     if (saveTimer && !saveTimerSuspended)
  93.     {
  94.         dispatch_suspend(saveTimer);
  95.         saveTimerSuspended = YES;
  96.     }
  97. }
  98.  
  99. - (void)performDelete
  100. {
  101.     if (maxAge > 0.0)
  102.     {
  103.         [self db_delete];
  104.        
  105.         lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
  106.     }
  107. }
  108.  
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. #pragma mark Timers
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112.  
  113. - (void)destroySaveTimer
  114. {
  115.     if (saveTimer)
  116.     {
  117.         dispatch_source_cancel(saveTimer);
  118.         if (saveTimerSuspended)
  119.         {
  120.             // Must resume a timer before releasing it (or it will crash)
  121.             dispatch_resume(saveTimer);
  122.             saveTimerSuspended = NO;
  123.         }
  124.         #if !OS_OBJECT_USE_OBJC
  125.         dispatch_release(saveTimer);
  126.         #endif
  127.         saveTimer = NULL;
  128.     }
  129. }
  130.  
  131. - (void)updateAndResumeSaveTimer
  132. {
  133.     if ((saveTimer != NULL) && (saveInterval > 0.0) && (unsavedTime > 0.0))
  134.     {
  135.         uint64_t interval = (uint64_t)(saveInterval * NSEC_PER_SEC);
  136.         dispatch_time_t startTime = dispatch_time(unsavedTime, interval);
  137.        
  138.         dispatch_source_set_timer(saveTimer, startTime, interval, 1.0);
  139.        
  140.         if (saveTimerSuspended)
  141.         {
  142.             dispatch_resume(saveTimer);
  143.             saveTimerSuspended = NO;
  144.         }
  145.     }
  146. }
  147.  
  148. - (void)createSuspendedSaveTimer
  149. {
  150.     if ((saveTimer == NULL) && (saveInterval > 0.0))
  151.     {
  152.         saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
  153.        
  154.         dispatch_source_set_event_handler(saveTimer, ^{ @autoreleasepool {
  155.            
  156.             [self performSaveAndSuspendSaveTimer];
  157.            
  158.         }});
  159.        
  160.         saveTimerSuspended = YES;
  161.     }
  162. }
  163.  
  164. - (void)destroyDeleteTimer
  165. {
  166.     if (deleteTimer)
  167.     {
  168.         dispatch_source_cancel(deleteTimer);
  169.         #if !OS_OBJECT_USE_OBJC
  170.         dispatch_release(deleteTimer);
  171.         #endif
  172.         deleteTimer = NULL;
  173.     }
  174. }
  175.  
  176. - (void)updateDeleteTimer
  177. {
  178.     if ((deleteTimer != NULL) && (deleteInterval > 0.0) && (maxAge > 0.0))
  179.     {
  180.         uint64_t interval = (uint64_t)(deleteInterval * NSEC_PER_SEC);
  181.         dispatch_time_t startTime;
  182.        
  183.         if (lastDeleteTime > 0)
  184.             startTime = dispatch_time(lastDeleteTime, interval);
  185.         else
  186.             startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
  187.        
  188.         dispatch_source_set_timer(deleteTimer, startTime, interval, 1.0);
  189.     }
  190. }
  191.  
  192. - (void)createAndStartDeleteTimer
  193. {
  194.     if ((deleteTimer == NULL) && (deleteInterval > 0.0) && (maxAge > 0.0))
  195.     {
  196.         deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
  197.  
  198.         if (deleteTimer != NULL) {
  199.             dispatch_source_set_event_handler(deleteTimer, ^{ @autoreleasepool {
  200.  
  201.                 [self performDelete];
  202.  
  203.             }});
  204.  
  205.             [self updateDeleteTimer];
  206.            
  207.             if (deleteTimer != NULL) dispatch_resume(deleteTimer);
  208.         }
  209.     }
  210. }
  211.  
  212. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  213. #pragma mark Configuration
  214. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215.  
  216. - (NSUInteger)saveThreshold
  217. {
  218.     // The design of this method is taken from the DDAbstractLogger implementation.
  219.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  220.    
  221.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  222.     // This method is designed explicitly for external access.
  223.     //
  224.     // Using "self." syntax to go through this method will cause immediate deadlock.
  225.     // This is the intended result. Fix it by accessing the ivar directly.
  226.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  227.    
  228.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  229.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  230.    
  231.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  232.    
  233.     __block NSUInteger result;
  234.    
  235.     dispatch_sync(globalLoggingQueue, ^{
  236.         dispatch_sync(loggerQueue, ^{
  237.             result = saveThreshold;
  238.         });
  239.     });
  240.    
  241.     return result;
  242. }
  243.  
  244. - (void)setSaveThreshold:(NSUInteger)threshold
  245. {
  246.     dispatch_block_t block = ^{ @autoreleasepool {
  247.        
  248.         if (saveThreshold != threshold)
  249.         {
  250.             saveThreshold = threshold;
  251.            
  252.             // Since the saveThreshold has changed,
  253.             // we check to see if the current unsavedCount has surpassed the new threshold.
  254.             //
  255.             // If it has, we immediately save the log.
  256.            
  257.             if ((unsavedCount >= saveThreshold) && (saveThreshold > 0))
  258.             {
  259.                 [self performSaveAndSuspendSaveTimer];
  260.             }
  261.         }
  262.     }};
  263.    
  264.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  265.     // For documentation please refer to the DDAbstractLogger implementation.
  266.    
  267.     if ([self isOnInternalLoggerQueue])
  268.     {
  269.         block();
  270.     }
  271.     else
  272.     {
  273.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  274.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  275.        
  276.         dispatch_async(globalLoggingQueue, ^{
  277.             dispatch_async(loggerQueue, block);
  278.         });
  279.     }
  280. }
  281.  
  282. - (NSTimeInterval)saveInterval
  283. {
  284.     // The design of this method is taken from the DDAbstractLogger implementation.
  285.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  286.    
  287.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  288.     // This method is designed explicitly for external access.
  289.     //
  290.     // Using "self." syntax to go through this method will cause immediate deadlock.
  291.     // This is the intended result. Fix it by accessing the ivar directly.
  292.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  293.    
  294.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  295.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  296.    
  297.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  298.    
  299.     __block NSTimeInterval result;
  300.    
  301.     dispatch_sync(globalLoggingQueue, ^{
  302.         dispatch_sync(loggerQueue, ^{
  303.             result = saveInterval;
  304.         });
  305.     });
  306.    
  307.     return result;
  308. }
  309.  
  310. - (void)setSaveInterval:(NSTimeInterval)interval
  311. {
  312.     dispatch_block_t block = ^{ @autoreleasepool {
  313.    
  314.         // C99 recommended floating point comparison macro
  315.         // Read: isLessThanOrGreaterThan(floatA, floatB)
  316.        
  317.         if (/* saveInterval != interval */ islessgreater(saveInterval, interval))
  318.         {
  319.             saveInterval = interval;
  320.            
  321.             // There are several cases we need to handle here.
  322.             //
  323.             // 1. If the saveInterval was previously enabled and it just got disabled,
  324.             //    then we need to stop the saveTimer. (And we might as well release it.)
  325.             //
  326.             // 2. If the saveInterval was previously disabled and it just got enabled,
  327.             //    then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
  328.             //
  329.             // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
  330.             //
  331.             // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
  332.             //    (Plus we might need to do an immediate save.)
  333.            
  334.             if (saveInterval > 0.0)
  335.             {
  336.                 if (saveTimer == NULL)
  337.                 {
  338.                     // Handles #2
  339.                     //
  340.                     // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
  341.                     // if a save is needed the timer will fire immediately.
  342.                    
  343.                     [self createSuspendedSaveTimer];
  344.                     [self updateAndResumeSaveTimer];
  345.                 }
  346.                 else
  347.                 {
  348.                     // Handles #3
  349.                     // Handles #4
  350.                     //
  351.                     // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
  352.                     // if a save is needed the timer will fire immediately.
  353.                    
  354.                     [self updateAndResumeSaveTimer];
  355.                 }
  356.             }
  357.             else if (saveTimer)
  358.             {
  359.                 // Handles #1
  360.                
  361.                 [self destroySaveTimer];
  362.             }
  363.         }
  364.     }};
  365.    
  366.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  367.     // For documentation please refer to the DDAbstractLogger implementation.
  368.    
  369.     if ([self isOnInternalLoggerQueue])
  370.     {
  371.         block();
  372.     }
  373.     else
  374.     {
  375.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  376.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  377.        
  378.         dispatch_async(globalLoggingQueue, ^{
  379.             dispatch_async(loggerQueue, block);
  380.         });
  381.     }
  382. }
  383.  
  384. - (NSTimeInterval)maxAge
  385. {
  386.     // The design of this method is taken from the DDAbstractLogger implementation.
  387.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  388.    
  389.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  390.     // This method is designed explicitly for external access.
  391.     //
  392.     // Using "self." syntax to go through this method will cause immediate deadlock.
  393.     // This is the intended result. Fix it by accessing the ivar directly.
  394.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  395.    
  396.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  397.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  398.    
  399.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  400.    
  401.     __block NSTimeInterval result;
  402.    
  403.     dispatch_sync(globalLoggingQueue, ^{
  404.         dispatch_sync(loggerQueue, ^{
  405.             result = maxAge;
  406.         });
  407.     });
  408.    
  409.     return result;
  410. }
  411.  
  412. - (void)setMaxAge:(NSTimeInterval)interval
  413. {
  414.     dispatch_block_t block = ^{ @autoreleasepool {
  415.        
  416.         // C99 recommended floating point comparison macro
  417.         // Read: isLessThanOrGreaterThan(floatA, floatB)
  418.        
  419.         if (/* maxAge != interval */ islessgreater(maxAge, interval))
  420.         {
  421.             NSTimeInterval oldMaxAge = maxAge;
  422.             NSTimeInterval newMaxAge = interval;
  423.            
  424.             maxAge = interval;
  425.            
  426.             // There are several cases we need to handle here.
  427.             //
  428.             // 1. If the maxAge was previously enabled and it just got disabled,
  429.             //    then we need to stop the deleteTimer. (And we might as well release it.)
  430.             //
  431.             // 2. If the maxAge was previously disabled and it just got enabled,
  432.             //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
  433.             //
  434.             // 3. If the maxAge was increased,
  435.             //    then we don't need to do anything.
  436.             //
  437.             // 4. If the maxAge was decreased,
  438.             //    then we should do an immediate delete.
  439.            
  440.             BOOL shouldDeleteNow = NO;
  441.            
  442.             if (oldMaxAge > 0.0)
  443.             {
  444.                 if (newMaxAge <= 0.0)
  445.                 {
  446.                     // Handles #1
  447.                    
  448.                     [self destroyDeleteTimer];
  449.                 }
  450.                 else if (oldMaxAge > newMaxAge)
  451.                 {
  452.                     // Handles #4
  453.                     shouldDeleteNow = YES;
  454.                 }
  455.             }
  456.             else if (newMaxAge > 0.0)
  457.             {
  458.                 // Handles #2
  459.                 shouldDeleteNow = YES;
  460.             }
  461.            
  462.             if (shouldDeleteNow)
  463.             {
  464.                 [self performDelete];
  465.                
  466.                 if (deleteTimer)
  467.                     [self updateDeleteTimer];
  468.                 else
  469.                     [self createAndStartDeleteTimer];
  470.             }
  471.         }
  472.     }};
  473.    
  474.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  475.     // For documentation please refer to the DDAbstractLogger implementation.
  476.    
  477.     if ([self isOnInternalLoggerQueue])
  478.     {
  479.         block();
  480.     }
  481.     else
  482.     {
  483.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  484.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  485.        
  486.         dispatch_async(globalLoggingQueue, ^{
  487.             dispatch_async(loggerQueue, block);
  488.         });
  489.     }
  490. }
  491.  
  492. - (NSTimeInterval)deleteInterval
  493. {
  494.     // The design of this method is taken from the DDAbstractLogger implementation.
  495.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  496.    
  497.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  498.     // This method is designed explicitly for external access.
  499.     //
  500.     // Using "self." syntax to go through this method will cause immediate deadlock.
  501.     // This is the intended result. Fix it by accessing the ivar directly.
  502.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  503.    
  504.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  505.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  506.    
  507.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  508.    
  509.     __block NSTimeInterval result;
  510.    
  511.     dispatch_sync(globalLoggingQueue, ^{
  512.         dispatch_sync(loggerQueue, ^{
  513.             result = deleteInterval;
  514.         });
  515.     });
  516.    
  517.     return result;
  518. }
  519.  
  520. - (void)setDeleteInterval:(NSTimeInterval)interval
  521. {
  522.     dispatch_block_t block = ^{ @autoreleasepool {
  523.        
  524.         // C99 recommended floating point comparison macro
  525.         // Read: isLessThanOrGreaterThan(floatA, floatB)
  526.        
  527.         if (/* deleteInterval != interval */ islessgreater(deleteInterval, interval))
  528.         {
  529.             deleteInterval = interval;
  530.            
  531.             // There are several cases we need to handle here.
  532.             //
  533.             // 1. If the deleteInterval was previously enabled and it just got disabled,
  534.             //    then we need to stop the deleteTimer. (And we might as well release it.)
  535.             //
  536.             // 2. If the deleteInterval was previously disabled and it just got enabled,
  537.             //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
  538.             //
  539.             // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
  540.             //
  541.             // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
  542.             //    (Plus we might need to do an immediate delete.)
  543.            
  544.             if (deleteInterval > 0.0)
  545.             {
  546.                 if (deleteTimer == NULL)
  547.                 {
  548.                     // Handles #2
  549.                     //
  550.                     // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
  551.                     // if a delete is needed the timer will fire immediately.
  552.                    
  553.                     [self createAndStartDeleteTimer];
  554.                 }
  555.                 else
  556.                 {
  557.                     // Handles #3
  558.                     // Handles #4
  559.                     //
  560.                     // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
  561.                     // if a save is needed the timer will fire immediately.
  562.                    
  563.                     [self updateDeleteTimer];
  564.                 }
  565.             }
  566.             else if (deleteTimer)
  567.             {
  568.                 // Handles #1
  569.                
  570.                 [self destroyDeleteTimer];
  571.             }
  572.         }
  573.     }};
  574.    
  575.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  576.     // For documentation please refer to the DDAbstractLogger implementation.
  577.    
  578.     if ([self isOnInternalLoggerQueue])
  579.     {
  580.         block();
  581.     }
  582.     else
  583.     {
  584.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  585.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  586.        
  587.         dispatch_async(globalLoggingQueue, ^{
  588.             dispatch_async(loggerQueue, block);
  589.         });
  590.     }
  591. }
  592.  
  593. - (BOOL)deleteOnEverySave
  594. {
  595.     // The design of this method is taken from the DDAbstractLogger implementation.
  596.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  597.    
  598.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  599.     // This method is designed explicitly for external access.
  600.     //
  601.     // Using "self." syntax to go through this method will cause immediate deadlock.
  602.     // This is the intended result. Fix it by accessing the ivar directly.
  603.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  604.    
  605.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  606.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  607.    
  608.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  609.    
  610.     __block BOOL result;
  611.    
  612.     dispatch_sync(globalLoggingQueue, ^{
  613.         dispatch_sync(loggerQueue, ^{
  614.             result = deleteOnEverySave;
  615.         });
  616.     });
  617.    
  618.     return result;
  619. }
  620.  
  621. - (void)setDeleteOnEverySave:(BOOL)flag
  622. {
  623.     dispatch_block_t block = ^{
  624.        
  625.         deleteOnEverySave = flag;
  626.     };
  627.    
  628.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  629.     // For documentation please refer to the DDAbstractLogger implementation.
  630.    
  631.     if ([self isOnInternalLoggerQueue])
  632.     {
  633.         block();
  634.     }
  635.     else
  636.     {
  637.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  638.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  639.        
  640.         dispatch_async(globalLoggingQueue, ^{
  641.             dispatch_async(loggerQueue, block);
  642.         });
  643.     }
  644. }
  645.  
  646. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  647. #pragma mark Public API
  648. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  649.  
  650. - (void)savePendingLogEntries
  651. {
  652.     dispatch_block_t block = ^{ @autoreleasepool {
  653.        
  654.         [self performSaveAndSuspendSaveTimer];
  655.     }};
  656.    
  657.     if ([self isOnInternalLoggerQueue])
  658.         block();
  659.     else
  660.         dispatch_async(loggerQueue, block);
  661. }
  662.  
  663. - (void)deleteOldLogEntries
  664. {
  665.     dispatch_block_t block = ^{ @autoreleasepool {
  666.        
  667.         [self performDelete];
  668.     }};
  669.    
  670.     if ([self isOnInternalLoggerQueue])
  671.         block();
  672.     else
  673.         dispatch_async(loggerQueue, block);
  674. }
  675.  
  676. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  677. #pragma mark DDLogger
  678. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  679.  
  680. - (void)didAddLogger
  681. {
  682.     // If you override me be sure to invoke [super didAddLogger];
  683.    
  684.     [self createSuspendedSaveTimer];
  685.    
  686.     [self createAndStartDeleteTimer];
  687. }
  688.  
  689. - (void)willRemoveLogger
  690. {
  691.     // If you override me be sure to invoke [super willRemoveLogger];
  692.    
  693.     [self performSaveAndSuspendSaveTimer];
  694.    
  695.     [self destroySaveTimer];
  696.     [self destroyDeleteTimer];
  697. }
  698.  
  699. - (void)logMessage:(DDLogMessage *)logMessage
  700. {
  701.     if ([self db_log:logMessage])
  702.     {
  703.         BOOL firstUnsavedEntry = (++unsavedCount == 1);
  704.        
  705.         if ((unsavedCount >= saveThreshold) && (saveThreshold > 0))
  706.         {
  707.             [self performSaveAndSuspendSaveTimer];
  708.         }
  709.         else if (firstUnsavedEntry)
  710.         {
  711.             unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
  712.             [self updateAndResumeSaveTimer];
  713.         }
  714.     }
  715. }
  716.  
  717. - (void)flush
  718. {
  719.     // This method is invoked by DDLog's flushLog method.
  720.     //
  721.     // It is called automatically when the application quits,
  722.     // or if the developer invokes DDLog's flushLog method prior to crashing or something.
  723.    
  724.     [self performSaveAndSuspendSaveTimer];
  725. }
  726.  
  727. @end
  728.