aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firebase/Core/FIRLogger.m
diff options
context:
space:
mode:
Diffstat (limited to 'Firebase/Core/FIRLogger.m')
-rw-r--r--Firebase/Core/FIRLogger.m228
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