No Description

AWSDDAbstractDatabaseLogger.m 23KB

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