DDAbstractDatabaseLogger.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. // Software License Agreement (BSD License)
  2. //
  3. // Copyright (c) 2010-2021, Deusty, LLC
  4. // All rights reserved.
  5. //
  6. // Redistribution and use of this software in source and binary forms,
  7. // with or without modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. //
  12. // * Neither the name of Deusty nor the names of its contributors may be used
  13. // to endorse or promote products derived from this software without specific
  14. // prior written permission of Deusty, LLC.
  15. #if !__has_feature(objc_arc)
  16. #error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  17. #endif
  18. #import <CocoaLumberjack/DDAbstractDatabaseLogger.h>
  19. @interface DDAbstractDatabaseLogger ()
  20. - (void)destroySaveTimer;
  21. - (void)updateAndResumeSaveTimer;
  22. - (void)createSuspendedSaveTimer;
  23. - (void)destroyDeleteTimer;
  24. - (void)updateDeleteTimer;
  25. - (void)createAndStartDeleteTimer;
  26. @end
  27. #pragma mark -
  28. @implementation DDAbstractDatabaseLogger
  29. - (instancetype)init {
  30. if ((self = [super init])) {
  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. - (void)dealloc {
  39. [self destroySaveTimer];
  40. [self destroyDeleteTimer];
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  43. #pragma mark Override Me
  44. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  45. - (BOOL)db_log:(__unused DDLogMessage *)logMessage {
  46. // Override me and add your implementation.
  47. //
  48. // Return YES if an item was added to the buffer.
  49. // Return NO if the logMessage was ignored.
  50. return NO;
  51. }
  52. - (void)db_save {
  53. // Override me and add your implementation.
  54. }
  55. - (void)db_delete {
  56. // Override me and add your implementation.
  57. }
  58. - (void)db_saveAndDelete {
  59. // Override me and add your implementation.
  60. }
  61. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  62. #pragma mark Private API
  63. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  64. - (void)performSaveAndSuspendSaveTimer {
  65. if (_unsavedCount > 0) {
  66. if (_deleteOnEverySave) {
  67. [self db_saveAndDelete];
  68. } else {
  69. [self db_save];
  70. }
  71. }
  72. _unsavedCount = 0;
  73. _unsavedTime = 0;
  74. if (_saveTimer != NULL && _saveTimerSuspended == 0) {
  75. dispatch_suspend(_saveTimer);
  76. _saveTimerSuspended = 1;
  77. }
  78. }
  79. - (void)performDelete {
  80. if (_maxAge > 0.0) {
  81. [self db_delete];
  82. _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
  83. }
  84. }
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. #pragma mark Timers
  87. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. - (void)destroySaveTimer {
  89. if (_saveTimer != NULL) {
  90. dispatch_source_cancel(_saveTimer);
  91. // Must activate a timer before releasing it (or it will crash)
  92. if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
  93. if (_saveTimerSuspended < 0) {
  94. dispatch_activate(_saveTimer);
  95. } else if (_saveTimerSuspended > 0) {
  96. dispatch_resume(_saveTimer);
  97. }
  98. } else {
  99. if (_saveTimerSuspended != 0) {
  100. dispatch_resume(_saveTimer);
  101. }
  102. }
  103. #if !OS_OBJECT_USE_OBJC
  104. dispatch_release(_saveTimer);
  105. #endif
  106. _saveTimer = NULL;
  107. _saveTimerSuspended = 0;
  108. }
  109. }
  110. - (void)updateAndResumeSaveTimer {
  111. if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0)) {
  112. uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
  113. dispatch_time_t startTime = dispatch_time(_unsavedTime, (int64_t)interval);
  114. dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC);
  115. if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
  116. if (_saveTimerSuspended < 0) {
  117. dispatch_activate(_saveTimer);
  118. _saveTimerSuspended = 0;
  119. } else if (_saveTimerSuspended > 0) {
  120. dispatch_resume(_saveTimer);
  121. _saveTimerSuspended = 0;
  122. }
  123. } else {
  124. if (_saveTimerSuspended != 0) {
  125. dispatch_resume(_saveTimer);
  126. _saveTimerSuspended = 0;
  127. }
  128. }
  129. }
  130. }
  131. - (void)createSuspendedSaveTimer {
  132. if ((_saveTimer == NULL) && (_saveInterval > 0.0)) {
  133. _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
  134. dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
  135. [self performSaveAndSuspendSaveTimer];
  136. } });
  137. _saveTimerSuspended = -1;
  138. }
  139. }
  140. - (void)destroyDeleteTimer {
  141. if (_deleteTimer != NULL) {
  142. dispatch_source_cancel(_deleteTimer);
  143. #if !OS_OBJECT_USE_OBJC
  144. dispatch_release(_deleteTimer);
  145. #endif
  146. _deleteTimer = NULL;
  147. }
  148. }
  149. - (void)updateDeleteTimer {
  150. if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
  151. int64_t interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
  152. dispatch_time_t startTime;
  153. if (_lastDeleteTime > 0) {
  154. startTime = dispatch_time(_lastDeleteTime, interval);
  155. } else {
  156. startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
  157. }
  158. dispatch_source_set_timer(_deleteTimer, startTime, (uint64_t)interval, 1ull * NSEC_PER_SEC);
  159. }
  160. }
  161. - (void)createAndStartDeleteTimer {
  162. if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
  163. _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
  164. if (_deleteTimer != NULL) {
  165. dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
  166. [self performDelete];
  167. } });
  168. [self updateDeleteTimer];
  169. // We are sure that -updateDeleteTimer did call dispatch_source_set_timer()
  170. // since it has the same guards on _deleteInterval and _maxAge
  171. if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *))
  172. dispatch_activate(_deleteTimer);
  173. else
  174. dispatch_resume(_deleteTimer);
  175. }
  176. }
  177. }
  178. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  179. #pragma mark Configuration
  180. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  181. - (NSUInteger)saveThreshold {
  182. // The design of this method is taken from the DDAbstractLogger implementation.
  183. // For extensive documentation please refer to the DDAbstractLogger implementation.
  184. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  185. // This method is designed explicitly for external access.
  186. //
  187. // Using "self." syntax to go through this method will cause immediate deadlock.
  188. // This is the intended result. Fix it by accessing the ivar directly.
  189. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  190. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  191. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  192. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  193. __block NSUInteger result;
  194. dispatch_sync(globalLoggingQueue, ^{
  195. dispatch_sync(self.loggerQueue, ^{
  196. result = self->_saveThreshold;
  197. });
  198. });
  199. return result;
  200. }
  201. - (void)setSaveThreshold:(NSUInteger)threshold {
  202. dispatch_block_t block = ^{
  203. @autoreleasepool {
  204. if (self->_saveThreshold != threshold) {
  205. self->_saveThreshold = threshold;
  206. // Since the saveThreshold has changed,
  207. // we check to see if the current unsavedCount has surpassed the new threshold.
  208. //
  209. // If it has, we immediately save the log.
  210. if ((self->_unsavedCount >= self->_saveThreshold) && (self->_saveThreshold > 0)) {
  211. [self performSaveAndSuspendSaveTimer];
  212. }
  213. }
  214. }
  215. };
  216. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  217. // For documentation please refer to the DDAbstractLogger implementation.
  218. if ([self isOnInternalLoggerQueue]) {
  219. block();
  220. } else {
  221. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  222. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  223. dispatch_async(globalLoggingQueue, ^{
  224. dispatch_async(self.loggerQueue, block);
  225. });
  226. }
  227. }
  228. - (NSTimeInterval)saveInterval {
  229. // The design of this method is taken from the DDAbstractLogger implementation.
  230. // For extensive documentation please refer to the DDAbstractLogger implementation.
  231. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  232. // This method is designed explicitly for external access.
  233. //
  234. // Using "self." syntax to go through this method will cause immediate deadlock.
  235. // This is the intended result. Fix it by accessing the ivar directly.
  236. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  237. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  238. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  239. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  240. __block NSTimeInterval result;
  241. dispatch_sync(globalLoggingQueue, ^{
  242. dispatch_sync(self.loggerQueue, ^{
  243. result = self->_saveInterval;
  244. });
  245. });
  246. return result;
  247. }
  248. - (void)setSaveInterval:(NSTimeInterval)interval {
  249. dispatch_block_t block = ^{
  250. @autoreleasepool {
  251. // C99 recommended floating point comparison macro
  252. // Read: isLessThanOrGreaterThan(floatA, floatB)
  253. if (/* saveInterval != interval */ islessgreater(self->_saveInterval, interval)) {
  254. self->_saveInterval = interval;
  255. // There are several cases we need to handle here.
  256. //
  257. // 1. If the saveInterval was previously enabled and it just got disabled,
  258. // then we need to stop the saveTimer. (And we might as well release it.)
  259. //
  260. // 2. If the saveInterval was previously disabled and it just got enabled,
  261. // then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
  262. //
  263. // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
  264. //
  265. // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
  266. // (Plus we might need to do an immediate save.)
  267. if (self->_saveInterval > 0.0) {
  268. if (self->_saveTimer == NULL) {
  269. // Handles #2
  270. //
  271. // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
  272. // if a save is needed the timer will fire immediately.
  273. [self createSuspendedSaveTimer];
  274. [self updateAndResumeSaveTimer];
  275. } else {
  276. // Handles #3
  277. // Handles #4
  278. //
  279. // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
  280. // if a save is needed the timer will fire immediately.
  281. [self updateAndResumeSaveTimer];
  282. }
  283. } else if (self->_saveTimer) {
  284. // Handles #1
  285. [self destroySaveTimer];
  286. }
  287. }
  288. }
  289. };
  290. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  291. // For documentation please refer to the DDAbstractLogger implementation.
  292. if ([self isOnInternalLoggerQueue]) {
  293. block();
  294. } else {
  295. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  296. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  297. dispatch_async(globalLoggingQueue, ^{
  298. dispatch_async(self.loggerQueue, block);
  299. });
  300. }
  301. }
  302. - (NSTimeInterval)maxAge {
  303. // The design of this method is taken from the DDAbstractLogger implementation.
  304. // For extensive documentation please refer to the DDAbstractLogger implementation.
  305. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  306. // This method is designed explicitly for external access.
  307. //
  308. // Using "self." syntax to go through this method will cause immediate deadlock.
  309. // This is the intended result. Fix it by accessing the ivar directly.
  310. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  311. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  312. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  313. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  314. __block NSTimeInterval result;
  315. dispatch_sync(globalLoggingQueue, ^{
  316. dispatch_sync(self.loggerQueue, ^{
  317. result = self->_maxAge;
  318. });
  319. });
  320. return result;
  321. }
  322. - (void)setMaxAge:(NSTimeInterval)interval {
  323. dispatch_block_t block = ^{
  324. @autoreleasepool {
  325. // C99 recommended floating point comparison macro
  326. // Read: isLessThanOrGreaterThan(floatA, floatB)
  327. if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) {
  328. NSTimeInterval oldMaxAge = self->_maxAge;
  329. NSTimeInterval newMaxAge = interval;
  330. self->_maxAge = interval;
  331. // There are several cases we need to handle here.
  332. //
  333. // 1. If the maxAge was previously enabled and it just got disabled,
  334. // then we need to stop the deleteTimer. (And we might as well release it.)
  335. //
  336. // 2. If the maxAge was previously disabled and it just got enabled,
  337. // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
  338. //
  339. // 3. If the maxAge was increased,
  340. // then we don't need to do anything.
  341. //
  342. // 4. If the maxAge was decreased,
  343. // then we should do an immediate delete.
  344. BOOL shouldDeleteNow = NO;
  345. if (oldMaxAge > 0.0) {
  346. if (newMaxAge <= 0.0) {
  347. // Handles #1
  348. [self destroyDeleteTimer];
  349. } else if (oldMaxAge > newMaxAge) {
  350. // Handles #4
  351. shouldDeleteNow = YES;
  352. }
  353. } else if (newMaxAge > 0.0) {
  354. // Handles #2
  355. shouldDeleteNow = YES;
  356. }
  357. if (shouldDeleteNow) {
  358. [self performDelete];
  359. if (self->_deleteTimer) {
  360. [self updateDeleteTimer];
  361. } else {
  362. [self createAndStartDeleteTimer];
  363. }
  364. }
  365. }
  366. }
  367. };
  368. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  369. // For documentation please refer to the DDAbstractLogger implementation.
  370. if ([self isOnInternalLoggerQueue]) {
  371. block();
  372. } else {
  373. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  374. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  375. dispatch_async(globalLoggingQueue, ^{
  376. dispatch_async(self.loggerQueue, block);
  377. });
  378. }
  379. }
  380. - (NSTimeInterval)deleteInterval {
  381. // The design of this method is taken from the DDAbstractLogger implementation.
  382. // For extensive documentation please refer to the DDAbstractLogger implementation.
  383. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  384. // This method is designed explicitly for external access.
  385. //
  386. // Using "self." syntax to go through this method will cause immediate deadlock.
  387. // This is the intended result. Fix it by accessing the ivar directly.
  388. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  389. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  390. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  391. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  392. __block NSTimeInterval result;
  393. dispatch_sync(globalLoggingQueue, ^{
  394. dispatch_sync(self.loggerQueue, ^{
  395. result = self->_deleteInterval;
  396. });
  397. });
  398. return result;
  399. }
  400. - (void)setDeleteInterval:(NSTimeInterval)interval {
  401. dispatch_block_t block = ^{
  402. @autoreleasepool {
  403. // C99 recommended floating point comparison macro
  404. // Read: isLessThanOrGreaterThan(floatA, floatB)
  405. if (/* deleteInterval != interval */ islessgreater(self->_deleteInterval, interval)) {
  406. self->_deleteInterval = interval;
  407. // There are several cases we need to handle here.
  408. //
  409. // 1. If the deleteInterval was previously enabled and it just got disabled,
  410. // then we need to stop the deleteTimer. (And we might as well release it.)
  411. //
  412. // 2. If the deleteInterval was previously disabled and it just got enabled,
  413. // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
  414. //
  415. // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
  416. //
  417. // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
  418. // (Plus we might need to do an immediate delete.)
  419. if (self->_deleteInterval > 0.0) {
  420. if (self->_deleteTimer == NULL) {
  421. // Handles #2
  422. //
  423. // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
  424. // if a delete is needed the timer will fire immediately.
  425. [self createAndStartDeleteTimer];
  426. } else {
  427. // Handles #3
  428. // Handles #4
  429. //
  430. // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
  431. // if a save is needed the timer will fire immediately.
  432. [self updateDeleteTimer];
  433. }
  434. } else if (self->_deleteTimer) {
  435. // Handles #1
  436. [self destroyDeleteTimer];
  437. }
  438. }
  439. }
  440. };
  441. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  442. // For documentation please refer to the DDAbstractLogger implementation.
  443. if ([self isOnInternalLoggerQueue]) {
  444. block();
  445. } else {
  446. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  447. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  448. dispatch_async(globalLoggingQueue, ^{
  449. dispatch_async(self.loggerQueue, block);
  450. });
  451. }
  452. }
  453. - (BOOL)deleteOnEverySave {
  454. // The design of this method is taken from the DDAbstractLogger implementation.
  455. // For extensive documentation please refer to the DDAbstractLogger implementation.
  456. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  457. // This method is designed explicitly for external access.
  458. //
  459. // Using "self." syntax to go through this method will cause immediate deadlock.
  460. // This is the intended result. Fix it by accessing the ivar directly.
  461. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  462. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  463. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  464. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  465. __block BOOL result;
  466. dispatch_sync(globalLoggingQueue, ^{
  467. dispatch_sync(self.loggerQueue, ^{
  468. result = self->_deleteOnEverySave;
  469. });
  470. });
  471. return result;
  472. }
  473. - (void)setDeleteOnEverySave:(BOOL)flag {
  474. dispatch_block_t block = ^{
  475. self->_deleteOnEverySave = flag;
  476. };
  477. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  478. // For documentation please refer to the DDAbstractLogger implementation.
  479. if ([self isOnInternalLoggerQueue]) {
  480. block();
  481. } else {
  482. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  483. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  484. dispatch_async(globalLoggingQueue, ^{
  485. dispatch_async(self.loggerQueue, block);
  486. });
  487. }
  488. }
  489. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  490. #pragma mark Public API
  491. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  492. - (void)savePendingLogEntries {
  493. dispatch_block_t block = ^{
  494. @autoreleasepool {
  495. [self performSaveAndSuspendSaveTimer];
  496. }
  497. };
  498. if ([self isOnInternalLoggerQueue]) {
  499. block();
  500. } else {
  501. dispatch_async(self.loggerQueue, block);
  502. }
  503. }
  504. - (void)deleteOldLogEntries {
  505. dispatch_block_t block = ^{
  506. @autoreleasepool {
  507. [self performDelete];
  508. }
  509. };
  510. if ([self isOnInternalLoggerQueue]) {
  511. block();
  512. } else {
  513. dispatch_async(self.loggerQueue, block);
  514. }
  515. }
  516. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  517. #pragma mark DDLogger
  518. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  519. - (void)didAddLogger {
  520. // If you override me be sure to invoke [super didAddLogger];
  521. [self createSuspendedSaveTimer];
  522. [self createAndStartDeleteTimer];
  523. }
  524. - (void)willRemoveLogger {
  525. // If you override me be sure to invoke [super willRemoveLogger];
  526. [self performSaveAndSuspendSaveTimer];
  527. [self destroySaveTimer];
  528. [self destroyDeleteTimer];
  529. }
  530. - (void)logMessage:(DDLogMessage *)logMessage {
  531. if ([self db_log:logMessage]) {
  532. BOOL firstUnsavedEntry = (++_unsavedCount == 1);
  533. if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
  534. [self performSaveAndSuspendSaveTimer];
  535. } else if (firstUnsavedEntry) {
  536. _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
  537. [self updateAndResumeSaveTimer];
  538. }
  539. }
  540. }
  541. - (void)flush {
  542. // This method is invoked by DDLog's flushLog method.
  543. //
  544. // It is called automatically when the application quits,
  545. // or if the developer invokes DDLog's flushLog method prior to crashing or something.
  546. [self performSaveAndSuspendSaveTimer];
  547. }
  548. @end