From 9191a3962f0842fff87d447d98eb40461e14d3eb Mon Sep 17 00:00:00 2001 From: Diana Perez Afanador Date: Tue, 12 Mar 2024 15:07:31 +0100 Subject: [PATCH 1/8] Add support for Logger categories --- CHANGELOG.md | 9 +- Realm.xcodeproj/project.pbxproj | 4 + Realm/RLMLogger.h | 44 ++- Realm/RLMLogger.mm | 56 +++- Realm/RLMLogger_Private.h | 15 +- Realm/RLMSyncManager.mm | 9 +- Realm/Tests/RealmTests.mm | 52 ++-- .../SwiftUISyncTestHost/ContentView.swift | 2 +- RealmSwift/Logger.swift | 275 ++++++++++++++++++ RealmSwift/Realm.swift | 40 --- RealmSwift/Tests/RealmTests.swift | 214 ++++++++++++-- 11 files changed, 607 insertions(+), 113 deletions(-) create mode 100644 RealmSwift/Logger.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index dfe4aa09a8..b92a65b7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,19 @@ x.y.z Release notes (yyyy-MM-dd) ============================================================= ### Enhancements * None. +* Update release packaging for Xcode 15.4. +* Added support for filtering logs by category. Users wil have more fine grained control over + the log level for each category as well. + ```swift + Logger.shared.setLogLevel(.info, category: Category.Storage.transactions) + ``` ### Fixed * ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?) * None. - +### Deprecations +* `RLMLogger.level`/`Logger.level` has been deprecated in favor of using `RLMLogger.setLevel:category:`/`Logger.setLevel(:category:)` and `RLMLogger.getLevelForCategory:`/`Logger.getLevel(for:)`. ### Compatibility * Realm Studio: 15.0.0 or later. diff --git a/Realm.xcodeproj/project.pbxproj b/Realm.xcodeproj/project.pbxproj index 82b6715fd5..ca9405618a 100644 --- a/Realm.xcodeproj/project.pbxproj +++ b/Realm.xcodeproj/project.pbxproj @@ -360,6 +360,7 @@ AC81360F287F21350029F15E /* AsymmetricObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC81360E287F21350029F15E /* AsymmetricObject.swift */; }; AC813612287F21700029F15E /* RLMAsymmetricObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AC813610287F21700029F15E /* RLMAsymmetricObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC813613287F21700029F15E /* RLMAsymmetricObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC813611287F21700029F15E /* RLMAsymmetricObject.mm */; }; + AC848BEC2BFFA4AF0026A2A6 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC848BEB2BFFA4AF0026A2A6 /* Logger.swift */; }; AC8846762686573B00DF4A65 /* SwiftUISyncTestHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8846752686573B00DF4A65 /* SwiftUISyncTestHostApp.swift */; }; AC8846782686573B00DF4A65 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8846772686573B00DF4A65 /* ContentView.swift */; }; AC8846B72687BC4100DF4A65 /* SwiftUIServerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8846B62687BC4100DF4A65 /* SwiftUIServerTests.swift */; }; @@ -912,6 +913,7 @@ AC81360E287F21350029F15E /* AsymmetricObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsymmetricObject.swift; sourceTree = ""; }; AC813610287F21700029F15E /* RLMAsymmetricObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMAsymmetricObject.h; sourceTree = ""; }; AC813611287F21700029F15E /* RLMAsymmetricObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RLMAsymmetricObject.mm; sourceTree = ""; }; + AC848BEB2BFFA4AF0026A2A6 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; AC8846732686573B00DF4A65 /* SwiftUISyncTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUISyncTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; AC8846752686573B00DF4A65 /* SwiftUISyncTestHostApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUISyncTestHostApp.swift; sourceTree = ""; }; AC8846772686573B00DF4A65 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -1362,6 +1364,7 @@ AC7825B82ACD90BE007ABA4B /* Geospatial.swift */, 5D1534B71CCFF545008976D7 /* LinkingObjects.swift */, 5D660FE41BE98D670021E04F /* List.swift */, + AC848BEB2BFFA4AF0026A2A6 /* Logger.swift */, 0C3BD4D225C1C5AB007CFDD3 /* Map.swift */, 5D660FE51BE98D670021E04F /* Migration.swift */, CF76F80124816B3800890DD2 /* MongoClient.swift */, @@ -2558,6 +2561,7 @@ 3F857A482769291800F9B9B1 /* KeyPathStrings.swift in Sources */, 5D1534B81CCFF545008976D7 /* LinkingObjects.swift in Sources */, 5D660FF21BE98D670021E04F /* List.swift in Sources */, + AC848BEC2BFFA4AF0026A2A6 /* Logger.swift in Sources */, 0C3BD4D325C1C5AB007CFDD3 /* Map.swift in Sources */, 5D660FF31BE98D670021E04F /* Migration.swift in Sources */, CF76F80224816B3800890DD2 /* MongoClient.swift in Sources */, diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index 96ab919671..7d71458d4e 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -55,6 +55,12 @@ typedef RLM_CLOSED_ENUM(NSUInteger, RLMLogLevel) { RLM_SWIFT_SENDABLE // invoked on a background thread typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); +/// A log callback function which can be set on RLMLogger. +/// +/// The log function may be called from multiple threads simultaneously, and is +/// responsible for performing its own synchronization if any is required. +RLM_SWIFT_SENDABLE // invoked on a background thread +typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, NSString *category, NSString *message) NS_REFINED_FOR_SWIFT; /** `RLMLogger` is used for creating your own custom logging logic. @@ -63,8 +69,9 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); Set this custom logger as you default logger using `setDefaultLogger`. RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - logFunction:^(RLMLogLevel level, NSString * message) { - NSLog(@"Realm Log - %lu, %@", (unsigned long)level, message); + category:RLMLogCategoryRealm + logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { + NSLog(@"Realm Log - %lu, %@, %@", (unsigned long)level, category, message); }]; @note By default default log threshold level is `RLMLogLevelInfo`, and logging strings are output to Apple System Logger. @@ -74,7 +81,8 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); /** Gets the logging threshold level used by the logger. */ -@property (nonatomic) RLMLogLevel level; +@property (nonatomic) RLMLogLevel level +__attribute__((deprecated("Use `setLevel(level:category)` or `setLevel:category` instead."))); /// :nodoc: - (instancetype)init NS_UNAVAILABLE; @@ -85,7 +93,19 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); @param level The log level to be set for the logger. @param logFunction The log function which will be invoked whenever there is a log message. */ -- (instancetype)initWithLevel:(RLMLogLevel)level logFunction:(RLMLogFunction)logFunction; +- (instancetype)initWithLevel:(RLMLogLevel)level logFunction:(RLMLogFunction)logFunction +__attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); + +/** + Creates a logger with the associated log level and the logic function to define your own logging logic. + + @param level The log level to be set for the logger. + @param category The log category to be set for the logger. + @param logFunction The log function which will be invoked whenever there is a log message. +*/ +- (instancetype)initWithLevel:(RLMLogLevel)level + category:(NSString *)category + logFunction:(RLMLogCategoryFunction)logFunction; #pragma mark RLMLogger Default Logger API @@ -94,6 +114,22 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); */ @property (class) RLMLogger *defaultLogger NS_SWIFT_NAME(shared); +/** + Sets the logger's associated log and category. + + @param level The log level to be set for the logger. + @param category The log function which will be invoked whenever there is a log message. +*/ +- (void)setLevel:(RLMLogLevel)level category:(NSString *)category NS_REFINED_FOR_SWIFT; + +/** + Gets the logger's associated level for the specified category. + + @param category The log category which we need the level. + @returns The log level for the specified category +*/ +- (RLMLogLevel)getLevelForCategory:(NSString *)category NS_REFINED_FOR_SWIFT; + @end RLM_HEADER_AUDIT_END(nullability) diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index 7cb27e6a1b..5fbfaabbc9 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -22,11 +22,12 @@ #import -typedef void (^RLMLoggerFunction)(RLMLogLevel level, NSString *message); +typedef void (^RLMLoggerFunction)(RLMLogLevel level, NSString *category, NSString *message); using namespace realm; using Logger = realm::util::Logger; using Level = Logger::Level; +using LogCategory = realm::util::LogCategory; namespace { static Level levelForLogLevel(RLMLogLevel logLevel) { @@ -61,7 +62,7 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { static NSString* levelPrefix(Level logLevel) { switch (logLevel) { - case Level::off: + case Level::off: return @""; case Level::all: return @""; case Level::trace: return @"Trace"; case Level::debug: return @"Debug"; @@ -75,18 +76,18 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { } struct CocoaLogger : public Logger { - void do_log(const realm::util::LogCategory&, Level level, const std::string& message) override { - NSLog(@"%@: %@", levelPrefix(level), RLMStringDataToNSString(message)); + void do_log(const LogCategory& category, Level level, const std::string& message) override { + NSLog(@"%@:%@ %@", levelPrefix(level), RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); } }; class CustomLogger : public Logger { public: RLMLoggerFunction function; - void do_log(const realm::util::LogCategory&, Level level, const std::string& message) override { + void do_log(const LogCategory& category, Level level, const std::string& message) override { @autoreleasepool { if (function) { - function(logLevelForLevel(level), RLMStringDataToNSString(message)); + function(logLevelForLevel(level), RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); } } } @@ -120,10 +121,26 @@ - (instancetype)initWithLogger:(std::shared_ptr)logger { return self; } -- (instancetype)initWithLevel:(RLMLogLevel)level logFunction:(RLMLogFunction)logFunction { +- (instancetype)initWithLevel:(RLMLogLevel)level + logFunction:(RLMLogFunction)logFunction { if (self = [super init]) { auto logger = std::make_shared(); logger->set_level_threshold(levelForLogLevel(level)); + auto block = [logFunction](RLMLogLevel level, NSString *, NSString *message) { + logFunction(level, message); + }; + logger->function = block; + self->_logger = logger; + } + return self; +} + +- (instancetype)initWithLevel:(RLMLogLevel)level + category:(NSString *)category + logFunction:(RLMLogCategoryFunction)logFunction { + if (self = [super init]) { + auto logger = std::make_shared(); + logger->set_level_threshold(LogCategory::get_category(category.UTF8String), levelForLogLevel(level)); logger->function = logFunction; self->_logger = logger; } @@ -140,11 +157,34 @@ - (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... { } } -- (void)logLevel:(RLMLogLevel)logLevel message:(NSString *)message { +- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message { auto level = levelForLogLevel(logLevel); if (_logger->would_log(level)) { _logger->log(level, "%1", message.UTF8String); + _logger->log(LogCategory::get_category(category.UTF8String), levelForLogLevel(logLevel), message.UTF8String); + } +} + +- (void)setLevel:(RLMLogLevel)level category:(NSString *)category { + RLMLogger *defaultLogger = [RLMLogger defaultLogger]; + defaultLogger->_logger->set_level_threshold(LogCategory::get_category(category.UTF8String), levelForLogLevel(level)); +} + +- (RLMLogLevel)getLevelForCategory:(NSString *)category { + RLMLogger *defaultLogger = [RLMLogger defaultLogger]; + return logLevelForLevel(defaultLogger->_logger->get_level_threshold(LogCategory::get_category(category.UTF8String))); +} + +#pragma mark Testing + ++ (NSArray *)getAllCategories { + NSMutableArray *a = [NSMutableArray new]; + auto categories = LogCategory::get_category_names(); + for (const auto& category : categories) { + NSString *categoryName = RLMStringDataToNSString(category); + [a addObject:categoryName]; } + return a; } #pragma mark Global Logger Setter diff --git a/Realm/RLMLogger_Private.h b/Realm/RLMLogger_Private.h index 08f8c591eb..2a2ba269dc 100644 --- a/Realm/RLMLogger_Private.h +++ b/Realm/RLMLogger_Private.h @@ -30,7 +30,20 @@ RLM_HEADER_AUDIT_BEGIN(nullability) @param message The message to log. */ - (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... NS_SWIFT_UNAVAILABLE(""); -- (void)logLevel:(RLMLogLevel)logLevel message:(NSString *)message; + +/** + Log a message to the supplied level. + + @param logLevel The log level for the message. + @param category The log category for the message. + @param message The message to log. + */ +- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message; + +/** +Gets all the categories from Core. This is to be used for testing purposes only. + */ ++ (NSArray *)getAllCategories; @end RLM_HEADER_AUDIT_END(nullability) diff --git a/Realm/RLMSyncManager.mm b/Realm/RLMSyncManager.mm index 6a76b62b87..032a04d47a 100644 --- a/Realm/RLMSyncManager.mm +++ b/Realm/RLMSyncManager.mm @@ -38,6 +38,7 @@ // NEXT-MAJOR: All the code associated to the logger from sync manager should be removed. using Level = realm::util::Logger::Level; +using LogCategory = realm::util::LogCategory; namespace { Level levelForSyncLogLevel(RLMSyncLogLevel logLevel) { @@ -73,14 +74,14 @@ RLMSyncLogLevel logLevelForLevel(Level logLevel) { #pragma mark - Loggers struct CocoaSyncLogger : public realm::util::Logger { - void do_log(const realm::util::LogCategory&, Level, const std::string& message) override { - NSLog(@"Sync: %@", RLMStringDataToNSString(message)); + void do_log(const realm::util::LogCategory& category, Level, const std::string& message) override { + NSLog(@"%@: %@", RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); } }; static std::unique_ptr defaultSyncLogger(realm::util::Logger::Level level) { auto logger = std::make_unique(); - logger->set_level_threshold(level); + logger->set_level_threshold(LogCategory::sync, level); return std::move(logger); } @@ -154,7 +155,7 @@ - (void)setLogger:(RLMSyncLogFunction)logFn { _syncManager->set_logger_factory([logFn](realm::util::Logger::Level level) { auto logger = std::make_unique(); logger->logFn = logFn; - logger->set_level_threshold(level); + logger->set_level_threshold(LogCategory::sync, level); return logger; }); } diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index 3dc3d2090d..5fd970e92e 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -1475,7 +1475,7 @@ - (void)testAsyncTransactionShouldWrite { [asyncComplete fulfill]; XCTAssertNil(error); }]; - + [self waitForExpectationsWithTimeout:1.0 handler:nil]; } @@ -1774,7 +1774,7 @@ - (void)testAsyncBeginTransactionInAsyncTransaction { [realm beginAsyncWriteTransaction:^{ [StringObject createInRealm:realm withValue:@[@"string"]]; - + [realm commitAsyncWriteTransaction:^(NSError *error) { XCTAssertEqual(0U, [StringObject allObjects].count); [transaction1 fulfill]; @@ -1801,7 +1801,7 @@ - (void)testAsyncTransactionFromSyncTransaction { [realm beginWriteTransaction]; [StringObject createInRealm:realm withValue:@[@"string"]]; - + [realm beginAsyncWriteTransaction:^{ [StringObject createInRealm:realm withValue:@[@"string"]]; @@ -1823,7 +1823,7 @@ - (void)testAsyncNestedWrites { [self dispatchAsync:^{ RLMRealm *realm = [RLMRealm defaultRealmForQueue:self.bgQueue]; - + [realm beginAsyncWriteTransaction:^{ [StringObject createInRealm:realm withValue:@[@"string 1"]]; @@ -1941,7 +1941,7 @@ - (void)testAsyncWriteOnQueueConfinedRealm { }]; }]; }); - + [self waitForExpectationsWithTimeout:2.0 handler:nil]; XCTAssertEqual(1U, [StringObject allObjectsInRealm:[RLMRealm defaultRealm]].count); } @@ -1977,7 +1977,7 @@ - (void)testAsyncCancelWtongTransaction { }]; [realm cancelAsyncTransaction:transId+1]; - + [self waitForExpectationsWithTimeout:2.0 handler:nil]; XCTAssertEqual(1U, [StringObject allObjectsInRealm:[RLMRealm defaultRealm]].count); } @@ -2008,7 +2008,7 @@ - (void)testAsyncIsInTransaction { XCTAssertFalse(realm.isPerformingAsynchronousWriteOperations); XCTAssertFalse(realm.inWriteTransaction); - + [realm beginWriteTransaction]; XCTAssertFalse(realm.isPerformingAsynchronousWriteOperations); XCTAssertTrue(realm.inWriteTransaction); @@ -2589,7 +2589,7 @@ - (void)testThaw { - (void)testThawDifferentThread { RLMRealm *frozenRealm = [[RLMRealm defaultRealm] freeze]; XCTAssertTrue(frozenRealm.frozen); - + // Thaw on a thread which already has a Realm should use existing reference. [self dispatchAsyncAndWait:^{ RLMRealm *realm = [RLMRealm defaultRealm]; @@ -2597,7 +2597,7 @@ - (void)testThawDifferentThread { XCTAssertFalse(thawed.frozen); XCTAssertEqual(thawed, realm); }]; - + // Thaw on thread without existing refernce. [self dispatchAsyncAndWait:^{ RLMRealm *thawed = [frozenRealm thaw]; @@ -2960,45 +2960,48 @@ - (void)tearDown { } - (void)testSetDefaultLogLevel { __block NSMutableString *logs = [[NSMutableString alloc] init]; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelAll logFunction:^(RLMLogLevel level, NSString *message) { + NSString *category = @"Realm"; + RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelAll category:category logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([RLMLogger defaultLogger].level, RLMLogLevelAll); + XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelAll); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertTrue([logs containsString:@"7 DB:"]); // Trace [logs setString: @""]; - logger.level = RLMLogLevelDetail; + [logger setLevel:RLMLogLevelDetail category:category]; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([RLMLogger defaultLogger].level, RLMLogLevelDetail); + XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelDetail); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace } - (void)testDefaultLogger { __block NSMutableString *logs = [[NSMutableString alloc] init]; + NSString *category = @"Realm"; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelOff - logFunction:^(RLMLogLevel level, NSString *message) { + category:category + logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; - XCTAssertEqual(RLMLogger.defaultLogger.level, RLMLogLevelOff); + XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelOff); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] == 0); // Test LogLevel Detail - logger.level = RLMLogLevelDetail; + [logger setLevel:RLMLogLevelDetail category:category]; @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] > 0); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace // Test LogLevel All - logger.level = RLMLogLevelAll; + [logger setLevel:RLMLogLevelAll category:category]; @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] > 0); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail @@ -3007,11 +3010,12 @@ - (void)testDefaultLogger { [logs setString: @""]; // Init Custom Logger RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - logFunction:^(RLMLogLevel level, NSString * message) { + category:category + logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; - XCTAssertEqual(RLMLogger.defaultLogger.level, RLMLogLevelDebug); + XCTAssertEqual([RLMLogger.defaultLogger getLevelForCategory:category], RLMLogLevelDebug); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace @@ -3019,8 +3023,10 @@ - (void)testDefaultLogger { - (void)testCustomLoggerLogMessage { __block NSMutableString *logs = [[NSMutableString alloc] init]; + NSString *category = @"Realm"; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelInfo - logFunction:^(RLMLogLevel level, NSString * message) { + category:category + logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @@ -3046,9 +3052,11 @@ - (void)tearDown { - (void)testSyncConnectionMetrics { __block NSMutableString *logs = [[NSMutableString alloc] init]; + NSString *category = @"Realm"; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - logFunction:^(RLMLogLevel level, NSString * message) { - [logs appendFormat:@" %@ %lu %@\n", [NSDate date], level, message]; + category:category + logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { + [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; RLMApp *app = [RLMApp appWithId:@"test-id"]; diff --git a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift index 66a22fae49..061615a08b 100644 --- a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift +++ b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift @@ -198,7 +198,7 @@ class LoginHelper: ObservableObject { private let appConfig = AppConfiguration(baseURL: "http://localhost:9090") func login(email: String, password: String, completion: @escaping (User) -> Void) { - Logger.shared.level = .all + Logger.shared.setLogLevel(.all, category: Category.Realm) let app = RealmSwift.App(id: ProcessInfo.processInfo.environment["app_id"]!, configuration: appConfig) app.login(credentials: .emailPassword(email: email, password: password)) .receive(on: DispatchQueue.main) diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift new file mode 100644 index 0000000000..0a0acfee54 --- /dev/null +++ b/RealmSwift/Logger.swift @@ -0,0 +1,275 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm Inc. +// +// 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 Realm +import Realm.Private + +/** + `Logger` is used for creating your own custom logging logic. + + You can define your own logger creating an instance of `Logger` and define the log function which will be + invoked whenever there is a log message. + + ```swift + let logger = Logger(level: .all, category: Category.realm) { level, message in + print("Realm Log - \(category.toString())-\(level): \(message)") + } + ``` + + Set this custom logger as you default logger using `Logger.shared`. + + ```swift + Logger.shared = inMemoryLogger + ``` + + - note: By default default log threshold level is `.info`, and logging strings are output to Apple System Logger. +*/ +public typealias Logger = RLMLogger +extension Logger { + /** + Log a message to the supplied level. + + ```swift + let logger = Logger(level: .info, logFunction: { level, message in + print("Realm Log - \(level): \(message)") + }) + logger.log(level: .info, message: "Info DB: Database opened succesfully") + ``` + + - parameter level: The log level for the message. + - parameter category: The log category for the message. + - parameter message: The message to log. + */ + internal func log(level: LogLevel, category: LogCategory = Category.realm, message: String) { + self.log(with: level, category: category.toString(), message: message) + } + + /** + Creates a logger with the associated log level, category and the logic function to define your own logging logic. + + ```swift + let logger = Logger(level: .info, category: Category.All, logFunction: { level, category, message in + print("\(category.toString()) - \(level): \(message)") + }) + logger.log(level: .info, category: Category.realm, message: "Info DB: Database opened succesfully") + ``` + + - parameter level: The log level to be set for the logger. + - parameter category: The log category to be set for the logger, by default it will setup the top Category `Category.realm` + - parameter function: The log function which will be invoked whenever there is a log message. + + - note:By setting the log level of a category, it will set all its subcategories log level as well. + - SeeAlso: `LogCategory` + */ + public convenience init(level: LogLevel, category: LogCategory = Category.realm, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { + self.init(level: level, category: category.toString()) { level, cat, message in + function(level, Category.fromString(cat)!, message) + } + } + + /** + Sets the log level for a given log category. + + - parameter level: The log level to be set for the logger. + - parameter category: The log category to be set for the logger, by default it will setup the top Category `Category.realm` + + - note:By setting the log level of a category, it will set all its subcategories log level as well. + - SeeAlso: `LogCategory` + */ + public func setLogLevel(_ level: LogLevel, for category: LogCategory = Category.realm) { + Logger.shared.__setLevel(level, category: category.toString()) + } + + /** + Gets the current log level of a log category. + + - parameter category: The target log category. + + - returns: The `LogLevel` for the given category. + - SeeAlso: `LogCategory` + */ + public func getLogLevel(for category: LogCategory) -> LogLevel { + Logger.shared.__getLevelForCategory(category.toString()) + } +} + +/// Defines a log category for the Realm `Logger`. +public protocol LogCategory: Sendable { + /** + Returns the string represtation of the Log category. + + - returns: A string representing the log category. + - SeeAlso: `LogCategory` + */ + func toString() -> String +} + +/** + Category hierarchy: + ``` + Realm + ├─► Storage + │ ├─► Transaction + │ ├─► Query + │ ├─► Object + │ └─► Notification + ├─► Sync + │ ├─► Client + │ │ ├─► Session + │ │ ├─► Changeset + │ │ ├─► Network + │ │ └─► Reset + │ └─► Server + ├─► App + └─► Sdk + ``` +*/ +public enum Category: String, LogCategory { + /// Top level log category for Realm, updating this category level would set all other subcategories too. + case realm = "Realm" + /// Log category for all sdk related logs. + case sdk = "Realm.SDK" + /// Log category for all app related logs. + case app = "Realm.App" + + /** + Returns the string represtation of the Log category. + + - returns: A string representing the log category. + - SeeAlso: `LogCategory` + */ + public func toString() -> String { + return self.rawValue + } + + /// :nodoc: + fileprivate static func fromString(_ string: String) -> LogCategory? { + if let category = Category(rawValue: string) { + return category + } else if let storage = Storage(rawValue: string) { + return storage + } else if let sync = Sync(rawValue: string) { + return sync + } else if let client = Sync.Client(rawValue: string) { + return client + } else { + return nil + } + } + + /** + Log category for all storage related logs. + + Category hierarchy: + ``` + Storage + ├─► Transaction + ├─► Query + ├─► Object + └─► Notification + ``` + */ + public enum Storage: String, LogCategory { + /// Log category for all database related logs. + case all = "Realm.Storage" + /// Log category for all database transaction related logs. + case transaction = "Realm.Storage.Transaction" + /// Log category for all database queries related logs. + case query = "Realm.Storage.Query" + /// Log category for all database object related logs. + case object = "Realm.Storage.Object" + /// Log category for all database notification related logs. + case notification = "Realm.Storage.Notification" + + /** + Returns the string represtation of the Log category. + + - returns: A string representing the log category. + - SeeAlso: `LogCategory` + */ + public func toString() -> String { + return self.rawValue + } + } + + /** + Log category for all sync related logs. + + Category hierarchy: + ``` + Sync + ├─► Client + │ ├─► Session + │ ├─► Changeset + │ ├─► Network + │ └─► Reset + └─► Server + ``` + */ + public enum Sync: String, LogCategory { + /// Log category for all sync related logs. + case all = "Realm.Sync" + /// Log category for all sync server related logs. + case server = "Realm.Sync.Server" + + /** + Returns the string represtation of the Log category. + + - returns: A string representing the log category. + - SeeAlso: `LogCategory` + */ + public func toString() -> String { + return self.rawValue + } + + /** + Log category for all storage related logs. + + Category hierarchy: + ``` + Storage + ├─► Transaction + ├─► Query + ├─► Object + └─► Notification + ``` + */ + public enum Client: String, LogCategory { + /// Log category for all sync client related logs. + case all = "Realm.Sync.Client" + /// Log category for all sync client session related logs. + case session = "Realm.Sync.Client.Session" + /// Log category for all sync client changeset related logs. + case changeset = "Realm.Sync.Client.Changeset" + /// Log category for all sync client network related logs. + case network = "Realm.Sync.Client.Network" + /// Log category for all sync client reset related logs. + case reset = "Realm.Sync.Client.Reset" + + /** + Returns the string represtation of the Log category. + + - returns: A string representing the log category. + - SeeAlso: `LogCategory` + */ + public func toString() -> String { + return self.rawValue + } + } + } +} diff --git a/RealmSwift/Realm.swift b/RealmSwift/Realm.swift index 5a5bed9b18..3e7473354c 100644 --- a/RealmSwift/Realm.swift +++ b/RealmSwift/Realm.swift @@ -1631,43 +1631,3 @@ extension Projection: RealmFetchable { return Root.className() } } - -/** - `Logger` is used for creating your own custom logging logic. - - You can define your own logger creating an instance of `Logger` and define the log function which will be - invoked whenever there is a log message. - - ```swift - let logger = Logger(level: .all) { level, message in - print("Realm Log - \(level): \(message)") - } - ``` - - Set this custom logger as you default logger using `Logger.shared`. - - ```swift - Logger.shared = inMemoryLogger - ``` - - - note: By default default log threshold level is `.info`, and logging strings are output to Apple System Logger. -*/ -public typealias Logger = RLMLogger -extension Logger { - /** - Log a message to the supplied level. - - ```swift - let logger = Logger(level: .info, logFunction: { level, message in - print("Realm Log - \(level): \(message)") - }) - logger.log(level: .info, message: "Info DB: Database opened succesfully") - ``` - - - parameter level: The log level for the message. - - parameter message: The message to log. - */ - internal func log(level: LogLevel, message: String) { - self.logLevel(level, message: message) - } -} diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index 5539141468..2a6a5e35af 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -23,6 +23,7 @@ #endif import Foundation import Realm +import Realm.Private import XCTest #if canImport(RealmSwiftTestSupport) @@ -1953,66 +1954,215 @@ class LoggerTests: TestCase, @unchecked Sendable { override func tearDown() { Logger.shared = logger } + func testSetDefaultLogLevel() throws { - var logs: String = "" - let logger = Logger(level: .off) { level, message in - logs += "\(Date.now) \(level.logLevel) \(message)" + let logs = Locked("") + let logger = Logger(level: .off) { level, _, message in + logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) } Logger.shared = logger try autoreleasepool { _ = try Realm() } - XCTAssertTrue(logs.isEmpty) + XCTAssertTrue(logs.value.isEmpty) - logger.level = .all + logger.setLogLevel(.all) try autoreleasepool { _ = try Realm() } // We should be getting logs after changing the log level - XCTAssertEqual(Logger.shared.level, .all) - XCTAssertTrue(logs.contains("Details DB:")) - XCTAssertTrue(logs.contains("Trace DB:")) + XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .all) + XCTAssertTrue(logs.value.contains("Details DB:")) + XCTAssertTrue(logs.value.contains("Trace DB:")) } func testDefaultLogger() throws { - var logs: String = "" - let logger = Logger(level: .off) { level, message in - logs += "\(Date.now) \(level.logLevel) \(message)" + let logs = Locked("") + let logger = Logger(level: .off) { level, _, message in + logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) } Logger.shared = logger - XCTAssertEqual(Logger.shared.level, .off) + XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .off) try autoreleasepool { _ = try Realm() } - XCTAssertTrue(logs.isEmpty) + XCTAssertTrue(logs.value.isEmpty) // Info - logger.level = .detail + logger.setLogLevel(.detail) try autoreleasepool { _ = try Realm() } - XCTAssertTrue(!logs.isEmpty) - XCTAssertTrue(logs.contains("Details DB:")) + XCTAssertTrue(!logs.value.isEmpty) + XCTAssertTrue(logs.value.contains("Details DB:")) // Trace - logs = "" - logger.level = .trace + logs.wrappedValue = "" + logger.setLogLevel(.trace) try autoreleasepool { _ = try Realm() } - XCTAssertTrue(!logs.isEmpty) - XCTAssertTrue(logs.contains("Trace DB:")) + XCTAssertTrue(!logs.value.isEmpty) + XCTAssertTrue(logs.value.contains("Trace DB:")) // Detail - logs = "" - logger.level = .detail + logs.wrappedValue = "" + logger.setLogLevel(.detail) try autoreleasepool { _ = try Realm() } - XCTAssertTrue(!logs.isEmpty) - XCTAssertTrue(logs.contains("Details DB:")) - XCTAssertFalse(logs.contains("Trace DB:")) + XCTAssertTrue(!logs.value.isEmpty) + XCTAssertTrue(logs.value.contains("Details DB:")) + XCTAssertFalse(logs.value.contains("Trace DB:")) - logs = "" - Logger.shared = Logger(level: .trace) { level, message in - logs += "\(Date.now) \(level.logLevel) \(message)" + logs.wrappedValue = "" + Logger.shared = Logger(level: .trace) { level, _, message in + logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) } - XCTAssertEqual(Logger.shared.level, .trace) + XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .trace) try autoreleasepool { _ = try Realm() } - XCTAssertTrue(!logs.isEmpty) - XCTAssertTrue(logs.contains("Details DB:")) - XCTAssertTrue(logs.contains("Trace DB:")) + XCTAssertTrue(!logs.value.isEmpty) + XCTAssertTrue(logs.value.contains("Details DB:")) + XCTAssertTrue(logs.value.contains("Trace DB:")) + } + + // Core defines the different categories in runtime, forcing the SDK to define the categories again. + // This test validates that we have added new defined categories to the Categories enum and/or + // child categories + func testAllCategoriesWatchDog() throws { + for category in Logger.getAllCategories() { + XCTAssertNotNil(categoryfromString(category), "LogCategory `\(category)` not added to the Category enum.") + XCTAssertEqual(categoryfromString(category)?.toString(), category) + } + } + + func testLogLevelForCategories() throws { + Logger.shared.setLogLevel(.off) + XCTAssertEqual(logger.getLogLevel(for: Category.realm), .off) + + for category in Logger.getAllCategories() { + let categoryEnum = categoryfromString(category) + XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") + + logger.setLogLevel(.trace, for: categoryEnum!) + XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + XCTAssertNotEqual(logger.getLogLevel(for: categoryEnum!), .all) + } + } + + func testLogMessageCategory() throws { + let logs = Locked("") + let logger = Logger(level: .off) { level, category, message in + logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + } + Logger.shared = logger + + for category in Logger.getAllCategories() { + logs.wrappedValue = "" + let categoryEnum = categoryfromString(category) + XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") + + logger.setLogLevel(.trace, for: categoryEnum!) + + XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + logger.log(with: .trace, category: category, message: "Test") + XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \(category)") + } + } + + /// Logger shoulldn't log category, even if it's a child or same category type, this test workd because `get_category_names()` returns categories from parent to children. + func testNotLogParentOrRelatedCategory() throws { + let logs = Locked("") + let logger = Logger(level: .off) { level, category, message in + logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + } + Logger.shared = logger + + let categories = Logger.getAllCategories() + for (index, category) in categories.enumerated() { + guard index <= categories.count-2 else { return } + logs.wrappedValue = "" + let categoryEnum = categoryfromString(categories[index+1]) + XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") + + logger.setLogLevel(.trace, for: categoryEnum!) + XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + + logger.log(with: .trace, category: category, message: "Test") + XCTAssertFalse(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log shouldn't contain \(categories[index+1])") + } + } + + /// Logger should log any message with from a child category + func testShouldLogWhenParentCategory() throws { + let logs = Locked("") + let logger = Logger(level: .off) { level, category, message in + logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + } + Logger.shared = logger + logger.setLogLevel(.trace, for: Category.realm) + XCTAssertEqual(logger.getLogLevel(for: Category.realm), .trace) + + for category in Logger.getAllCategories() { + logs.wrappedValue = "" + logger.log(with: .trace, category: category, message: "Test") + XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \( Category.realm.toString())") + } + } + + func testChangeCategoryLevel() throws { + let logs = Locked("") + let logger = Logger(level: .off) { level, category, message in + logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + } + Logger.shared = logger + + logger.setLogLevel(.trace, for: Category.realm) + XCTAssertEqual(logger.getLogLevel(for: Category.realm), .trace) + + for category in Logger.getAllCategories() { + let categoryEnum = categoryfromString(category) + XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + } + + logger.setLogLevel(.all, for: Category.realm) + XCTAssertEqual(logger.getLogLevel(for: Category.realm), .all) + + for category in Logger.getAllCategories() { + let categoryEnum = categoryfromString(category) + XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .all) + } + } + + func testChangeSubCategoryLevel() throws { + let logs = Locked("") + let logger = Logger(level: .off) { level, category, message in + logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + } + Logger.shared = logger + + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.all), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.transaction), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.query), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.object), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.notification), .off) + + logger.setLogLevel(.info, for: Category.Storage.all) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.all), .info) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.transaction), .info) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.query), .info) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.object), .info) + XCTAssertEqual(logger.getLogLevel(for: Category.Storage.notification), .info) + + XCTAssertEqual(logger.getLogLevel(for: Category.realm), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.sdk), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.app), .off) + XCTAssertEqual(logger.getLogLevel(for: Category.Sync.all), .off) + } + + func categoryfromString(_ string: String) -> LogCategory? { + if let category = Category(rawValue: string) { + return category + } else if let storage = Category.Storage(rawValue: string) { + return storage + } else if let sync = Category.Sync(rawValue: string) { + return sync + } else if let client = Category.Sync.Client(rawValue: string) { + return client + } else { + return nil + } } } From 0dbddd681ed8a001f79020848ce8b10f8fc22cf9 Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Thu, 30 May 2024 17:40:45 +0200 Subject: [PATCH 2/8] Solved PR comments --- Realm/ObjectServerTests/RLMSyncTestCase.mm | 2 +- Realm/RLMLogger.h | 75 ++++++++++++++++-- Realm/RLMLogger.mm | 85 +++++++++++++++++--- Realm/RLMLogger_Private copy.h | 51 ++++++++++++ Realm/RLMLogger_Private.h | 11 ++- Realm/Tests/RealmTests.mm | 26 ++++--- RealmSwift/Logger.swift | 91 +++++++++++++++++++++- 7 files changed, 307 insertions(+), 34 deletions(-) create mode 100644 Realm/RLMLogger_Private copy.h diff --git a/Realm/ObjectServerTests/RLMSyncTestCase.mm b/Realm/ObjectServerTests/RLMSyncTestCase.mm index 2776bad936..40e97ec060 100644 --- a/Realm/ObjectServerTests/RLMSyncTestCase.mm +++ b/Realm/ObjectServerTests/RLMSyncTestCase.mm @@ -613,7 +613,7 @@ - (RLMApp *)appWithId:(NSString *)appId { RLMApp *app = [RLMApp appWithConfiguration:config]; RLMSyncManager *syncManager = app.syncManager; syncManager.userAgent = self.name; - RLMLogger.defaultLogger.level = RLMLogLevelWarn; + [RLMLogger.defaultLogger setLevel:RLMLogLevelWarn category:RLMLogCategoryRealmSync]; return app; } diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index 7d71458d4e..c4f67b6884 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -48,6 +48,60 @@ typedef RLM_CLOSED_ENUM(NSUInteger, RLMLogLevel) { RLMLogLevelAll } NS_SWIFT_NAME(LogLevel); +/** +An enum representing different categories of sync-related logging that can be configured. +Category hierarchy: +``` + Realm + ├─► Storage + │ ├─► Transaction + │ ├─► Query + │ ├─► Object + │ └─► Notification + ├─► Sync + │ ├─► Client + │ │ ├─► Session + │ │ ├─► Changeset + │ │ ├─► Network + │ │ └─► Reset + │ └─► Server + ├─► App + └─► Sdk +``` +*/ +typedef NS_ENUM(NSUInteger, RLMLogCategory) { + /// Top level log category for Realm, updating this category level would set all other subcategories too. + RLMLogCategoryRealm, + /// Log category for all sdk related logs. + RLMLogCategoryRealmSDK, + /// Log category for all app related logs. + RLMLogCategoryRealmApp, + /// Log category for all database related logs. + RLMLogCategoryRealmStorage, + /// Log category for all database transaction related logs. + RLMLogCategoryRealmStorageTransaction, + /// Log category for all database queries related logs. + RLMLogCategoryRealmStorageQuery, + /// Log category for all database object related logs. + RLMLogCategoryRealmStorageObject, + /// Log category for all database notification related logs. + RLMLogCategoryRealmStorageNotification, + /// Log category for all sync related logs. + RLMLogCategoryRealmSync, + /// Log category for all sync client related logs. + RLMLogCategoryRealmSyncClient, + /// Log category for all sync client session related logs. + RLMLogCategoryRealmSyncClientSession, + /// Log category for all sync client changeset related logs. + RLMLogCategoryRealmSyncClientChangeset, + /// Log category for all sync client network related logs. + RLMLogCategoryRealmSyncClientNetwork, + /// Log category for all sync client reset related logs. + RLMLogCategoryRealmSyncClientReset, + /// Log category for all sync server related logs. + RLMLogCategoryRealmSyncServer +}; + /// A log callback function which can be set on RLMLogger. /// /// The log function may be called from multiple threads simultaneously, and is @@ -60,7 +114,7 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); /// The log function may be called from multiple threads simultaneously, and is /// responsible for performing its own synchronization if any is required. RLM_SWIFT_SENDABLE // invoked on a background thread -typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, NSString *category, NSString *message) NS_REFINED_FOR_SWIFT; +typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, RLMLogCategory category, NSString *message) NS_REFINED_FOR_SWIFT; /** `RLMLogger` is used for creating your own custom logging logic. @@ -69,8 +123,8 @@ typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, NSString *category, NS Set this custom logger as you default logger using `setDefaultLogger`. RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - category:RLMLogCategoryRealm - logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { + category:RLMLogCategoryRealm + logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { NSLog(@"Realm Log - %lu, %@, %@", (unsigned long)level, category, message); }]; @@ -104,7 +158,7 @@ __attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); @param logFunction The log function which will be invoked whenever there is a log message. */ - (instancetype)initWithLevel:(RLMLogLevel)level - category:(NSString *)category + category:(RLMLogCategory)category logFunction:(RLMLogCategoryFunction)logFunction; #pragma mark RLMLogger Default Logger API @@ -120,7 +174,7 @@ __attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); @param level The log level to be set for the logger. @param category The log function which will be invoked whenever there is a log message. */ -- (void)setLevel:(RLMLogLevel)level category:(NSString *)category NS_REFINED_FOR_SWIFT; +- (void)setLevel:(RLMLogLevel)level category:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; /** Gets the logger's associated level for the specified category. @@ -128,7 +182,16 @@ __attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); @param category The log category which we need the level. @returns The log level for the specified category */ -- (RLMLogLevel)getLevelForCategory:(NSString *)category NS_REFINED_FOR_SWIFT; +- (RLMLogLevel)getLevelForCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; + +/** + Log a message to the supplied level. + + @param logLevel The log level for the message. + @param category The log category for the message. + @param message The message to log. + */ +- (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message; @end diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index 5fbfaabbc9..b55cfbc9ef 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -22,7 +22,7 @@ #import -typedef void (^RLMLoggerFunction)(RLMLogLevel level, NSString *category, NSString *message); +typedef void (^RLMLoggerFunction)(RLMLogLevel level, RLMLogCategory category, NSString *message); using namespace realm; using Logger = realm::util::Logger; @@ -75,6 +75,57 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { REALM_UNREACHABLE(); // Unrecognized log level. } +static NSArray *categories = [NSArray arrayWithObjects: + @"Realm", + @"Realm.SDK", + @"Realm.App", + @"Realm.Storage", + @"Realm.Storage.Transaction", + @"Realm.Storage.Query", + @"Realm.Storage.Object", + @"Realm.Storage.Notification", + @"Realm.Sync", + @"Realm.Sync.Client", + @"Realm.Sync.Client.Session", + @"Realm.Sync.Client.Changeset", + @"Realm.Sync.Client.Network", + @"Realm.Sync.Client.Reset", + @"Realm.Sync.Server", + nil]; + +static std::string categoryNameForLogCategory(RLMLogCategory logCategory) { + if (logCategory < [categories count]) { + if (auto categoryName = [categories objectAtIndex:logCategory]) { + return categoryName.UTF8String; + } + } + REALM_UNREACHABLE(); +} + +static RLMLogCategory logCategoryForCategoryName(std::string category) { + auto index = [categories indexOfObject:RLMStringDataToNSString(category)]; + if (index != NSNotFound) { + switch (index) { + case 0: return RLMLogCategoryRealm; + case 1: return RLMLogCategoryRealmSDK; + case 2: return RLMLogCategoryRealmApp; + case 3: return RLMLogCategoryRealmStorage; + case 4: return RLMLogCategoryRealmStorageTransaction; + case 5: return RLMLogCategoryRealmStorageQuery; + case 6: return RLMLogCategoryRealmStorageObject; + case 7: return RLMLogCategoryRealmStorageNotification; + case 8: return RLMLogCategoryRealmSync; + case 9: return RLMLogCategoryRealmSyncClient; + case 10: return RLMLogCategoryRealmSyncClientSession; + case 11: return RLMLogCategoryRealmSyncClientChangeset; + case 12: return RLMLogCategoryRealmSyncClientNetwork; + case 13: return RLMLogCategoryRealmSyncClientReset; + case 14: return RLMLogCategoryRealmSyncServer; + } + } + REALM_UNREACHABLE(); +} + struct CocoaLogger : public Logger { void do_log(const LogCategory& category, Level level, const std::string& message) override { NSLog(@"%@:%@ %@", levelPrefix(level), RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); @@ -87,7 +138,7 @@ void do_log(const LogCategory& category, Level level, const std::string& message void do_log(const LogCategory& category, Level level, const std::string& message) override { @autoreleasepool { if (function) { - function(logLevelForLevel(level), RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); + function(logLevelForLevel(level), logCategoryForCategoryName(category.get_name()), RLMStringDataToNSString(message)); } } } @@ -126,7 +177,7 @@ - (instancetype)initWithLevel:(RLMLogLevel)level if (self = [super init]) { auto logger = std::make_shared(); logger->set_level_threshold(levelForLogLevel(level)); - auto block = [logFunction](RLMLogLevel level, NSString *, NSString *message) { + auto block = [logFunction](RLMLogLevel level, RLMLogCategory, NSString *message) { logFunction(level, message); }; logger->function = block; @@ -136,11 +187,11 @@ - (instancetype)initWithLevel:(RLMLogLevel)level } - (instancetype)initWithLevel:(RLMLogLevel)level - category:(NSString *)category + category:(RLMLogCategory)category logFunction:(RLMLogCategoryFunction)logFunction { if (self = [super init]) { auto logger = std::make_shared(); - logger->set_level_threshold(LogCategory::get_category(category.UTF8String), levelForLogLevel(level)); + logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); logger->function = logFunction; self->_logger = logger; } @@ -157,22 +208,28 @@ - (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... { } } -- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message { +- (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message { + auto level = levelForLogLevel(logLevel); + if (_logger->would_log(level)) { + _logger->log(LogCategory::get_category(categoryNameForLogCategory(category)), levelForLogLevel(logLevel), message.UTF8String); + } +} + +- (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName message:(NSString *)message { auto level = levelForLogLevel(logLevel); if (_logger->would_log(level)) { - _logger->log(level, "%1", message.UTF8String); - _logger->log(LogCategory::get_category(category.UTF8String), levelForLogLevel(logLevel), message.UTF8String); + _logger->log(LogCategory::get_category(categoryName.UTF8String), levelForLogLevel(logLevel), message.UTF8String); } } -- (void)setLevel:(RLMLogLevel)level category:(NSString *)category { +- (void)setLevel:(RLMLogLevel)level category:(RLMLogCategory)category { RLMLogger *defaultLogger = [RLMLogger defaultLogger]; - defaultLogger->_logger->set_level_threshold(LogCategory::get_category(category.UTF8String), levelForLogLevel(level)); + defaultLogger->_logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); } -- (RLMLogLevel)getLevelForCategory:(NSString *)category { +- (RLMLogLevel)getLevelForCategory:(RLMLogCategory)category { RLMLogger *defaultLogger = [RLMLogger defaultLogger]; - return logLevelForLevel(defaultLogger->_logger->get_level_threshold(LogCategory::get_category(category.UTF8String))); + return logLevelForLevel(defaultLogger->_logger->get_level_threshold(categoryNameForLogCategory(category))); } #pragma mark Testing @@ -187,6 +244,10 @@ - (RLMLogLevel)getLevelForCategory:(NSString *)category { return a; } ++ (RLMLogCategory)categoryFromString:(NSString *)string { + return logCategoryForCategoryName(string.UTF8String); +} + #pragma mark Global Logger Setter + (instancetype)defaultLogger { diff --git a/Realm/RLMLogger_Private copy.h b/Realm/RLMLogger_Private copy.h new file mode 100644 index 0000000000..654493b77f --- /dev/null +++ b/Realm/RLMLogger_Private copy.h @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// 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 +#import + +RLM_HEADER_AUDIT_BEGIN(nullability) + +@interface RLMLogger() + +/** + Log a message to the supplied level. + + @param logLevel The log level for the message. + @param message The message to log. + */ +- (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... NS_SWIFT_UNAVAILABLE(""); + +/** + Log a message to the supplied level. + + @param logLevel The log level for the message. + @param category The log category for the message. + @param message The message to log. + */ +- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message; + +/** +Gets all the categories from Core. This is to be used for testing purposes only. + */ ++ (NSArray *)getAllCategories; + ++ (RLMLogCategory)categoryFromString:(NSString *)string; +@end + +RLM_HEADER_AUDIT_END(nullability) diff --git a/Realm/RLMLogger_Private.h b/Realm/RLMLogger_Private.h index 2a2ba269dc..7e725b1cb7 100644 --- a/Realm/RLMLogger_Private.h +++ b/Realm/RLMLogger_Private.h @@ -35,15 +35,22 @@ RLM_HEADER_AUDIT_BEGIN(nullability) Log a message to the supplied level. @param logLevel The log level for the message. - @param category The log category for the message. + @param category The log category name for the message. @param message The message to log. */ -- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message; +- (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName message:(NSString *)message; + +#pragma mark Testing /** Gets all the categories from Core. This is to be used for testing purposes only. */ + (NSArray *)getAllCategories; + +/** +Returns a `RLMLogCategory` from a string. + */ ++ (RLMLogCategory)categoryFromString:(NSString *)string; @end RLM_HEADER_AUDIT_END(nullability) diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index 5fd970e92e..55a812c2df 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -2960,8 +2960,8 @@ - (void)tearDown { } - (void)testSetDefaultLogLevel { __block NSMutableString *logs = [[NSMutableString alloc] init]; - NSString *category = @"Realm"; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelAll category:category logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { + RLMLogCategory category = RLMLogCategoryRealm; + RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelAll category:category logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @@ -2981,10 +2981,10 @@ - (void)testSetDefaultLogLevel { - (void)testDefaultLogger { __block NSMutableString *logs = [[NSMutableString alloc] init]; - NSString *category = @"Realm"; + RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelOff category:category - logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { + logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @@ -3011,7 +3011,7 @@ - (void)testDefaultLogger { // Init Custom Logger RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug category:category - logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { + logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; @@ -3023,10 +3023,10 @@ - (void)testDefaultLogger { - (void)testCustomLoggerLogMessage { __block NSMutableString *logs = [[NSMutableString alloc] init]; - NSString *category = @"Realm"; + RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelInfo category:category - logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { + logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @@ -3036,6 +3036,14 @@ - (void)testCustomLoggerLogMessage { XCTAssertTrue([logs containsString:@"TEST: IMPORTANT INFO 0"]); // Detail XCTAssertFalse([logs containsString:@"IMPORTANT TRACE"]); // Trace } + +// Core defines the different categories in runtime, forcing the SDK to define the categories again. +// This test validates that we have added new defined categories to the RLMLogCategory enum. +- (void)testAllCategoriesWatchDog { + for (id category in [RLMLogger getAllCategories]) { + XCTAssertNoThrow([RLMLogger categoryFromString:category]); + } +} @end @interface RLMMetricsTests : RLMTestCase @@ -3052,10 +3060,10 @@ - (void)tearDown { - (void)testSyncConnectionMetrics { __block NSMutableString *logs = [[NSMutableString alloc] init]; - NSString *category = @"Realm"; + RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug category:category - logFunction:^(RLMLogLevel level, NSString *category, NSString * message) { + logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index 0a0acfee54..b6c59632a2 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -77,8 +77,8 @@ extension Logger { - SeeAlso: `LogCategory` */ public convenience init(level: LogLevel, category: LogCategory = Category.realm, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { - self.init(level: level, category: category.toString()) { level, cat, message in - function(level, Category.fromString(cat)!, message) + self.init(level: level, category: ObjectiveCSupport.convert(value: category)) { level, cat, message in + function(level, ObjectiveCSupport.convert(value: cat), message) } } @@ -92,7 +92,7 @@ extension Logger { - SeeAlso: `LogCategory` */ public func setLogLevel(_ level: LogLevel, for category: LogCategory = Category.realm) { - Logger.shared.__setLevel(level, category: category.toString()) + Logger.shared.__setLevel(level, category: ObjectiveCSupport.convert(value: category)) } /** @@ -104,7 +104,7 @@ extension Logger { - SeeAlso: `LogCategory` */ public func getLogLevel(for category: LogCategory) -> LogLevel { - Logger.shared.__getLevelForCategory(category.toString()) + Logger.shared.__getLevelFor(ObjectiveCSupport.convert(value: category)) } } @@ -273,3 +273,86 @@ public enum Category: String, LogCategory { } } } + +private extension ObjectiveCSupport { + + /// Converts a Swift category `LogCategory` to an Objective-C `RLMLogCategory. + /// - Parameter value: The `LogCategory`. + /// - Returns: Conversion of `value` to its Objective-C representation. + static func convert(value: LogCategory) -> RLMLogCategory { + switch value { + case Category.realm: + return RLMLogCategory.realm + case Category.sdk: + return RLMLogCategory.realmSDK + case Category.app: + return RLMLogCategory.realmApp + case Category.Storage.all: + return RLMLogCategory.realmStorage + case Category.Storage.transaction: + return RLMLogCategory.realmStorageTransaction + case Category.Storage.query: + return RLMLogCategory.realmStorageQuery + case Category.Storage.object: + return RLMLogCategory.realmStorageObject + case Category.Storage.notification: + return RLMLogCategory.realmStorageNotification + case Category.Sync.all: + return RLMLogCategory.realmSync + case Category.Sync.Client.all: + return RLMLogCategory.realmSyncClient + case Category.Sync.Client.session: + return RLMLogCategory.realmSyncClientSession + case Category.Sync.Client.changeset: + return RLMLogCategory.realmSyncClientChangeset + case Category.Sync.Client.network: + return RLMLogCategory.realmSyncClientNetwork + case Category.Sync.Client.reset: + return RLMLogCategory.realmSyncClientReset + case Category.Sync.server: + return RLMLogCategory.realmSyncServer + default: + throwRealmException("") + } + } + + /// Converts an Objective-C category `RLMLogCategory` to a Swift `LogCategory. + /// - Parameter value: The `RLMLogCategory`. + /// - Returns: Conversion of `value` to its Swift representation. + static func convert(value: RLMLogCategory) -> LogCategory { + switch value { + case RLMLogCategory.realm: + return Category.realm + case RLMLogCategory.realmSDK: + return Category.sdk + case RLMLogCategory.realmApp: + return Category.app + case RLMLogCategory.realmStorage: + return Category.Storage.all + case RLMLogCategory.realmStorageTransaction: + return Category.Storage.transaction + case RLMLogCategory.realmStorageQuery: + return Category.Storage.query + case RLMLogCategory.realmStorageObject: + return Category.Storage.object + case RLMLogCategory.realmStorageNotification: + return Category.Storage.notification + case RLMLogCategory.realmSync: + return Category.Sync.all + case RLMLogCategory.realmSyncClient: + return Category.Sync.Client.all + case RLMLogCategory.realmSyncClientSession: + return Category.Sync.Client.session + case RLMLogCategory.realmSyncClientChangeset: + return Category.Sync.Client.changeset + case RLMLogCategory.realmSyncClientNetwork: + return Category.Sync.Client.network + case RLMLogCategory.realmSyncClientReset: + return Category.Sync.Client.reset + case RLMLogCategory.realmSyncServer: + return Category.Sync.server + default: + throwRealmException("") + } + } +} From 1d4f22f1b06710e880fdd509861abce83c713660 Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Fri, 31 May 2024 15:39:03 +0200 Subject: [PATCH 3/8] Solve PR comments --- Realm/RLMLogger.h | 2 +- Realm/RLMLogger.mm | 10 ++-- Realm/RLMLogger_Private copy.h | 51 ------------------ Realm/RLMLogger_Private.h | 2 +- Realm/RLMSyncManager.mm | 2 +- Realm/Tests/RealmTests.mm | 10 ++-- RealmSwift/Logger.swift | 57 +++----------------- RealmSwift/Tests/RealmTests.swift | 90 +++++++++++++++---------------- 8 files changed, 65 insertions(+), 159 deletions(-) delete mode 100644 Realm/RLMLogger_Private copy.h diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index c4f67b6884..9710740427 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -182,7 +182,7 @@ __attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); @param category The log category which we need the level. @returns The log level for the specified category */ -- (RLMLogLevel)getLevelForCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; +- (RLMLogLevel)levelForCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; /** Log a message to the supplied level. diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index b55cfbc9ef..ebefa602c2 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -223,18 +223,16 @@ - (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName } - (void)setLevel:(RLMLogLevel)level category:(RLMLogCategory)category { - RLMLogger *defaultLogger = [RLMLogger defaultLogger]; - defaultLogger->_logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); + _logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); } -- (RLMLogLevel)getLevelForCategory:(RLMLogCategory)category { - RLMLogger *defaultLogger = [RLMLogger defaultLogger]; - return logLevelForLevel(defaultLogger->_logger->get_level_threshold(categoryNameForLogCategory(category))); +- (RLMLogLevel)levelForCategory:(RLMLogCategory)category { + return logLevelForLevel(_logger->get_level_threshold(categoryNameForLogCategory(category))); } #pragma mark Testing -+ (NSArray *)getAllCategories { ++ (NSArray *)allCategories { NSMutableArray *a = [NSMutableArray new]; auto categories = LogCategory::get_category_names(); for (const auto& category : categories) { diff --git a/Realm/RLMLogger_Private copy.h b/Realm/RLMLogger_Private copy.h deleted file mode 100644 index 654493b77f..0000000000 --- a/Realm/RLMLogger_Private copy.h +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 -#import - -RLM_HEADER_AUDIT_BEGIN(nullability) - -@interface RLMLogger() - -/** - Log a message to the supplied level. - - @param logLevel The log level for the message. - @param message The message to log. - */ -- (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... NS_SWIFT_UNAVAILABLE(""); - -/** - Log a message to the supplied level. - - @param logLevel The log level for the message. - @param category The log category for the message. - @param message The message to log. - */ -- (void)logWithLevel:(RLMLogLevel)logLevel category:(NSString *)category message:(NSString *)message; - -/** -Gets all the categories from Core. This is to be used for testing purposes only. - */ -+ (NSArray *)getAllCategories; - -+ (RLMLogCategory)categoryFromString:(NSString *)string; -@end - -RLM_HEADER_AUDIT_END(nullability) diff --git a/Realm/RLMLogger_Private.h b/Realm/RLMLogger_Private.h index 7e725b1cb7..be8af0c0c9 100644 --- a/Realm/RLMLogger_Private.h +++ b/Realm/RLMLogger_Private.h @@ -45,7 +45,7 @@ RLM_HEADER_AUDIT_BEGIN(nullability) /** Gets all the categories from Core. This is to be used for testing purposes only. */ -+ (NSArray *)getAllCategories; ++ (NSArray *)allCategories; /** Returns a `RLMLogCategory` from a string. diff --git a/Realm/RLMSyncManager.mm b/Realm/RLMSyncManager.mm index 032a04d47a..b5acbfa202 100644 --- a/Realm/RLMSyncManager.mm +++ b/Realm/RLMSyncManager.mm @@ -75,7 +75,7 @@ RLMSyncLogLevel logLevelForLevel(Level logLevel) { struct CocoaSyncLogger : public realm::util::Logger { void do_log(const realm::util::LogCategory& category, Level, const std::string& message) override { - NSLog(@"%@: %@", RLMStringDataToNSString(category.get_name()), RLMStringDataToNSString(message)); + NSLog(@"%s: %s", category.get_name().c_str(), message.c_str()); } }; diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index 55a812c2df..6f2dde2459 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -2967,14 +2967,14 @@ - (void)testSetDefaultLogLevel { RLMLogger.defaultLogger = logger; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelAll); + XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelAll); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertTrue([logs containsString:@"7 DB:"]); // Trace [logs setString: @""]; [logger setLevel:RLMLogLevelDetail category:category]; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelDetail); + XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelDetail); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace } @@ -2988,7 +2988,7 @@ - (void)testDefaultLogger { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; - XCTAssertEqual([[RLMLogger defaultLogger] getLevelForCategory:category], RLMLogLevelOff); + XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelOff); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] == 0); @@ -3015,7 +3015,7 @@ - (void)testDefaultLogger { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; - XCTAssertEqual([RLMLogger.defaultLogger getLevelForCategory:category], RLMLogLevelDebug); + XCTAssertEqual([RLMLogger.defaultLogger levelForCategory:category], RLMLogLevelDebug); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace @@ -3040,7 +3040,7 @@ - (void)testCustomLoggerLogMessage { // Core defines the different categories in runtime, forcing the SDK to define the categories again. // This test validates that we have added new defined categories to the RLMLogCategory enum. - (void)testAllCategoriesWatchDog { - for (id category in [RLMLogger getAllCategories]) { + for (id category in [RLMLogger allCategories]) { XCTAssertNoThrow([RLMLogger categoryFromString:category]); } } diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index b6c59632a2..f101a45ff7 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -27,7 +27,7 @@ import Realm.Private ```swift let logger = Logger(level: .all, category: Category.realm) { level, message in - print("Realm Log - \(category.toString())-\(level): \(message)") + print("Realm Log - \(category.rawValue)-\(level): \(message)") } ``` @@ -56,7 +56,7 @@ extension Logger { - parameter message: The message to log. */ internal func log(level: LogLevel, category: LogCategory = Category.realm, message: String) { - self.log(with: level, category: category.toString(), message: message) + self.log(with: level, category: ObjectiveCSupport.convert(value: category), message: message) } /** @@ -64,9 +64,8 @@ extension Logger { ```swift let logger = Logger(level: .info, category: Category.All, logFunction: { level, category, message in - print("\(category.toString()) - \(level): \(message)") + print("\(category.rawValue) - \(level): \(message)") }) - logger.log(level: .info, category: Category.realm, message: "Info DB: Database opened succesfully") ``` - parameter level: The log level to be set for the logger. @@ -103,8 +102,8 @@ extension Logger { - returns: The `LogLevel` for the given category. - SeeAlso: `LogCategory` */ - public func getLogLevel(for category: LogCategory) -> LogLevel { - Logger.shared.__getLevelFor(ObjectiveCSupport.convert(value: category)) + public func logLevel(for category: LogCategory) -> LogLevel { + Logger.shared.__level(for: ObjectiveCSupport.convert(value: category)) } } @@ -116,7 +115,7 @@ public protocol LogCategory: Sendable { - returns: A string representing the log category. - SeeAlso: `LogCategory` */ - func toString() -> String + var rawValue: String { get } } /** @@ -147,16 +146,6 @@ public enum Category: String, LogCategory { /// Log category for all app related logs. case app = "Realm.App" - /** - Returns the string represtation of the Log category. - - - returns: A string representing the log category. - - SeeAlso: `LogCategory` - */ - public func toString() -> String { - return self.rawValue - } - /// :nodoc: fileprivate static func fromString(_ string: String) -> LogCategory? { if let category = Category(rawValue: string) { @@ -195,16 +184,6 @@ public enum Category: String, LogCategory { case object = "Realm.Storage.Object" /// Log category for all database notification related logs. case notification = "Realm.Storage.Notification" - - /** - Returns the string represtation of the Log category. - - - returns: A string representing the log category. - - SeeAlso: `LogCategory` - */ - public func toString() -> String { - return self.rawValue - } } /** @@ -227,16 +206,6 @@ public enum Category: String, LogCategory { /// Log category for all sync server related logs. case server = "Realm.Sync.Server" - /** - Returns the string represtation of the Log category. - - - returns: A string representing the log category. - - SeeAlso: `LogCategory` - */ - public func toString() -> String { - return self.rawValue - } - /** Log category for all storage related logs. @@ -260,16 +229,6 @@ public enum Category: String, LogCategory { case network = "Realm.Sync.Client.Network" /// Log category for all sync client reset related logs. case reset = "Realm.Sync.Client.Reset" - - /** - Returns the string represtation of the Log category. - - - returns: A string representing the log category. - - SeeAlso: `LogCategory` - */ - public func toString() -> String { - return self.rawValue - } } } } @@ -312,7 +271,7 @@ private extension ObjectiveCSupport { case Category.Sync.server: return RLMLogCategory.realmSyncServer default: - throwRealmException("") + fatalError() } } @@ -352,7 +311,7 @@ private extension ObjectiveCSupport { case RLMLogCategory.realmSyncServer: return Category.Sync.server default: - throwRealmException("") + fatalError() } } } diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index 2a6a5e35af..d304ec3adc 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -1967,7 +1967,7 @@ class LoggerTests: TestCase, @unchecked Sendable { logger.setLogLevel(.all) try autoreleasepool { _ = try Realm() } // We should be getting logs after changing the log level - XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .all) + XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .all) XCTAssertTrue(logs.value.contains("Details DB:")) XCTAssertTrue(logs.value.contains("Trace DB:")) } @@ -1979,7 +1979,7 @@ class LoggerTests: TestCase, @unchecked Sendable { } Logger.shared = logger - XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .off) + XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .off) try autoreleasepool { _ = try Realm() } XCTAssertTrue(logs.value.isEmpty) @@ -2011,7 +2011,7 @@ class LoggerTests: TestCase, @unchecked Sendable { Logger.shared = Logger(level: .trace) { level, _, message in logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) } - XCTAssertEqual(Logger.shared.getLogLevel(for: Category.realm), .trace) + XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .trace) try autoreleasepool { _ = try Realm() } XCTAssertTrue(!logs.value.isEmpty) XCTAssertTrue(logs.value.contains("Details DB:")) @@ -2022,42 +2022,42 @@ class LoggerTests: TestCase, @unchecked Sendable { // This test validates that we have added new defined categories to the Categories enum and/or // child categories func testAllCategoriesWatchDog() throws { - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { XCTAssertNotNil(categoryfromString(category), "LogCategory `\(category)` not added to the Category enum.") - XCTAssertEqual(categoryfromString(category)?.toString(), category) + XCTAssertEqual(categoryfromString(category)?.rawValue, category) } } func testLogLevelForCategories() throws { Logger.shared.setLogLevel(.off) - XCTAssertEqual(logger.getLogLevel(for: Category.realm), .off) + XCTAssertEqual(logger.logLevel(for: Category.realm), .off) - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) - XCTAssertNotEqual(logger.getLogLevel(for: categoryEnum!), .all) + XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) + XCTAssertNotEqual(logger.logLevel(for: categoryEnum!), .all) } } func testLogMessageCategory() throws { let logs = Locked("") let logger = Logger(level: .off) { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) } Logger.shared = logger - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { logs.wrappedValue = "" let categoryEnum = categoryfromString(category) XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) - logger.log(with: .trace, category: category, message: "Test") + XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) + logger.log(with: .trace, categoryName: category, message: "Test") XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \(category)") } } @@ -2066,11 +2066,11 @@ class LoggerTests: TestCase, @unchecked Sendable { func testNotLogParentOrRelatedCategory() throws { let logs = Locked("") let logger = Logger(level: .off) { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) } Logger.shared = logger - let categories = Logger.getAllCategories() + let categories = Logger.allCategories() for (index, category) in categories.enumerated() { guard index <= categories.count-2 else { return } logs.wrappedValue = "" @@ -2078,9 +2078,9 @@ class LoggerTests: TestCase, @unchecked Sendable { XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) - logger.log(with: .trace, category: category, message: "Test") + logger.log(with: .trace, categoryName: category, message: "Test") XCTAssertFalse(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log shouldn't contain \(categories[index+1])") } } @@ -2089,67 +2089,67 @@ class LoggerTests: TestCase, @unchecked Sendable { func testShouldLogWhenParentCategory() throws { let logs = Locked("") let logger = Logger(level: .off) { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) } Logger.shared = logger logger.setLogLevel(.trace, for: Category.realm) - XCTAssertEqual(logger.getLogLevel(for: Category.realm), .trace) + XCTAssertEqual(logger.logLevel(for: Category.realm), .trace) - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { logs.wrappedValue = "" - logger.log(with: .trace, category: category, message: "Test") - XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \( Category.realm.toString())") + logger.log(with: .trace, categoryName: category, message: "Test") + XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \( Category.realm.rawValue)") } } func testChangeCategoryLevel() throws { let logs = Locked("") let logger = Logger(level: .off) { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) } Logger.shared = logger logger.setLogLevel(.trace, for: Category.realm) - XCTAssertEqual(logger.getLogLevel(for: Category.realm), .trace) + XCTAssertEqual(logger.logLevel(for: Category.realm), .trace) - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) - XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .trace) + XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) } logger.setLogLevel(.all, for: Category.realm) - XCTAssertEqual(logger.getLogLevel(for: Category.realm), .all) + XCTAssertEqual(logger.logLevel(for: Category.realm), .all) - for category in Logger.getAllCategories() { + for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) - XCTAssertEqual(logger.getLogLevel(for: categoryEnum!), .all) + XCTAssertEqual(logger.logLevel(for: categoryEnum!), .all) } } func testChangeSubCategoryLevel() throws { let logs = Locked("") let logger = Logger(level: .off) { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.toString()) \(message) " }) + logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) } Logger.shared = logger - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.all), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.transaction), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.query), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.object), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.notification), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.all), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.transaction), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.query), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.object), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.notification), .off) logger.setLogLevel(.info, for: Category.Storage.all) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.all), .info) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.transaction), .info) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.query), .info) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.object), .info) - XCTAssertEqual(logger.getLogLevel(for: Category.Storage.notification), .info) - - XCTAssertEqual(logger.getLogLevel(for: Category.realm), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.sdk), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.app), .off) - XCTAssertEqual(logger.getLogLevel(for: Category.Sync.all), .off) + XCTAssertEqual(logger.logLevel(for: Category.Storage.all), .info) + XCTAssertEqual(logger.logLevel(for: Category.Storage.transaction), .info) + XCTAssertEqual(logger.logLevel(for: Category.Storage.query), .info) + XCTAssertEqual(logger.logLevel(for: Category.Storage.object), .info) + XCTAssertEqual(logger.logLevel(for: Category.Storage.notification), .info) + + XCTAssertEqual(logger.logLevel(for: Category.realm), .off) + XCTAssertEqual(logger.logLevel(for: Category.sdk), .off) + XCTAssertEqual(logger.logLevel(for: Category.app), .off) + XCTAssertEqual(logger.logLevel(for: Category.Sync.all), .off) } func categoryfromString(_ string: String) -> LogCategory? { From a301e20b6de3000a172d7bd31e52dae5f2f651b8 Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Fri, 14 Jun 2024 20:44:32 +0200 Subject: [PATCH 4/8] Solve PR comments --- CHANGELOG.md | 5 +- Realm/ObjectServerTests/RLMSyncTestCase.mm | 2 +- Realm/RLMLogger.h | 49 ++++--- Realm/RLMLogger.mm | 33 +++-- Realm/Tests/RealmTests.mm | 36 +++--- RealmSwift/Logger.swift | 69 +++++++--- RealmSwift/Tests/RealmTests.swift | 143 ++++++++++++--------- 7 files changed, 194 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b92a65b7df..da170c3ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ x.y.z Release notes (yyyy-MM-dd) * Added support for filtering logs by category. Users wil have more fine grained control over the log level for each category as well. ```swift - Logger.shared.setLogLevel(.info, category: Category.Storage.transactions) + Logger.setLogLevel(.info, category: Category.Storage.transactions) ``` ### Fixed @@ -14,7 +14,8 @@ x.y.z Release notes (yyyy-MM-dd) * None. ### Deprecations -* `RLMLogger.level`/`Logger.level` has been deprecated in favor of using `RLMLogger.setLevel:category:`/`Logger.setLevel(:category:)` and `RLMLogger.getLevelForCategory:`/`Logger.getLevel(for:)`. +* `RLMLogger.level`/`Logger.level` has been deprecated in favor of using `RLMLogger.setLevel:forCategory:`/`Logger.setLevel(:category:)` and `RLMLogger.getLevelForCategory:`/`Logger.getLevel(for:)`. +* Is not recommended to initialise a `RLMLogger/Logger` with a level anymore. ### Compatibility * Realm Studio: 15.0.0 or later. diff --git a/Realm/ObjectServerTests/RLMSyncTestCase.mm b/Realm/ObjectServerTests/RLMSyncTestCase.mm index 40e97ec060..9dd55fee88 100644 --- a/Realm/ObjectServerTests/RLMSyncTestCase.mm +++ b/Realm/ObjectServerTests/RLMSyncTestCase.mm @@ -613,7 +613,7 @@ - (RLMApp *)appWithId:(NSString *)appId { RLMApp *app = [RLMApp appWithConfiguration:config]; RLMSyncManager *syncManager = app.syncManager; syncManager.userAgent = self.name; - [RLMLogger.defaultLogger setLevel:RLMLogLevelWarn category:RLMLogCategoryRealmSync]; + [RLMLogger setLevel:RLMLogLevelWarn forCategory:RLMLogCategoryRealmSync]; return app; } diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index 9710740427..e2806fc6bb 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -116,15 +116,13 @@ typedef void (^RLMLogFunction)(RLMLogLevel level, NSString *message); RLM_SWIFT_SENDABLE // invoked on a background thread typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, RLMLogCategory category, NSString *message) NS_REFINED_FOR_SWIFT; /** - `RLMLogger` is used for creating your own custom logging logic. + Global logger class used by all Realm components. You can define your own logger creating an instance of `RLMLogger` and define the log function which will be invoked whenever there is a log message. Set this custom logger as you default logger using `setDefaultLogger`. - RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - category:RLMLogCategoryRealm - logFunction:^(RLMLogLevel level, NSString *category, NSString *message) { + RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, NSString *category, NSString *message) { NSLog(@"Realm Log - %lu, %@, %@", (unsigned long)level, category, message); }]; @@ -146,52 +144,53 @@ __attribute__((deprecated("Use `setLevel(level:category)` or `setLevel:category` @param level The log level to be set for the logger. @param logFunction The log function which will be invoked whenever there is a log message. + + @note This will set the log level for the log category `RLMLogCategoryRealm`. */ - (instancetype)initWithLevel:(RLMLogLevel)level logFunction:(RLMLogFunction)logFunction -__attribute__((deprecated("Use `initWithLevel:logFunction:` instead."))); +__attribute__((deprecated("Use `initWithLogFunction:` instead."))); /** - Creates a logger with the associated log level and the logic function to define your own logging logic. + Creates a logger with a callback, which will be invoked whenever there is a log message. - @param level The log level to be set for the logger. - @param category The log category to be set for the logger. @param logFunction The log function which will be invoked whenever there is a log message. */ -- (instancetype)initWithLevel:(RLMLogLevel)level - category:(RLMLogCategory)category - logFunction:(RLMLogCategoryFunction)logFunction; +- (instancetype)initWithLogFunction:(RLMLogCategoryFunction)logFunction; #pragma mark RLMLogger Default Logger API /** - The current default logger. When setting a logger as default, this logger will be used whenever information must be logged. + The current default logger. When setting a logger as default, this logger will replace the current default logger and will + be used whenever information must be logged. + + @note By default the logger */ @property (class) RLMLogger *defaultLogger NS_SWIFT_NAME(shared); /** - Sets the logger's associated log and category. + Log a message to the supplied level. + + @param logLevel The log level for the message. + @param category The log category for the message. + @param message The message to log. + */ +- (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message; + +/** + Sets the gobal log level for a given category. @param level The log level to be set for the logger. @param category The log function which will be invoked whenever there is a log message. */ -- (void)setLevel:(RLMLogLevel)level category:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; ++ (void)setLevel:(RLMLogLevel)level forCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; /** - Gets the logger's associated level for the specified category. + Gets the global log level for the specified category. @param category The log category which we need the level. @returns The log level for the specified category */ -- (RLMLogLevel)levelForCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; - -/** - Log a message to the supplied level. - - @param logLevel The log level for the message. - @param category The log category for the message. - @param message The message to log. - */ -- (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message; ++ (RLMLogLevel)levelForCategory:(RLMLogCategory)category NS_REFINED_FOR_SWIFT; @end diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index ebefa602c2..56dee6d8f6 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -161,7 +161,7 @@ - (void)setLevel:(RLMLogLevel)level { + (void)initialize { auto defaultLogger = std::make_shared(); - defaultLogger->set_level_threshold(Level::info); + defaultLogger->set_level_threshold(LogCategory::realm, Level::info); Logger::set_default_logger(defaultLogger); } @@ -186,13 +186,13 @@ - (instancetype)initWithLevel:(RLMLogLevel)level return self; } -- (instancetype)initWithLevel:(RLMLogLevel)level - category:(RLMLogCategory)category - logFunction:(RLMLogCategoryFunction)logFunction { +- (instancetype)initWithLogFunction:(RLMLogCategoryFunction)logFunction { if (self = [super init]) { auto logger = std::make_shared(); - logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); - logger->function = logFunction; + auto block = [logFunction](RLMLogLevel level, RLMLogCategory category, NSString *message) { + logFunction(level, category, message); + }; + logger->function = block; self->_logger = logger; } return self; @@ -210,24 +210,29 @@ - (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... { - (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message { auto level = levelForLogLevel(logLevel); - if (_logger->would_log(level)) { - _logger->log(LogCategory::get_category(categoryNameForLogCategory(category)), levelForLogLevel(logLevel), message.UTF8String); + auto cat = categoryNameForLogCategory(category); + LogCategory& lcat = LogCategory::get_category(cat); + if (_logger->would_log(lcat, level)) { + _logger->log(lcat, levelForLogLevel(logLevel), message.UTF8String); } } - (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName message:(NSString *)message { auto level = levelForLogLevel(logLevel); - if (_logger->would_log(level)) { - _logger->log(LogCategory::get_category(categoryName.UTF8String), levelForLogLevel(logLevel), message.UTF8String); + LogCategory& lcat = LogCategory::get_category(categoryName.UTF8String); + if (_logger->would_log(lcat, level)) { + _logger->log(lcat, levelForLogLevel(logLevel), message.UTF8String); } } -- (void)setLevel:(RLMLogLevel)level category:(RLMLogCategory)category { - _logger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); ++ (void)setLevel:(RLMLogLevel)level forCategory:(RLMLogCategory)category { + auto defaultLogger = Logger::get_default_logger(); + defaultLogger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); } -- (RLMLogLevel)levelForCategory:(RLMLogCategory)category { - return logLevelForLevel(_logger->get_level_threshold(categoryNameForLogCategory(category))); ++ (RLMLogLevel)levelForCategory:(RLMLogCategory)category { + auto defaultLogger = Logger::get_default_logger(); + return logLevelForLevel(defaultLogger->get_level_threshold(categoryNameForLogCategory(category))); } #pragma mark Testing diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index 6f2dde2459..8411f5769e 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -2961,20 +2961,21 @@ - (void)tearDown { - (void)testSetDefaultLogLevel { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelAll category:category logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { + RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; + [RLMLogger setLevel:RLMLogLevelAll forCategory:category]; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelAll); + XCTAssertEqual([RLMLogger levelForCategory:category], RLMLogLevelAll); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertTrue([logs containsString:@"7 DB:"]); // Trace [logs setString: @""]; - [logger setLevel:RLMLogLevelDetail category:category]; + [RLMLogger setLevel:RLMLogLevelDetail forCategory:category]; @autoreleasepool { [RLMRealm defaultRealm]; } - XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelDetail); + XCTAssertEqual([RLMLogger levelForCategory:category], RLMLogLevelDetail); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace } @@ -2982,26 +2983,25 @@ - (void)testSetDefaultLogLevel { - (void)testDefaultLogger { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelOff - category:category - logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { + RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; - XCTAssertEqual([[RLMLogger defaultLogger] levelForCategory:category], RLMLogLevelOff); + [RLMLogger setLevel:RLMLogLevelOff forCategory:category]; + XCTAssertEqual([RLMLogger levelForCategory:category], RLMLogLevelOff); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] == 0); // Test LogLevel Detail - [logger setLevel:RLMLogLevelDetail category:category]; + [RLMLogger setLevel:RLMLogLevelDetail forCategory:category]; @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] > 0); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace // Test LogLevel All - [logger setLevel:RLMLogLevelAll category:category]; + [RLMLogger setLevel:RLMLogLevelAll forCategory:category]; @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs length] > 0); XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail @@ -3009,13 +3009,11 @@ - (void)testDefaultLogger { [logs setString: @""]; // Init Custom Logger - RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - category:category - logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { + RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; }]; - - XCTAssertEqual([RLMLogger.defaultLogger levelForCategory:category], RLMLogLevelDebug); + [RLMLogger setLevel:RLMLogLevelDebug forCategory:category]; + XCTAssertEqual([RLMLogger levelForCategory:category], RLMLogLevelDebug); @autoreleasepool { [RLMRealm defaultRealm]; } XCTAssertTrue([logs containsString:@"5 DB:"]); // Detail XCTAssertFalse([logs containsString:@"7 DB:"]); // Trace @@ -3024,9 +3022,7 @@ - (void)testDefaultLogger { - (void)testCustomLoggerLogMessage { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelInfo - category:category - logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { + RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; @@ -3061,9 +3057,7 @@ - (void)tearDown { - (void)testSyncConnectionMetrics { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; - RLMLogger *logger = [[RLMLogger alloc] initWithLevel:RLMLogLevelDebug - category:category - logFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { + RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; }]; RLMLogger.defaultLogger = logger; diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index f101a45ff7..ebf1c4d934 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -20,24 +20,36 @@ import Realm import Realm.Private /** - `Logger` is used for creating your own custom logging logic. + Global logger class used by all Realm components. + + Set the global log level for a given category. + ```swift + Logger.setLogLevel(.info, for: Category.sdk) + ``` + + Read the global log level for a given category. + ```swift + let level = Logger.logLevel(for: Category.Storage.all) + ``` You can define your own logger creating an instance of `Logger` and define the log function which will be invoked whenever there is a log message. ```swift - let logger = Logger(level: .all, category: Category.realm) { level, message in + let logger = Logger(function: { level, message in print("Realm Log - \(category.rawValue)-\(level): \(message)") - } + }) ``` - Set this custom logger as you default logger using `Logger.shared`. + Set this custom logger as you default logger using `Logger.shared`. This will replace the default logger. ```swift - Logger.shared = inMemoryLogger + Logger.shared = logger ``` - - note: By default default log threshold level is `.info`, and logging strings are output to Apple System Logger. + - note: By default log threshold level is `.info`, for the log category `.Category.realm`, + and logging strings are output to Apple System Logger. + - SeeAlso: `LogCategory` */ public typealias Logger = RLMLogger extension Logger { @@ -60,7 +72,7 @@ extension Logger { } /** - Creates a logger with the associated log level, category and the logic function to define your own logging logic. + Creates a logger with the associated log level, and a logic function to define your own logging logic. ```swift let logger = Logger(level: .info, category: Category.All, logFunction: { level, category, message in @@ -69,20 +81,37 @@ extension Logger { ``` - parameter level: The log level to be set for the logger. - - parameter category: The log category to be set for the logger, by default it will setup the top Category `Category.realm` - parameter function: The log function which will be invoked whenever there is a log message. - - note:By setting the log level of a category, it will set all its subcategories log level as well. - - SeeAlso: `LogCategory` + - note: This will set the log level for the log category `Category.realm`. */ - public convenience init(level: LogLevel, category: LogCategory = Category.realm, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { - self.init(level: level, category: ObjectiveCSupport.convert(value: category)) { level, cat, message in - function(level, ObjectiveCSupport.convert(value: cat), message) - } + @available(*, deprecated, message: "Use init(function:)") + public convenience init(level: LogLevel, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { + self.init(logFunction: { level, category, message in + function(level, Category.realm, message) + }) + Logger.setLogLevel(level, for: Category.realm) + } + + /** + Creates a logger with a callback, which will be invoked whenever there is a log message. + + ```swift + let logger = Logger(function: { level, category, message in + print("\(category.rawValue) - \(level): \(message)") + }) + ``` + + - parameter function: The log function which will be invoked whenever there is a log message. + */ + public convenience init(function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { + self.init(logFunction: { level, category, message in + function(level, ObjectiveCSupport.convert(value: category), message) + }) } /** - Sets the log level for a given log category. + Sets the global log level for a given log category. - parameter level: The log level to be set for the logger. - parameter category: The log category to be set for the logger, by default it will setup the top Category `Category.realm` @@ -90,20 +119,20 @@ extension Logger { - note:By setting the log level of a category, it will set all its subcategories log level as well. - SeeAlso: `LogCategory` */ - public func setLogLevel(_ level: LogLevel, for category: LogCategory = Category.realm) { - Logger.shared.__setLevel(level, category: ObjectiveCSupport.convert(value: category)) + public static func setLogLevel(_ level: LogLevel, for category: LogCategory = Category.realm) { + Logger.__setLevel(level, for: ObjectiveCSupport.convert(value: category)) } /** - Gets the current log level of a log category. + Gets the current global log level of a log category. - parameter category: The target log category. - returns: The `LogLevel` for the given category. - SeeAlso: `LogCategory` */ - public func logLevel(for category: LogCategory) -> LogLevel { - Logger.shared.__level(for: ObjectiveCSupport.convert(value: category)) + public static func logLevel(for category: LogCategory) -> LogLevel { + Logger.__level(for: ObjectiveCSupport.convert(value: category)) } } diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index d304ec3adc..ced350fd36 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -1957,34 +1957,35 @@ class LoggerTests: TestCase, @unchecked Sendable { func testSetDefaultLogLevel() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, _, message in - logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) - } + let logger = Logger(function: { level, category, message in + logs.withLock({ $0 += "\(Date.now) \(category.rawValue):\(level.logLevel) \(message)" }) + }) Logger.shared = logger + Logger.setLogLevel(.off, for: Category.realm) try autoreleasepool { _ = try Realm() } XCTAssertTrue(logs.value.isEmpty) - logger.setLogLevel(.all) + Logger.setLogLevel(.all, for: Category.realm) try autoreleasepool { _ = try Realm() } // We should be getting logs after changing the log level - XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .all) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .all) XCTAssertTrue(logs.value.contains("Details DB:")) XCTAssertTrue(logs.value.contains("Trace DB:")) } func testDefaultLogger() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, _, message in - logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) - } + let logger = Logger(function: { level, category, message in + logs.withLock({ $0 += "\(Date.now) \(category.rawValue):\(level.logLevel) \(message)" }) + }) Logger.shared = logger - - XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .off) + Logger.setLogLevel(.off, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .off) try autoreleasepool { _ = try Realm() } XCTAssertTrue(logs.value.isEmpty) // Info - logger.setLogLevel(.detail) + Logger.setLogLevel(.detail, for: Category.realm) try autoreleasepool { _ = try Realm() } XCTAssertTrue(!logs.value.isEmpty) @@ -1992,7 +1993,7 @@ class LoggerTests: TestCase, @unchecked Sendable { // Trace logs.wrappedValue = "" - logger.setLogLevel(.trace) + Logger.setLogLevel(.trace, for: Category.realm) try autoreleasepool { _ = try Realm() } XCTAssertTrue(!logs.value.isEmpty) @@ -2000,7 +2001,7 @@ class LoggerTests: TestCase, @unchecked Sendable { // Detail logs.wrappedValue = "" - logger.setLogLevel(.detail) + Logger.setLogLevel(.detail, for: Category.realm) try autoreleasepool { _ = try Realm() } XCTAssertTrue(!logs.value.isEmpty) @@ -2008,10 +2009,11 @@ class LoggerTests: TestCase, @unchecked Sendable { XCTAssertFalse(logs.value.contains("Trace DB:")) logs.wrappedValue = "" - Logger.shared = Logger(level: .trace) { level, _, message in + Logger.shared = Logger(function: { level, _, message in logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) - } - XCTAssertEqual(Logger.shared.logLevel(for: Category.realm), .trace) + }) + Logger.setLogLevel(.trace, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .trace) try autoreleasepool { _ = try Realm() } XCTAssertTrue(!logs.value.isEmpty) XCTAssertTrue(logs.value.contains("Details DB:")) @@ -2029,24 +2031,24 @@ class LoggerTests: TestCase, @unchecked Sendable { } func testLogLevelForCategories() throws { - Logger.shared.setLogLevel(.off) - XCTAssertEqual(logger.logLevel(for: Category.realm), .off) + Logger.setLogLevel(.off, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .off) for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") - logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) - XCTAssertNotEqual(logger.logLevel(for: categoryEnum!), .all) + Logger.setLogLevel(.trace, for: categoryEnum!) + XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .trace) + XCTAssertNotEqual(Logger.logLevel(for: categoryEnum!), .all) } } func testLogMessageCategory() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, category, message in + let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - } + }) Logger.shared = logger for category in Logger.allCategories() { @@ -2054,9 +2056,9 @@ class LoggerTests: TestCase, @unchecked Sendable { let categoryEnum = categoryfromString(category) XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") - logger.setLogLevel(.trace, for: categoryEnum!) + Logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) + XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .trace) logger.log(with: .trace, categoryName: category, message: "Test") XCTAssertTrue(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log doesn't contain \(category)") } @@ -2065,9 +2067,9 @@ class LoggerTests: TestCase, @unchecked Sendable { /// Logger shoulldn't log category, even if it's a child or same category type, this test workd because `get_category_names()` returns categories from parent to children. func testNotLogParentOrRelatedCategory() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, category, message in + let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - } + }) Logger.shared = logger let categories = Logger.allCategories() @@ -2077,8 +2079,8 @@ class LoggerTests: TestCase, @unchecked Sendable { let categoryEnum = categoryfromString(categories[index+1]) XCTAssertNotNil(categoryEnum, "LogCategory `\(category)` not added to the Category enum.") - logger.setLogLevel(.trace, for: categoryEnum!) - XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) + Logger.setLogLevel(.trace, for: categoryEnum!) + XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .trace) logger.log(with: .trace, categoryName: category, message: "Test") XCTAssertFalse(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log shouldn't contain \(categories[index+1])") @@ -2088,12 +2090,12 @@ class LoggerTests: TestCase, @unchecked Sendable { /// Logger should log any message with from a child category func testShouldLogWhenParentCategory() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, category, message in + let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - } + }) Logger.shared = logger - logger.setLogLevel(.trace, for: Category.realm) - XCTAssertEqual(logger.logLevel(for: Category.realm), .trace) + Logger.setLogLevel(.trace, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .trace) for category in Logger.allCategories() { logs.wrappedValue = "" @@ -2104,52 +2106,73 @@ class LoggerTests: TestCase, @unchecked Sendable { func testChangeCategoryLevel() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, category, message in + let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - } + }) Logger.shared = logger - logger.setLogLevel(.trace, for: Category.realm) - XCTAssertEqual(logger.logLevel(for: Category.realm), .trace) + Logger.setLogLevel(.trace, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .trace) for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) - XCTAssertEqual(logger.logLevel(for: categoryEnum!), .trace) + XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .trace) } - logger.setLogLevel(.all, for: Category.realm) - XCTAssertEqual(logger.logLevel(for: Category.realm), .all) + Logger.setLogLevel(.all, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .all) for category in Logger.allCategories() { let categoryEnum = categoryfromString(category) - XCTAssertEqual(logger.logLevel(for: categoryEnum!), .all) + XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .all) } } func testChangeSubCategoryLevel() throws { let logs = Locked("") - let logger = Logger(level: .off) { level, category, message in + let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - } + }) Logger.shared = logger - XCTAssertEqual(logger.logLevel(for: Category.Storage.all), .off) - XCTAssertEqual(logger.logLevel(for: Category.Storage.transaction), .off) - XCTAssertEqual(logger.logLevel(for: Category.Storage.query), .off) - XCTAssertEqual(logger.logLevel(for: Category.Storage.object), .off) - XCTAssertEqual(logger.logLevel(for: Category.Storage.notification), .off) - - logger.setLogLevel(.info, for: Category.Storage.all) - XCTAssertEqual(logger.logLevel(for: Category.Storage.all), .info) - XCTAssertEqual(logger.logLevel(for: Category.Storage.transaction), .info) - XCTAssertEqual(logger.logLevel(for: Category.Storage.query), .info) - XCTAssertEqual(logger.logLevel(for: Category.Storage.object), .info) - XCTAssertEqual(logger.logLevel(for: Category.Storage.notification), .info) - - XCTAssertEqual(logger.logLevel(for: Category.realm), .off) - XCTAssertEqual(logger.logLevel(for: Category.sdk), .off) - XCTAssertEqual(logger.logLevel(for: Category.app), .off) - XCTAssertEqual(logger.logLevel(for: Category.Sync.all), .off) + Logger.setLogLevel(.off, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.all), .off) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.transaction), .off) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.query), .off) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.object), .off) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.notification), .off) + + Logger.setLogLevel(.info, for: Category.Storage.all) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.all), .info) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.transaction), .info) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.query), .info) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.object), .info) + XCTAssertEqual(Logger.logLevel(for: Category.Storage.notification), .info) + + XCTAssertEqual(Logger.logLevel(for: Category.realm), .off) + XCTAssertEqual(Logger.logLevel(for: Category.sdk), .off) + XCTAssertEqual(Logger.logLevel(for: Category.app), .off) + XCTAssertEqual(Logger.logLevel(for: Category.Sync.all), .off) + } + + func testing() throws { + let logs = Locked("") + let logger = Logger(function: { level, _, message in + logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) + }) + + Logger.shared = logger + + Logger.setLogLevel(.info, for: Category.sdk) + logger.log(level: .info, category: Category.sdk, message: "SDK test entry") + + XCTAssertTrue(logs.value.contains("SDK test entry")) + logs.wrappedValue = "" + + logger.log(level: .info, category: Category.realm, message: "REALM test entry") + + XCTAssertFalse(logs.value.contains("REALM test entry")) + logs.wrappedValue = "" } func categoryfromString(_ string: String) -> LogCategory? { From 246f31588b280f45f35c32676bf70d020609771c Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Mon, 17 Jun 2024 12:03:00 +0200 Subject: [PATCH 5/8] Solve PR comments --- Realm/RLMLogger.h | 3 ++- RealmSwift/Logger.swift | 8 +++---- RealmSwift/Tests/RealmTests.swift | 37 ++++++++++++++++--------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index e2806fc6bb..ce282d9c8d 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -126,7 +126,8 @@ typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, RLMLogCategory categor NSLog(@"Realm Log - %lu, %@, %@", (unsigned long)level, category, message); }]; - @note By default default log threshold level is `RLMLogLevelInfo`, and logging strings are output to Apple System Logger. + @note By default default log threshold level is `RLMLogLevelInfo` for the log category `RLMLogCategoryRealm`, + and logging strings are output to Apple System Logger. */ @interface RLMLogger : NSObject diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index ebf1c4d934..6a02b25d90 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -32,11 +32,11 @@ import Realm.Private let level = Logger.logLevel(for: Category.Storage.all) ``` - You can define your own logger creating an instance of `Logger` and define the log function which will be + You can define your own custom logger creating an instance of `Logger` and defining the log function which will be invoked whenever there is a log message. ```swift - let logger = Logger(function: { level, message in + let logger = Logger(function: { level, category, message in print("Realm Log - \(category.rawValue)-\(level): \(message)") }) ``` @@ -44,7 +44,7 @@ import Realm.Private Set this custom logger as you default logger using `Logger.shared`. This will replace the default logger. ```swift - Logger.shared = logger + Logger.shared = logger ``` - note: By default log threshold level is `.info`, for the log category `.Category.realm`, @@ -83,7 +83,7 @@ extension Logger { - parameter level: The log level to be set for the logger. - parameter function: The log function which will be invoked whenever there is a log message. - - note: This will set the log level for the log category `Category.realm`. + - note: This will set the specified log level for the log category `Category.realm`. */ @available(*, deprecated, message: "Use init(function:)") public convenience init(level: LogLevel, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index ced350fd36..abce0828bf 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -1973,7 +1973,7 @@ class LoggerTests: TestCase, @unchecked Sendable { XCTAssertTrue(logs.value.contains("Trace DB:")) } - func testDefaultLogger() throws { + func testSetDefaultLogger() throws { let logs = Locked("") let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(Date.now) \(category.rawValue):\(level.logLevel) \(message)" }) @@ -2044,7 +2044,7 @@ class LoggerTests: TestCase, @unchecked Sendable { } } - func testLogMessageCategory() throws { + func testLogMessageForCategory() throws { let logs = Locked("") let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) @@ -2064,8 +2064,11 @@ class LoggerTests: TestCase, @unchecked Sendable { } } - /// Logger shoulldn't log category, even if it's a child or same category type, this test workd because `get_category_names()` returns categories from parent to children. - func testNotLogParentOrRelatedCategory() throws { + /// This test works because `get_category_names()` returns categories from parent to children. + func testShouldNotLogParentOrRelatedCategory() throws { + Logger.setLogLevel(.off, for: Category.realm) + XCTAssertEqual(Logger.logLevel(for: Category.realm), .off) + let logs = Locked("") let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) @@ -2083,11 +2086,12 @@ class LoggerTests: TestCase, @unchecked Sendable { XCTAssertEqual(Logger.logLevel(for: categoryEnum!), .trace) logger.log(with: .trace, categoryName: category, message: "Test") - XCTAssertFalse(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log shouldn't contain \(categories[index+1])") + XCTAssertFalse(logs.value.contains("\(LogLevel.trace.logLevel) \(category) Test"), "Log shouldn't contain message from \(category)") + Logger.setLogLevel(.off, for: categoryEnum!) } } - /// Logger should log any message with from a child category + /// Logger should log messages from all child categories func testShouldLogWhenParentCategory() throws { let logs = Locked("") let logger = Logger(function: { level, category, message in @@ -2129,12 +2133,6 @@ class LoggerTests: TestCase, @unchecked Sendable { } func testChangeSubCategoryLevel() throws { - let logs = Locked("") - let logger = Logger(function: { level, category, message in - logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) - }) - Logger.shared = logger - Logger.setLogLevel(.off, for: Category.realm) XCTAssertEqual(Logger.logLevel(for: Category.Storage.all), .off) XCTAssertEqual(Logger.logLevel(for: Category.Storage.transaction), .off) @@ -2155,7 +2153,7 @@ class LoggerTests: TestCase, @unchecked Sendable { XCTAssertEqual(Logger.logLevel(for: Category.Sync.all), .off) } - func testing() throws { + func testCallbackFilteringForCatgories() throws { let logs = Locked("") let logger = Logger(function: { level, _, message in logs.withLock({ $0 += "\(Date.now) \(level.logLevel) \(message)" }) @@ -2163,16 +2161,19 @@ class LoggerTests: TestCase, @unchecked Sendable { Logger.shared = logger - Logger.setLogLevel(.info, for: Category.sdk) - logger.log(level: .info, category: Category.sdk, message: "SDK test entry") + Logger.setLogLevel(.off, for: Category.realm) + Logger.setLogLevel(.info, for: Category.Storage.all) + + logger.log(level: .info, category: Category.Storage.all, message: "Storage test entry") + XCTAssertTrue(logs.value.contains("Storage test entry")) + logs.wrappedValue = "" - XCTAssertTrue(logs.value.contains("SDK test entry")) + logger.log(level: .info, category: Category.Storage.transaction, message: "Transaction test entry") + XCTAssertTrue(logs.value.contains("Transaction test entry")) logs.wrappedValue = "" logger.log(level: .info, category: Category.realm, message: "REALM test entry") - XCTAssertFalse(logs.value.contains("REALM test entry")) - logs.wrappedValue = "" } func categoryfromString(_ string: String) -> LogCategory? { From 18c57854a2cb619c3a3ab49fbe408f5dc183fe1d Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Mon, 24 Jun 2024 23:11:16 +0200 Subject: [PATCH 6/8] Solve CI issues --- CHANGELOG.md | 2 -- Realm/RLMLogger_Private.h | 2 +- Realm/Tests/RealmTests.mm | 12 +++++++----- RealmSwift/Logger.swift | 2 +- RealmSwift/Tests/RealmTests.swift | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da170c3ad9..2e10de9dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ x.y.z Release notes (yyyy-MM-dd) ============================================================= ### Enhancements -* None. -* Update release packaging for Xcode 15.4. * Added support for filtering logs by category. Users wil have more fine grained control over the log level for each category as well. ```swift diff --git a/Realm/RLMLogger_Private.h b/Realm/RLMLogger_Private.h index be8af0c0c9..b831937d37 100644 --- a/Realm/RLMLogger_Private.h +++ b/Realm/RLMLogger_Private.h @@ -35,7 +35,7 @@ RLM_HEADER_AUDIT_BEGIN(nullability) Log a message to the supplied level. @param logLevel The log level for the message. - @param category The log category name for the message. + @param categoryName The log category name for the message. @param message The message to log. */ - (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName message:(NSString *)message; diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index 8411f5769e..af03653b79 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -2962,7 +2962,7 @@ - (void)testSetDefaultLogLevel { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { - [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; + [logs appendFormat:@" %@ %lu %lu %@", [NSDate date], (unsigned long)category, level, message]; }]; RLMLogger.defaultLogger = logger; @@ -2984,7 +2984,7 @@ - (void)testDefaultLogger { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString *message) { - [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; + [logs appendFormat:@" %@ %lu %lu %@", [NSDate date], (unsigned long)category, level, message]; }]; RLMLogger.defaultLogger = logger; [RLMLogger setLevel:RLMLogLevelOff forCategory:category]; @@ -3010,7 +3010,7 @@ - (void)testDefaultLogger { [logs setString: @""]; // Init Custom Logger RLMLogger.defaultLogger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { - [logs appendFormat:@" %@ %lu %@", [NSDate date], level, message]; + [logs appendFormat:@" %@ %lu %lu %@", [NSDate date], (unsigned long)category, level, message]; }]; [RLMLogger setLevel:RLMLogLevelDebug forCategory:category]; XCTAssertEqual([RLMLogger levelForCategory:category], RLMLogLevelDebug); @@ -3023,9 +3023,10 @@ - (void)testCustomLoggerLogMessage { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { - [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; + [logs appendFormat:@" %@ %lu %lu %@", [NSDate date], (unsigned long)category, level, message]; }]; RLMLogger.defaultLogger = logger; + [RLMLogger setLevel:RLMLogLevelDebug forCategory:category]; [logger logWithLevel:RLMLogLevelInfo message:@"%@ IMPORTANT INFO %i", @"TEST:", 0]; [logger logWithLevel:RLMLogLevelTrace message:@"IMPORTANT TRACE"]; @@ -3058,9 +3059,10 @@ - (void)testSyncConnectionMetrics { __block NSMutableString *logs = [[NSMutableString alloc] init]; RLMLogCategory category = RLMLogCategoryRealm; RLMLogger *logger = [[RLMLogger alloc] initWithLogFunction:^(RLMLogLevel level, RLMLogCategory category, NSString * message) { - [logs appendFormat:@" %@ %lu %@.", [NSDate date], level, message]; + [logs appendFormat:@" %@ %lu %lu %@", [NSDate date], (unsigned long)category, level, message]; }]; RLMLogger.defaultLogger = logger; + [RLMLogger setLevel:RLMLogLevelAll forCategory:category]; RLMApp *app = [RLMApp appWithId:@"test-id"]; // We don't even need the login to succeed, we only want for the logger // to log the values on device info after trying to login. diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index 6a02b25d90..bb939232ef 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -88,7 +88,7 @@ extension Logger { @available(*, deprecated, message: "Use init(function:)") public convenience init(level: LogLevel, function: @escaping @Sendable (LogLevel, LogCategory, String) -> Void) { self.init(logFunction: { level, category, message in - function(level, Category.realm, message) + function(level, ObjectiveCSupport.convert(value: category), message) }) Logger.setLogLevel(level, for: Category.realm) } diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index abce0828bf..5cbe0745dd 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -2068,7 +2068,7 @@ class LoggerTests: TestCase, @unchecked Sendable { func testShouldNotLogParentOrRelatedCategory() throws { Logger.setLogLevel(.off, for: Category.realm) XCTAssertEqual(Logger.logLevel(for: Category.realm), .off) - + let logs = Locked("") let logger = Logger(function: { level, category, message in logs.withLock({ $0 += "\(level.logLevel) \(category.rawValue) \(message) " }) @@ -2163,16 +2163,16 @@ class LoggerTests: TestCase, @unchecked Sendable { Logger.setLogLevel(.off, for: Category.realm) Logger.setLogLevel(.info, for: Category.Storage.all) - - logger.log(level: .info, category: Category.Storage.all, message: "Storage test entry") + + logger.log(with: .info, categoryName: Category.Storage.all.rawValue, message: "Storage test entry") XCTAssertTrue(logs.value.contains("Storage test entry")) logs.wrappedValue = "" - logger.log(level: .info, category: Category.Storage.transaction, message: "Transaction test entry") + logger.log(with: .info, categoryName: Category.Storage.transaction.rawValue, message: "Transaction test entry") XCTAssertTrue(logs.value.contains("Transaction test entry")) logs.wrappedValue = "" - logger.log(level: .info, category: Category.realm, message: "REALM test entry") + logger.log(with: .info, categoryName: Category.realm.rawValue, message: "REALM test entry") XCTAssertFalse(logs.value.contains("REALM test entry")) } From 9e90d496896785df5694cb9fdfdf18930cfbe39e Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Tue, 25 Jun 2024 15:56:53 +0200 Subject: [PATCH 7/8] Solve PR comments --- CHANGELOG.md | 2 +- Realm/RLMLogger.h | 9 +- Realm/RLMLogger.mm | 91 +++++++++---------- .../SwiftUISyncTestHost/ContentView.swift | 2 +- RealmSwift/Logger.swift | 29 ++---- 5 files changed, 55 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e10de9dab..58c4f21c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ x.y.z Release notes (yyyy-MM-dd) ### Deprecations * `RLMLogger.level`/`Logger.level` has been deprecated in favor of using `RLMLogger.setLevel:forCategory:`/`Logger.setLevel(:category:)` and `RLMLogger.getLevelForCategory:`/`Logger.getLevel(for:)`. -* Is not recommended to initialise a `RLMLogger/Logger` with a level anymore. +* It is not recommended to initialize a `RLMLogger/Logger` with a level anymore. ### Compatibility * Realm Studio: 15.0.0 or later. diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index ce282d9c8d..3628340618 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -49,8 +49,9 @@ typedef RLM_CLOSED_ENUM(NSUInteger, RLMLogLevel) { } NS_SWIFT_NAME(LogLevel); /** -An enum representing different categories of sync-related logging that can be configured. -Category hierarchy: + An enum representing different categories of sync-related logging that can be configured. + Setting the log level for a parent category automatically sets the same level for all child categories. + Category hierarchy: ``` Realm ├─► Storage @@ -126,7 +127,7 @@ typedef void (^RLMLogCategoryFunction)(RLMLogLevel level, RLMLogCategory categor NSLog(@"Realm Log - %lu, %@, %@", (unsigned long)level, category, message); }]; - @note By default default log threshold level is `RLMLogLevelInfo` for the log category `RLMLogCategoryRealm`, + @note The default log threshold level is `RLMLogLevelInfo` for the log category `RLMLogCategoryRealm`, and logging strings are output to Apple System Logger. */ @interface RLMLogger : NSObject @@ -163,8 +164,6 @@ __attribute__((deprecated("Use `initWithLogFunction:` instead."))); /** The current default logger. When setting a logger as default, this logger will replace the current default logger and will be used whenever information must be logged. - - @note By default the logger */ @property (class) RLMLogger *defaultLogger NS_SWIFT_NAME(shared); diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index 56dee6d8f6..0931653c91 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -75,53 +75,47 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { REALM_UNREACHABLE(); // Unrecognized log level. } -static NSArray *categories = [NSArray arrayWithObjects: - @"Realm", - @"Realm.SDK", - @"Realm.App", - @"Realm.Storage", - @"Realm.Storage.Transaction", - @"Realm.Storage.Query", - @"Realm.Storage.Object", - @"Realm.Storage.Notification", - @"Realm.Sync", - @"Realm.Sync.Client", - @"Realm.Sync.Client.Session", - @"Realm.Sync.Client.Changeset", - @"Realm.Sync.Client.Network", - @"Realm.Sync.Client.Reset", - @"Realm.Sync.Server", - nil]; - -static std::string categoryNameForLogCategory(RLMLogCategory logCategory) { - if (logCategory < [categories count]) { - if (auto categoryName = [categories objectAtIndex:logCategory]) { - return categoryName.UTF8String; - } - } +static LogCategory& categoryForLogCategory(RLMLogCategory logCategory) { + switch (logCategory) { + case RLMLogCategoryRealm: return LogCategory::realm; + case RLMLogCategoryRealmSDK: return LogCategory::sdk; + case RLMLogCategoryRealmApp: return LogCategory::app; + case RLMLogCategoryRealmStorage: return LogCategory::storage; + case RLMLogCategoryRealmStorageTransaction: return LogCategory::transaction; + case RLMLogCategoryRealmStorageQuery: return LogCategory::query; + case RLMLogCategoryRealmStorageObject: return LogCategory::object; + case RLMLogCategoryRealmStorageNotification: return LogCategory::notification; + case RLMLogCategoryRealmSync: return LogCategory::sync; + case RLMLogCategoryRealmSyncClient: return LogCategory::client; + case RLMLogCategoryRealmSyncClientSession: return LogCategory::session; + case RLMLogCategoryRealmSyncClientChangeset: return LogCategory::changeset; + case RLMLogCategoryRealmSyncClientNetwork: return LogCategory::network; + case RLMLogCategoryRealmSyncClientReset: return LogCategory::reset; + case RLMLogCategoryRealmSyncServer: return LogCategory::server; + }; REALM_UNREACHABLE(); } static RLMLogCategory logCategoryForCategoryName(std::string category) { - auto index = [categories indexOfObject:RLMStringDataToNSString(category)]; - if (index != NSNotFound) { - switch (index) { - case 0: return RLMLogCategoryRealm; - case 1: return RLMLogCategoryRealmSDK; - case 2: return RLMLogCategoryRealmApp; - case 3: return RLMLogCategoryRealmStorage; - case 4: return RLMLogCategoryRealmStorageTransaction; - case 5: return RLMLogCategoryRealmStorageQuery; - case 6: return RLMLogCategoryRealmStorageObject; - case 7: return RLMLogCategoryRealmStorageNotification; - case 8: return RLMLogCategoryRealmSync; - case 9: return RLMLogCategoryRealmSyncClient; - case 10: return RLMLogCategoryRealmSyncClientSession; - case 11: return RLMLogCategoryRealmSyncClientChangeset; - case 12: return RLMLogCategoryRealmSyncClientNetwork; - case 13: return RLMLogCategoryRealmSyncClientReset; - case 14: return RLMLogCategoryRealmSyncServer; - } + NSDictionary *categories = @{ + @"Realm": @(RLMLogCategoryRealm), + @"Realm.SDK": @(RLMLogCategoryRealmSDK), + @"Realm.App": @(RLMLogCategoryRealmApp), + @"Realm.Storage": @(RLMLogCategoryRealmStorage), + @"Realm.Storage.Transaction": @(RLMLogCategoryRealmStorageTransaction), + @"Realm.Storage.Query": @(RLMLogCategoryRealmStorageQuery), + @"Realm.Storage.Object": @(RLMLogCategoryRealmStorageObject), + @"Realm.Storage.Notification": @(RLMLogCategoryRealmStorageNotification), + @"Realm.Sync": @(RLMLogCategoryRealmSync), + @"Realm.Sync.Client": @(RLMLogCategoryRealmSyncClient), + @"Realm.Sync.Client.Session": @(RLMLogCategoryRealmSyncClientSession), + @"Realm.Sync.Client.Changeset": @(RLMLogCategoryRealmSyncClientChangeset), + @"Realm.Sync.Client.Network": @(RLMLogCategoryRealmSyncClientNetwork), + @"Realm.Sync.Client.Reset": @(RLMLogCategoryRealmSyncClientReset), + @"Realm.Sync.Server": @(RLMLogCategoryRealmSyncServer) + }; + if (NSNumber *logCategory = [categories objectForKey:RLMStringDataToNSString(category)]) { + return RLMLogCategory([logCategory intValue]); } REALM_UNREACHABLE(); } @@ -210,10 +204,9 @@ - (void)logWithLevel:(RLMLogLevel)logLevel message:(NSString *)message, ... { - (void)logWithLevel:(RLMLogLevel)logLevel category:(RLMLogCategory)category message:(NSString *)message { auto level = levelForLogLevel(logLevel); - auto cat = categoryNameForLogCategory(category); - LogCategory& lcat = LogCategory::get_category(cat); - if (_logger->would_log(lcat, level)) { - _logger->log(lcat, levelForLogLevel(logLevel), message.UTF8String); + LogCategory& cat = categoryForLogCategory(category); + if (_logger->would_log(cat, level)) { + _logger->log(cat, levelForLogLevel(logLevel), message.UTF8String); } } @@ -227,12 +220,12 @@ - (void)logWithLevel:(RLMLogLevel)logLevel categoryName:(NSString *)categoryName + (void)setLevel:(RLMLogLevel)level forCategory:(RLMLogCategory)category { auto defaultLogger = Logger::get_default_logger(); - defaultLogger->set_level_threshold(categoryNameForLogCategory(category), levelForLogLevel(level)); + defaultLogger->set_level_threshold(categoryForLogCategory(category).get_name(), levelForLogLevel(level)); } + (RLMLogLevel)levelForCategory:(RLMLogCategory)category { auto defaultLogger = Logger::get_default_logger(); - return logLevelForLevel(defaultLogger->get_level_threshold(categoryNameForLogCategory(category))); + return logLevelForLevel(defaultLogger->get_level_threshold(categoryForLogCategory(category).get_name())); } #pragma mark Testing diff --git a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift index 061615a08b..e7d22b17fb 100644 --- a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift +++ b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift @@ -198,7 +198,7 @@ class LoginHelper: ObservableObject { private let appConfig = AppConfiguration(baseURL: "http://localhost:9090") func login(email: String, password: String, completion: @escaping (User) -> Void) { - Logger.shared.setLogLevel(.all, category: Category.Realm) + Logger.setLogLevel(.all, for: Category.realm) let app = RealmSwift.App(id: ProcessInfo.processInfo.environment["app_id"]!, configuration: appConfig) app.login(credentials: .emailPassword(email: email, password: password)) .receive(on: DispatchQueue.main) diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index bb939232ef..e5c265a518 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -47,7 +47,7 @@ import Realm.Private Logger.shared = logger ``` - - note: By default log threshold level is `.info`, for the log category `.Category.realm`, + - note: The default log threshold level is `.info`, for the log category `.Category.realm`, and logging strings are output to Apple System Logger. - SeeAlso: `LogCategory` */ @@ -67,7 +67,7 @@ extension Logger { - parameter category: The log category for the message. - parameter message: The message to log. */ - internal func log(level: LogLevel, category: LogCategory = Category.realm, message: String) { + internal func log(level: LogLevel, category: LogCategory = Category.sdk, message: String) { self.log(with: level, category: ObjectiveCSupport.convert(value: category), message: message) } @@ -175,21 +175,6 @@ public enum Category: String, LogCategory { /// Log category for all app related logs. case app = "Realm.App" - /// :nodoc: - fileprivate static func fromString(_ string: String) -> LogCategory? { - if let category = Category(rawValue: string) { - return category - } else if let storage = Storage(rawValue: string) { - return storage - } else if let sync = Sync(rawValue: string) { - return sync - } else if let client = Sync.Client(rawValue: string) { - return client - } else { - return nil - } - } - /** Log category for all storage related logs. @@ -240,11 +225,11 @@ public enum Category: String, LogCategory { Category hierarchy: ``` - Storage - ├─► Transaction - ├─► Query - ├─► Object - └─► Notification + Client + ├─► Session + ├─► Changeset + ├─► Network + └─► Reset ``` */ public enum Client: String, LogCategory { From 762ab1e8c9065f73b8cbaa18c1c71a109f065fbd Mon Sep 17 00:00:00 2001 From: Diana Maria Perez Afanador Date: Fri, 28 Jun 2024 16:05:01 +0200 Subject: [PATCH 8/8] Solved PR comments --- Realm/ObjectServerTests/RLMSyncTestCase.mm | 2 +- Realm/RLMLogger.h | 28 +++++------ Realm/RLMLogger.mm | 56 +++++++++++----------- RealmSwift/Logger.swift | 56 +++++++++++----------- 4 files changed, 71 insertions(+), 71 deletions(-) diff --git a/Realm/ObjectServerTests/RLMSyncTestCase.mm b/Realm/ObjectServerTests/RLMSyncTestCase.mm index 9dd55fee88..edf859587d 100644 --- a/Realm/ObjectServerTests/RLMSyncTestCase.mm +++ b/Realm/ObjectServerTests/RLMSyncTestCase.mm @@ -613,7 +613,7 @@ - (RLMApp *)appWithId:(NSString *)appId { RLMApp *app = [RLMApp appWithConfiguration:config]; RLMSyncManager *syncManager = app.syncManager; syncManager.userAgent = self.name; - [RLMLogger setLevel:RLMLogLevelWarn forCategory:RLMLogCategoryRealmSync]; + [RLMLogger setLevel:RLMLogLevelWarn forCategory:RLMLogCategorySync]; return app; } diff --git a/Realm/RLMLogger.h b/Realm/RLMLogger.h index 3628340618..35bb099f44 100644 --- a/Realm/RLMLogger.h +++ b/Realm/RLMLogger.h @@ -74,33 +74,33 @@ typedef NS_ENUM(NSUInteger, RLMLogCategory) { /// Top level log category for Realm, updating this category level would set all other subcategories too. RLMLogCategoryRealm, /// Log category for all sdk related logs. - RLMLogCategoryRealmSDK, + RLMLogCategorySDK, /// Log category for all app related logs. - RLMLogCategoryRealmApp, + RLMLogCategoryApp, /// Log category for all database related logs. - RLMLogCategoryRealmStorage, + RLMLogCategoryStorage, /// Log category for all database transaction related logs. - RLMLogCategoryRealmStorageTransaction, + RLMLogCategoryStorageTransaction, /// Log category for all database queries related logs. - RLMLogCategoryRealmStorageQuery, + RLMLogCategoryStorageQuery, /// Log category for all database object related logs. - RLMLogCategoryRealmStorageObject, + RLMLogCategoryStorageObject, /// Log category for all database notification related logs. - RLMLogCategoryRealmStorageNotification, + RLMLogCategoryStorageNotification, /// Log category for all sync related logs. - RLMLogCategoryRealmSync, + RLMLogCategorySync, /// Log category for all sync client related logs. - RLMLogCategoryRealmSyncClient, + RLMLogCategorySyncClient, /// Log category for all sync client session related logs. - RLMLogCategoryRealmSyncClientSession, + RLMLogCategorySyncClientSession, /// Log category for all sync client changeset related logs. - RLMLogCategoryRealmSyncClientChangeset, + RLMLogCategorySyncClientChangeset, /// Log category for all sync client network related logs. - RLMLogCategoryRealmSyncClientNetwork, + RLMLogCategorySyncClientNetwork, /// Log category for all sync client reset related logs. - RLMLogCategoryRealmSyncClientReset, + RLMLogCategorySyncClientReset, /// Log category for all sync server related logs. - RLMLogCategoryRealmSyncServer + RLMLogCategorySyncServer }; /// A log callback function which can be set on RLMLogger. diff --git a/Realm/RLMLogger.mm b/Realm/RLMLogger.mm index 0931653c91..dc58ffbd5a 100644 --- a/Realm/RLMLogger.mm +++ b/Realm/RLMLogger.mm @@ -78,20 +78,20 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { static LogCategory& categoryForLogCategory(RLMLogCategory logCategory) { switch (logCategory) { case RLMLogCategoryRealm: return LogCategory::realm; - case RLMLogCategoryRealmSDK: return LogCategory::sdk; - case RLMLogCategoryRealmApp: return LogCategory::app; - case RLMLogCategoryRealmStorage: return LogCategory::storage; - case RLMLogCategoryRealmStorageTransaction: return LogCategory::transaction; - case RLMLogCategoryRealmStorageQuery: return LogCategory::query; - case RLMLogCategoryRealmStorageObject: return LogCategory::object; - case RLMLogCategoryRealmStorageNotification: return LogCategory::notification; - case RLMLogCategoryRealmSync: return LogCategory::sync; - case RLMLogCategoryRealmSyncClient: return LogCategory::client; - case RLMLogCategoryRealmSyncClientSession: return LogCategory::session; - case RLMLogCategoryRealmSyncClientChangeset: return LogCategory::changeset; - case RLMLogCategoryRealmSyncClientNetwork: return LogCategory::network; - case RLMLogCategoryRealmSyncClientReset: return LogCategory::reset; - case RLMLogCategoryRealmSyncServer: return LogCategory::server; + case RLMLogCategorySDK: return LogCategory::sdk; + case RLMLogCategoryApp: return LogCategory::app; + case RLMLogCategoryStorage: return LogCategory::storage; + case RLMLogCategoryStorageTransaction: return LogCategory::transaction; + case RLMLogCategoryStorageQuery: return LogCategory::query; + case RLMLogCategoryStorageObject: return LogCategory::object; + case RLMLogCategoryStorageNotification: return LogCategory::notification; + case RLMLogCategorySync: return LogCategory::sync; + case RLMLogCategorySyncClient: return LogCategory::client; + case RLMLogCategorySyncClientSession: return LogCategory::session; + case RLMLogCategorySyncClientChangeset: return LogCategory::changeset; + case RLMLogCategorySyncClientNetwork: return LogCategory::network; + case RLMLogCategorySyncClientReset: return LogCategory::reset; + case RLMLogCategorySyncServer: return LogCategory::server; }; REALM_UNREACHABLE(); } @@ -99,20 +99,20 @@ static RLMLogLevel logLevelForLevel(Level logLevel) { static RLMLogCategory logCategoryForCategoryName(std::string category) { NSDictionary *categories = @{ @"Realm": @(RLMLogCategoryRealm), - @"Realm.SDK": @(RLMLogCategoryRealmSDK), - @"Realm.App": @(RLMLogCategoryRealmApp), - @"Realm.Storage": @(RLMLogCategoryRealmStorage), - @"Realm.Storage.Transaction": @(RLMLogCategoryRealmStorageTransaction), - @"Realm.Storage.Query": @(RLMLogCategoryRealmStorageQuery), - @"Realm.Storage.Object": @(RLMLogCategoryRealmStorageObject), - @"Realm.Storage.Notification": @(RLMLogCategoryRealmStorageNotification), - @"Realm.Sync": @(RLMLogCategoryRealmSync), - @"Realm.Sync.Client": @(RLMLogCategoryRealmSyncClient), - @"Realm.Sync.Client.Session": @(RLMLogCategoryRealmSyncClientSession), - @"Realm.Sync.Client.Changeset": @(RLMLogCategoryRealmSyncClientChangeset), - @"Realm.Sync.Client.Network": @(RLMLogCategoryRealmSyncClientNetwork), - @"Realm.Sync.Client.Reset": @(RLMLogCategoryRealmSyncClientReset), - @"Realm.Sync.Server": @(RLMLogCategoryRealmSyncServer) + @"Realm.SDK": @(RLMLogCategorySDK), + @"Realm.App": @(RLMLogCategoryApp), + @"Realm.Storage": @(RLMLogCategoryStorage), + @"Realm.Storage.Transaction": @(RLMLogCategoryStorageTransaction), + @"Realm.Storage.Query": @(RLMLogCategoryStorageQuery), + @"Realm.Storage.Object": @(RLMLogCategoryStorageObject), + @"Realm.Storage.Notification": @(RLMLogCategoryStorageNotification), + @"Realm.Sync": @(RLMLogCategorySync), + @"Realm.Sync.Client": @(RLMLogCategorySyncClient), + @"Realm.Sync.Client.Session": @(RLMLogCategorySyncClientSession), + @"Realm.Sync.Client.Changeset": @(RLMLogCategorySyncClientChangeset), + @"Realm.Sync.Client.Network": @(RLMLogCategorySyncClientNetwork), + @"Realm.Sync.Client.Reset": @(RLMLogCategorySyncClientReset), + @"Realm.Sync.Server": @(RLMLogCategorySyncServer) }; if (NSNumber *logCategory = [categories objectForKey:RLMStringDataToNSString(category)]) { return RLMLogCategory([logCategory intValue]); diff --git a/RealmSwift/Logger.swift b/RealmSwift/Logger.swift index e5c265a518..3bd00b97e7 100644 --- a/RealmSwift/Logger.swift +++ b/RealmSwift/Logger.swift @@ -257,33 +257,33 @@ private extension ObjectiveCSupport { case Category.realm: return RLMLogCategory.realm case Category.sdk: - return RLMLogCategory.realmSDK + return RLMLogCategory.SDK case Category.app: - return RLMLogCategory.realmApp + return RLMLogCategory.app case Category.Storage.all: - return RLMLogCategory.realmStorage + return RLMLogCategory.storage case Category.Storage.transaction: - return RLMLogCategory.realmStorageTransaction + return RLMLogCategory.storageTransaction case Category.Storage.query: - return RLMLogCategory.realmStorageQuery + return RLMLogCategory.storageQuery case Category.Storage.object: - return RLMLogCategory.realmStorageObject + return RLMLogCategory.storageObject case Category.Storage.notification: - return RLMLogCategory.realmStorageNotification + return RLMLogCategory.storageNotification case Category.Sync.all: - return RLMLogCategory.realmSync + return RLMLogCategory.sync case Category.Sync.Client.all: - return RLMLogCategory.realmSyncClient + return RLMLogCategory.syncClient case Category.Sync.Client.session: - return RLMLogCategory.realmSyncClientSession + return RLMLogCategory.syncClientSession case Category.Sync.Client.changeset: - return RLMLogCategory.realmSyncClientChangeset + return RLMLogCategory.syncClientChangeset case Category.Sync.Client.network: - return RLMLogCategory.realmSyncClientNetwork + return RLMLogCategory.syncClientNetwork case Category.Sync.Client.reset: - return RLMLogCategory.realmSyncClientReset + return RLMLogCategory.syncClientReset case Category.Sync.server: - return RLMLogCategory.realmSyncServer + return RLMLogCategory.syncServer default: fatalError() } @@ -296,33 +296,33 @@ private extension ObjectiveCSupport { switch value { case RLMLogCategory.realm: return Category.realm - case RLMLogCategory.realmSDK: + case RLMLogCategory.SDK: return Category.sdk - case RLMLogCategory.realmApp: + case RLMLogCategory.app: return Category.app - case RLMLogCategory.realmStorage: + case RLMLogCategory.storage: return Category.Storage.all - case RLMLogCategory.realmStorageTransaction: + case RLMLogCategory.storageTransaction: return Category.Storage.transaction - case RLMLogCategory.realmStorageQuery: + case RLMLogCategory.storageQuery: return Category.Storage.query - case RLMLogCategory.realmStorageObject: + case RLMLogCategory.storageObject: return Category.Storage.object - case RLMLogCategory.realmStorageNotification: + case RLMLogCategory.storageNotification: return Category.Storage.notification - case RLMLogCategory.realmSync: + case RLMLogCategory.sync: return Category.Sync.all - case RLMLogCategory.realmSyncClient: + case RLMLogCategory.syncClient: return Category.Sync.Client.all - case RLMLogCategory.realmSyncClientSession: + case RLMLogCategory.syncClientSession: return Category.Sync.Client.session - case RLMLogCategory.realmSyncClientChangeset: + case RLMLogCategory.syncClientChangeset: return Category.Sync.Client.changeset - case RLMLogCategory.realmSyncClientNetwork: + case RLMLogCategory.syncClientNetwork: return Category.Sync.Client.network - case RLMLogCategory.realmSyncClientReset: + case RLMLogCategory.syncClientReset: return Category.Sync.Client.reset - case RLMLogCategory.realmSyncServer: + case RLMLogCategory.syncServer: return Category.Sync.server default: fatalError()