diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index 9483bbcb5f9..a9e0486cf94 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -32,6 +32,8 @@ extern BOOL getGULLoggerDebugMode(void); +extern NSUserDefaults *getGULLoggerUsetDefaults(void); + static NSString *const kMessageCode = @"I-COR000001"; @interface GULLoggerTest : XCTestCase @@ -153,6 +155,85 @@ - (void)testGULLoggerLevelValues { XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); } +- (void)testGetErrorWarningNumberBeforeLogDontCrash { + GULResetLogger(); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + }); + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + }); + + [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] + timeout:0.5 + enforceOrder:YES]; +} + +- (void)testErrorNumberIncrement { + [getGULLoggerUsetDefaults() setInteger:10 forKey:kGULLoggerErrorCountKey]; + + GULLogError(@"my service", NO, kMessageCode, @"Message."); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + XCTAssertEqual(count, 11); + }); + + [self waitForExpectationsWithTimeout:0.5 handler:NULL]; +} + +- (void)testWarningNumberIncrement { + [getGULLoggerUsetDefaults() setInteger:5 forKey:kGULLoggerWarningCountKey]; + + GULLogWarning(@"my service", NO, kMessageCode, @"Message."); + + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + XCTAssertEqual(count, 6); + }); + + [self waitForExpectationsWithTimeout:0.5 handler:NULL]; +} + +- (void)testResetIssuesCount { + [getGULLoggerUsetDefaults() setInteger:3 forKey:kGULLoggerErrorCountKey]; + [getGULLoggerUsetDefaults() setInteger:4 forKey:kGULLoggerWarningCountKey]; + + GULResetNumberOfIssuesLogged(); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + XCTAssertEqual(count, 0); + }); + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + XCTAssertEqual(count, 0); + }); + + [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] + timeout:0.5 + enforceOrder:YES]; +} + // Helper functions. - (BOOL)logExists { [self drainGULClientQueue]; diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 495e5830bb0..4e8feafce8f 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -19,6 +19,9 @@ #import #import "Public/GULLoggerLevel.h" +NSString *const kGULLoggerErrorCountKey = @"kGULLoggerErrorCountKey"; +NSString *const kGULLoggerWarningCountKey = @"kGULLoggerWarningCountKey"; + /// ASL client facility name used by GULLogger. const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger"; @@ -43,6 +46,8 @@ static NSRegularExpression *sMessageCodeRegex; #endif +void GULIncrementLogCountForLevel(GULLoggerLevel level); + void GULLoggerInitializeASL(void) { dispatch_once(&sGULLoggerOnceToken, ^{ NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue]; @@ -149,6 +154,10 @@ void GULLogBasic(GULLoggerLevel level, NSString *message, va_list args_ptr) { GULLoggerInitializeASL(); + + // Keep count of how many errors and warnings are triggered. + GULIncrementLogCountForLevel(level); + if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) { return; } @@ -194,6 +203,70 @@ void GULLogBasic(GULLoggerLevel level, #undef GUL_MAKE_LOGGER +#pragma mark - Number of errors and warnings + +NSUserDefaults *getGULLoggerUsetDefaults(void) { + static NSUserDefaults *_userDefaults = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"GoogleUtilities.Logger.GULLogger"]; + }); + + return _userDefaults; +} + +dispatch_queue_t getGULLoggerCounterQueue(void) { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(queue, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); + }); + + return queue; +} + +void GULAsyncGetUserDefaultsIntegerForKey(NSString *key, void (^completion)(NSInteger)) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + NSInteger count = [getGULLoggerUsetDefaults() integerForKey:key]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(count); + }); + }); +} + +void GULNumberOfErrorsLogged(void (^completion)(NSInteger)) { + GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey, completion); +} + +void GULNumberOfWarningsLogged(void (^completion)(NSInteger)) { + GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey, completion); +} + +void GULResetNumberOfIssuesLogged(void) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerErrorCountKey]; + [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerWarningCountKey]; + }); +} + +void GULIncrementUserDefaultsIntegerForKey(NSString *key) { + NSUserDefaults *defaults = getGULLoggerUsetDefaults(); + NSInteger errorCount = [defaults integerForKey:key]; + [defaults setInteger:errorCount + 1 forKey:key]; +} + +void GULIncrementLogCountForLevel(GULLoggerLevel level) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + if (level == GULLoggerLevelError) { + GULIncrementUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); + } else if (level == GULLoggerLevelWarning) { + GULIncrementUserDefaultsIntegerForKey(kGULLoggerWarningCountKey); + } + }); +} + #pragma mark - GULLoggerWrapper @implementation GULLoggerWrapper diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index ff425768681..453de4bd955 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -25,6 +25,16 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NSString *const GULLoggerService; +/** + * The key used to store the logger's error count. + */ +extern NSString *const kGULLoggerErrorCountKey; + +/** + * The key used to store the logger's warning count. + */ +extern NSString *const kGULLoggerWarningCountKey; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -129,6 +139,21 @@ extern void GULLogDebug(GULLoggerService service, NSString *message, ...) NS_FORMAT_FUNCTION(4, 5); +/** + * Retrieve the number of errors that have been logged since the stat was last reset. + */ +extern void GULNumberOfErrorsLogged(void (^completion)(NSInteger)); + +/** + * Retrieve the number of warnings that have been logged since the stat was last reset. + */ +extern void GULNumberOfWarningsLogged(void (^completion)(NSInteger)); + +/** + * Reset number of errors and warnings that have been logged to 0. + */ +extern void GULResetNumberOfIssuesLogged(void); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus