diff options
Diffstat (limited to 'Firebase/Core/FIRLogger.m')
-rw-r--r-- | Firebase/Core/FIRLogger.m | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m new file mode 100644 index 0000000..0e3e325 --- /dev/null +++ b/Firebase/Core/FIRLogger.m @@ -0,0 +1,228 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "Private/FIRLogger.h" + +#import "FIRLoggerLevel.h" +#import "Private/FIRAppEnvironmentUtil.h" + +#include <asl.h> +#include <assert.h> +#include <stdbool.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/sysctl.h> + +FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]"; +FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]"; +FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; +FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; +FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; +FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]"; +FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; +FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; +FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]"; +FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]"; +FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; +FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; +FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]"; +FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]"; + +/// Arguments passed on launch. +NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled"; +NSString *const kFIREnableDebugModeApplicationArgument = @"-FIRDebugEnabled"; + +/// Key for the debug mode bit in NSUserDefaults. +NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode"; + +/// ASL client facility name used by FIRLogger. +const char *kFIRLoggerASLClientFacilityName = "com.firebase.app.logger"; + +/// Message format used by ASL client that matches format of NSLog. +const char *kFIRLoggerCustomASLMessageFormat = + "$((Time)(J.3)) $(Sender)[$(PID)] <$((Level)(str))> $Message"; + +static dispatch_once_t sFIRLoggerOnceToken; + +static aslclient sFIRLoggerClient; + +static dispatch_queue_t sFIRClientQueue; + +static BOOL sFIRLoggerDebugMode; + +// The sFIRAnalyticsDebugMode flag is here to support the -FIRDebugEnabled/-FIRDebugDisabled +// flags used by Analytics. Users who use those flags expect Analytics to log verbosely, +// while the rest of Firebase logs at the default level. This flag is introduced to support +// that behavior. +static BOOL sFIRAnalyticsDebugMode; + +static FIRLoggerLevel sFIRLoggerMaximumLevel; + +/// The regex pattern for the message code. +static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$"; +static NSRegularExpression *sMessageCodeRegex; + +void FIRLoggerInitializeASL() { + dispatch_once(&sFIRLoggerOnceToken, ^{ + // Initialize the ASL client handle. + sFIRLoggerClient = asl_open(NULL, kFIRLoggerASLClientFacilityName, ASL_OPT_STDERR); + + // Set the filter used by system/device log. Initialize in default mode. + asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE)); + sFIRLoggerDebugMode = NO; + sFIRAnalyticsDebugMode = NO; + sFIRLoggerMaximumLevel = FIRLoggerLevelNotice; + + NSArray *arguments = [NSProcessInfo processInfo].arguments; + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + BOOL debugMode = [userDefaults boolForKey:kFIRPersistedDebugModeKey]; + + if ([arguments containsObject:kFIRDisableDebugModeApplicationArgument]) { // Default mode + [userDefaults removeObjectForKey:kFIRPersistedDebugModeKey]; + } else if ([arguments containsObject:kFIREnableDebugModeApplicationArgument] + || debugMode) { // Debug mode + [userDefaults setBool:YES forKey:kFIRPersistedDebugModeKey]; + asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + sFIRLoggerDebugMode = YES; + } + + // We should disable debug mode if we are running from App Store. + if (sFIRLoggerDebugMode && [FIRAppEnvironmentUtil isFromAppStore]) { + sFIRLoggerDebugMode = NO; + } + + // Need to call asl_add_output_file so that the logs can appear in Xcode's console view. Set + // the ASL filter mask for this output file up to debug level so that all messages are viewable + // in the console. + asl_add_output_file(sFIRLoggerClient, STDERR_FILENO, kFIRLoggerCustomASLMessageFormat, + ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE); + + sFIRClientQueue = dispatch_queue_create("FIRLoggingClientQueue", DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(sFIRClientQueue, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); + + sMessageCodeRegex = + [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern options:0 error:NULL]; + }); +} + +void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode) { + FIRLoggerInitializeASL(); + dispatch_async(sFIRClientQueue, ^{ + // We should not enable debug mode if we are running from App Store. + if (analyticsDebugMode && [FIRAppEnvironmentUtil isFromAppStore]) { + return; + } + sFIRAnalyticsDebugMode = analyticsDebugMode; + asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + }); +} + +void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) { + if (loggerLevel < FIRLoggerLevelMin || loggerLevel > FIRLoggerLevelMax) { + FIRLogError(kFIRLoggerCore, @"I-COR000023", @"Invalid logger level, %ld", (long)loggerLevel); + return; + } + FIRLoggerInitializeASL(); + dispatch_async(sFIRClientQueue, ^{ + // We should not raise the logger level if we are running from App Store. + if (loggerLevel >= FIRLoggerLevelNotice && [FIRAppEnvironmentUtil isFromAppStore]) { + return; + } + + sFIRLoggerMaximumLevel = loggerLevel; + asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(loggerLevel)); + }); +} + +BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent) { + FIRLoggerInitializeASL(); + if (sFIRLoggerDebugMode) { + return YES; + } else if (sFIRAnalyticsDebugMode && analyticsComponent) { + return YES; + } + return (BOOL)(loggerLevel <= sFIRLoggerMaximumLevel); +} + +#ifdef DEBUG +void FIRResetLogger() { + sFIRLoggerOnceToken = 0; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kFIRPersistedDebugModeKey]; +} + +aslclient getFIRLoggerClient() { + return sFIRLoggerClient; +} + +dispatch_queue_t getFIRClientQueue() { + return sFIRClientQueue; +} + +BOOL getFIRLoggerDebugMode() { + return sFIRLoggerDebugMode; +} +#endif + +void FIRLogBasic(FIRLoggerLevel level, FIRLoggerService service, NSString *messageCode, + NSString *message, va_list args_ptr) { + FIRLoggerInitializeASL(); + BOOL canLog = level <= sFIRLoggerMaximumLevel; + + if (sFIRLoggerDebugMode) { + canLog = YES; + } else if (sFIRAnalyticsDebugMode && [kFIRLoggerAnalytics isEqualToString:service]) { + canLog = YES; + } + + if (!canLog) { + return; + } +#ifdef DEBUG + NSCAssert(messageCode.length == 11, @"Incorrect message code length."); + NSRange messageCodeRange = NSMakeRange(0, messageCode.length); + NSUInteger numberOfMatches = + [sMessageCodeRegex numberOfMatchesInString:messageCode options:0 range:messageCodeRange]; + NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); +#endif + NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr]; + logMsg = [NSString stringWithFormat:@"%@[%@] %@", service, messageCode, logMsg]; + dispatch_async(sFIRClientQueue, ^{ + asl_log(sFIRLoggerClient, NULL, level, "%s", logMsg.UTF8String); + }); +} + +/** + * Generates the logging functions using macros. + * + * Calling FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows: + * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [Firebase/Core][I-COR000001] Configure blah failed. + * Calling FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configure succeed.") shows: + * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [Firebase/Core][I-COR000001] Configure succeed. + */ +#define FIR_LOGGING_FUNCTION(level) \ +void FIRLog##level(FIRLoggerService service, NSString *messageCode, NSString *message, ...) { \ + va_list args_ptr; \ + va_start(args_ptr, message); \ + FIRLogBasic(FIRLoggerLevel##level, service, messageCode, message, args_ptr); \ + va_end(args_ptr); \ +} + +FIR_LOGGING_FUNCTION(Error) +FIR_LOGGING_FUNCTION(Warning) +FIR_LOGGING_FUNCTION(Notice) +FIR_LOGGING_FUNCTION(Info) +FIR_LOGGING_FUNCTION(Debug) + +#undef FIR_MAKE_LOGGER |