From fd798711736d7a1fdf9949e4448ba6b73e950cf1 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Mon, 27 Mar 2023 14:12:27 +0200 Subject: [PATCH 1/5] Integrate Core logging --- Realm/Realm/Handles/AppHandle.cs | 27 +----------- Realm/Realm/Handles/SharedRealmHandle.cs | 12 ++++-- Realm/Realm/Logging/LogLevel.cs | 18 ++++---- Realm/Realm/Logging/Logger.cs | 17 ++++++-- Realm/Realm/Native/AppConfiguration.cs | 5 --- Realm/Realm/Sync/App.cs | 3 -- .../GuidRepresentationMigrationTests.cs | 8 ++-- Tests/Realm.Tests/Database/InstanceTests.cs | 43 +++++++++++++++++++ .../LoneClass_generated.cs | 2 + Tests/Realm.Tests/RealmTest.cs | 2 - Tests/Realm.Tests/TestHelpers.cs | 6 +++ wrappers/realm-core | 2 +- wrappers/src/app_cs.cpp | 32 -------------- wrappers/src/marshalling.cpp | 4 +- wrappers/src/shared_realm_cs.cpp | 36 +++++++++------- wrappers/src/shared_realm_cs.hpp | 2 - wrappers/src/sync_session_cs.cpp | 4 +- 17 files changed, 114 insertions(+), 109 deletions(-) diff --git a/Realm/Realm/Handles/AppHandle.cs b/Realm/Realm/Handles/AppHandle.cs index 5cda992684..bc922d4da1 100644 --- a/Realm/Realm/Handles/AppHandle.cs +++ b/Realm/Realm/Handles/AppHandle.cs @@ -22,7 +22,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Realms.Logging; using Realms.Native; using Realms.Sync.Exceptions; using Realms.Sync.Native; @@ -35,9 +34,6 @@ internal partial class AppHandle : StandaloneHandle private static class NativeMethods { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void LogMessageCallback(IntPtr managed_handler, PrimitiveValue messageValue, LogLevel logLevel); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void UserCallback(IntPtr tcs_ptr, IntPtr user_ptr, AppError error); @@ -59,7 +55,7 @@ public static extern IntPtr initialize( [MarshalAs(UnmanagedType.LPWStr)] string cpu_arch, IntPtr cpu_arch_len, [MarshalAs(UnmanagedType.LPWStr)] string device_name, IntPtr device_name_len, [MarshalAs(UnmanagedType.LPWStr)] string device_version, IntPtr device_version_len, - UserCallback user_callback, VoidTaskCallback void_callback, StringCallback string_callback, LogMessageCallback log_message_callback, ApiKeysCallback api_keys_callback); + UserCallback user_callback, VoidTaskCallback void_callback, StringCallback string_callback, ApiKeysCallback api_keys_callback); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_create", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_app(Native.AppConfiguration app_config, byte[]? encryptionKey, out NativeException ex); @@ -152,13 +148,11 @@ static AppHandle() public static void Initialize() { - NativeMethods.LogMessageCallback logMessage = HandleLogMessage; NativeMethods.UserCallback userLogin = HandleUserCallback; NativeMethods.VoidTaskCallback taskCallback = HandleTaskCompletion; NativeMethods.StringCallback stringCallback = HandleStringCallback; NativeMethods.ApiKeysCallback apiKeysCallback = HandleApiKeysCallback; - GCHandle.Alloc(logMessage); GCHandle.Alloc(userLogin); GCHandle.Alloc(taskCallback); GCHandle.Alloc(stringCallback); @@ -191,7 +185,7 @@ public static void Initialize() cpuArch, cpuArch.IntPtrLength(), deviceName, deviceName.IntPtrLength(), deviceVersion, deviceVersion.IntPtrLength(), - userLogin, taskCallback, stringCallback, logMessage, apiKeysCallback); + userLogin, taskCallback, stringCallback, apiKeysCallback); } internal AppHandle(IntPtr handle) : base(handle) @@ -341,24 +335,7 @@ public SyncUserHandle GetUserForTesting(string id, string refreshToken, string a protected override void Unbind() => NativeMethods.destroy(handle); - [MonoPInvokeCallback(typeof(NativeMethods.LogMessageCallback))] - private static void HandleLogMessage(IntPtr managedHandler, PrimitiveValue messageValue, LogLevel level) - { - try - { - var message = messageValue.AsString(); - var logger = (Logger)GCHandle.FromIntPtr(managedHandler).Target!; - logger.Log(level, message); - } - catch (Exception ex) - { - var errorMessage = $"An error occurred while trying to log a message: {ex}"; - Logger.LogDefault(LogLevel.Error, errorMessage); - } - } - [MonoPInvokeCallback(typeof(NativeMethods.UserCallback))] - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The user will own its handle.")] private static void HandleUserCallback(IntPtr tcs_ptr, IntPtr user_ptr, AppError error) { var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); diff --git a/Realm/Realm/Handles/SharedRealmHandle.cs b/Realm/Realm/Handles/SharedRealmHandle.cs index fc4dc6f177..5d39f5b476 100644 --- a/Realm/Realm/Handles/SharedRealmHandle.cs +++ b/Realm/Realm/Handles/SharedRealmHandle.cs @@ -65,7 +65,7 @@ private static class NativeMethods public delegate void DisposeGCHandleCallback(IntPtr handle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void LogMessageCallback(PrimitiveValue message, LogLevel level); + public delegate void LogMessageCallback(StringValue message, LogLevel level); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HandleTaskCompletionCallback(IntPtr tcs_ptr, [MarshalAs(UnmanagedType.U1)] bool invoke_async, NativeException ex); @@ -234,6 +234,9 @@ public static extern void rename_property(SharedRealmHandle sharedRealm, [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_refresh_async", CallingConvention = CallingConvention.Cdecl)] public static extern bool refresh_async(SharedRealmHandle realm, IntPtr tcs_handle, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_set_log_level", CallingConvention = CallingConvention.Cdecl)] + public static extern bool set_log_level(LogLevel level); + #pragma warning restore SA1121 // Use built-in type alias #pragma warning restore IDE0049 // Use built-in type alias } @@ -272,6 +275,8 @@ public static void Initialize() NativeMethods.install_callbacks(notifyRealm, getNativeSchema, openRealm, disposeGCHandle, logMessage, notifyObject, notifyDictionary, onMigration, shouldCompact, handleTaskCompletion, onInitialization); } + public static void SetLogLevel(LogLevel level) => NativeMethods.set_log_level(level); + [Preserve] public SharedRealmHandle(IntPtr handle) : base(handle) { @@ -765,7 +770,6 @@ public static void NotifyRealmChanged(IntPtr stateHandle) } [MonoPInvokeCallback(typeof(NativeMethods.OpenRealmCallback))] - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Realm will be owned by the creator of the tcs")] private static void HandleOpenRealmCallback(IntPtr taskCompletionSource, IntPtr realm_reference, NativeException ex) { var handleTcs = GCHandle.FromIntPtr(taskCompletionSource); @@ -793,9 +797,9 @@ public static void DisposeGCHandleCallback(IntPtr handle) } [MonoPInvokeCallback(typeof(NativeMethods.LogMessageCallback))] - private static void LogMessage(PrimitiveValue message, LogLevel level) + private static void LogMessage(StringValue message, LogLevel level) { - Logger.LogDefault(level, message.AsString()); + Logger.LogDefault(level, message!); } [MonoPInvokeCallback(typeof(NativeMethods.MigrationCallback))] diff --git a/Realm/Realm/Logging/LogLevel.cs b/Realm/Realm/Logging/LogLevel.cs index aa6ae822ca..0a993d1baa 100644 --- a/Realm/Realm/Logging/LogLevel.cs +++ b/Realm/Realm/Logging/LogLevel.cs @@ -28,49 +28,49 @@ public enum LogLevel /// Log everything. This will seriously harm the performance of the /// sync client and should never be used in production scenarios. /// - All, + All = 0, /// /// A version of 'debug' that allows for very high volume output. /// This may seriously affect the performance of the sync client. /// - Trace, + Trace = 1, /// /// Reveal information that can aid debugging, no longer paying /// attention to efficiency. /// - Debug, + Debug = 2, /// /// Same as 'Info', but prioritize completeness over minimalism. /// - Detail, + Detail = 3, /// /// Log operational sync client messages, but in a minimalistic fashion to /// avoid general overhead from logging and to keep volume down. /// - Info, + Info = 4, /// /// Log errors and warnings. /// - Warn, + Warn = 5, /// /// Log errors only. /// - Error, + Error = 6, /// /// Log only fatal errors. /// - Fatal, + Fatal = 7, /// /// Log nothing. /// - Off + Off = 8, } } diff --git a/Realm/Realm/Logging/Logger.cs b/Realm/Realm/Logging/Logger.cs index 6b871fbb9c..7ca0e7d9d0 100644 --- a/Realm/Realm/Logging/Logger.cs +++ b/Realm/Realm/Logging/Logger.cs @@ -36,6 +36,7 @@ public abstract class Logger private readonly Lazy _gcHandle; private static Logger? _defaultLogger; + private static LogLevel logLevel = LogLevel.Info; /// /// Gets a that outputs messages to the default console. For most project types, that will be @@ -86,7 +87,15 @@ public abstract class Logger /// Gets or sets the verbosity of log messages. /// /// The log level for Realm-originating messages. - public static LogLevel LogLevel { get; set; } = LogLevel.Info; + public static LogLevel LogLevel + { + get => logLevel; + set + { + logLevel = value; + SharedRealmHandle.SetLogLevel(value); + } + } /// /// Gets or sets a custom implementation that will be used by @@ -197,10 +206,10 @@ internal class InMemoryLogger : Logger protected override void LogImpl(LogLevel level, string message) { lock (_builder) - { - _builder.AppendLine(FormatLog(level, message)); + { + _builder.AppendLine(FormatLog(level, message)); + } } - } public string GetLog() { diff --git a/Realm/Realm/Native/AppConfiguration.cs b/Realm/Realm/Native/AppConfiguration.cs index 17229da3f9..18d91c8e42 100644 --- a/Realm/Realm/Native/AppConfiguration.cs +++ b/Realm/Realm/Native/AppConfiguration.cs @@ -18,7 +18,6 @@ using System; using System.Runtime.InteropServices; -using Realms.Logging; namespace Realms.Sync.Native { @@ -106,10 +105,6 @@ internal MetadataPersistenceMode? MetadataPersistence } } - internal LogLevel log_level; - - internal IntPtr managed_logger; - internal IntPtr managed_http_client; internal UInt64 sync_connect_timeout_ms; diff --git a/Realm/Realm/Sync/App.cs b/Realm/Realm/Sync/App.cs index c2605f1fd4..d7458ddf2c 100644 --- a/Realm/Realm/Sync/App.cs +++ b/Realm/Realm/Sync/App.cs @@ -23,7 +23,6 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Realms.Helpers; -using Realms.Logging; namespace Realms.Sync { @@ -110,7 +109,6 @@ public class App /// Gets the currently user. If none exists, null is returned. /// /// Valid user or null to indicate nobody logged in. - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The User instance will own its handle.")] public User? CurrentUser => Handle.TryGetCurrentUser(out var userHandle) ? new User(userHandle, this) : null; /// @@ -167,7 +165,6 @@ public static App Create(AppConfiguration config) sync_fast_reconnect_limit = (ulong)syncTimeouts.FastReconnectLimit.TotalMilliseconds, sync_ping_keep_alive_period_ms = (ulong)syncTimeouts.PingKeepAlivePeriod.TotalMilliseconds, sync_pong_keep_alive_timeout_ms = (ulong)syncTimeouts.PongKeepAliveTimeout.TotalMilliseconds, - managed_logger = Logger.Default != null ? GCHandle.ToIntPtr(Logger.Default.GCHandle) : IntPtr.Zero, }; var handle = AppHandle.CreateApp(nativeConfig, config.MetadataEncryptionKey); diff --git a/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs b/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs index 987927b10a..5d54e292d0 100644 --- a/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs +++ b/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs @@ -83,7 +83,7 @@ public void Migration_FromLittleEndianGuidFile([Values(true, false)] bool useLeg if (useLegacyRepresentation) { - Assert.That(logger.GetLog(), Is.Empty); + Assert.That(logger.GetLog(), Does.Not.Contain("Guid")); } else { @@ -123,7 +123,7 @@ public void PopulatingANewFile([Values(true, false)] bool useLegacyRepresentatio Assert.That(actualObj, Is.EqualTo(actualFound)); } - Assert.That(logger.GetLog(), Is.Empty); + Assert.That(logger.GetLog(), Does.Not.Contain("Guid")); } [Test] @@ -200,7 +200,7 @@ public void UnmigratedRealm_WhenOpenedAsReadonly_LogsAMessageAndDoesntChangeFile Assert.That(realm.Find(flippedId), Is.EqualTo(actualObj)); } - Assert.That(logger.GetLog(), Does.Contain("may contain legacy guid values but is opened as readonly so it cannot be migrated")); + Assert.That(logger.GetLog(), Does.Contain("may contain legacy Guid values but is opened as readonly so it cannot be migrated")); } [Test] @@ -235,7 +235,7 @@ public void MigratedRealm_WhenOpenedAsReadonly_DoesntDoAnything() Assert.That(actualObj, Is.EqualTo(actualFound)); } - Assert.That(logger.GetLog(), Is.Empty); + Assert.That(logger.GetLog(), Does.Not.Contain("Guid")); } [Test] diff --git a/Tests/Realm.Tests/Database/InstanceTests.cs b/Tests/Realm.Tests/Database/InstanceTests.cs index d9061ad75a..016ebc41ca 100644 --- a/Tests/Realm.Tests/Database/InstanceTests.cs +++ b/Tests/Realm.Tests/Database/InstanceTests.cs @@ -23,9 +23,11 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading.Tasks; using NUnit.Framework; using Realms.Exceptions; +using Realms.Logging; using Realms.Schema; #if TEST_WEAVER using TestRealmObject = Realms.RealmObject; @@ -1267,6 +1269,47 @@ public void RealmDispose_DisposesActiveTransaction() Assert.That(ts.State, Is.EqualTo(TransactionState.RolledBack)); } + [Test] + public void Logger_ChangeLevel_ReflectedImmediately() + { + var logger = new Logger.InMemoryLogger(); + Logger.Default = logger; + + using var realm = GetRealm(Guid.NewGuid().ToString()); + + var expectedLog = new Regex("Info: DB: [^ ]* Thread [^ ]*: Open file"); + TestHelpers.AssertRegex(logger.GetLog(), expectedLog); + Assert.That(logger.GetLog(), Does.Not.Contain("Debug")); + + WriteObject(); + + // We're at info level, so we don't expect any statements. + Assert.That(logger.GetLog(), Is.Empty); + + Logger.LogLevel = LogLevel.Debug; + WriteObject(); + + // We're at Debug level now, so we should see the write message. + var expectedWriteLog = new Regex("Debug: DB: .* Commit of size [^ ]* done in [^ ]* us"); + TestHelpers.AssertRegex(logger.GetLog(), expectedWriteLog); + + // Revert back to Info level and make sure we don't log anything + Logger.LogLevel = LogLevel.Info; + WriteObject(); + + Assert.That(logger.GetLog(), Is.Empty); + + void WriteObject() + { + logger.Clear(); + + realm.Write(() => + { + realm.Add(new IntPropertyObject()); + }); + } + } + private const int DummyDataSize = 200; private static void AddDummyData(Realm realm) diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/LoneClass_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/LoneClass_generated.cs index 3a5208725f..3e208d8452 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/LoneClass_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/LoneClass_generated.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Realms; using Realms.Exceptions; +using Realms.Logging; using Realms.Schema; using Realms.Tests.Database; using Realms.Weaving; @@ -17,6 +18,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Serialization; using TestRealmObject = Realms.IRealmObject; diff --git a/Tests/Realm.Tests/RealmTest.cs b/Tests/Realm.Tests/RealmTest.cs index ac91d88512..aa5955c48b 100644 --- a/Tests/Realm.Tests/RealmTest.cs +++ b/Tests/Realm.Tests/RealmTest.cs @@ -18,7 +18,6 @@ using System; using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -152,7 +151,6 @@ protected Realm GetRealm(string path) return result; } - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "cts is disposed by the using - the compiler seems to be having a hard time due to the ternary")] protected async Task GetRealmAsync(RealmConfigurationBase config, int timeout = 10000, CancellationToken? cancellationToken = default) { using var cts = cancellationToken != null ? null : new CancellationTokenSource(timeout); diff --git a/Tests/Realm.Tests/TestHelpers.cs b/Tests/Realm.Tests/TestHelpers.cs index 9df4ee7f00..8eed7d1a54 100644 --- a/Tests/Realm.Tests/TestHelpers.cs +++ b/Tests/Realm.Tests/TestHelpers.cs @@ -23,6 +23,7 @@ using System.Linq; using System.Net.Http; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; @@ -373,6 +374,11 @@ public static string GetResultsPath(string[] args) => args.FirstOrDefault(a => a.StartsWith("--result="))?.Replace("--result=", string.Empty) ?? throw new Exception("You must provide path to store test results with --result path/to/results.xml"); + public static void AssertRegex(string testString, Regex regex) + { + Assert.That(regex.IsMatch(testString), $"Expected {testString} to match {regex}"); + } + private class FunctionObserver : IObserver { private readonly Action _onNext; diff --git a/wrappers/realm-core b/wrappers/realm-core index dd91f5f967..087e203642 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit dd91f5f967c4ae89c37e24ab2a0315c31106648f +Subproject commit 087e203642d070450e33486f2d71450b82e2802c diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index 017451bc40..5ef4493be4 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -39,7 +39,6 @@ using namespace app; using SharedSyncUser = std::shared_ptr; -using LogMessageCallbackT = void(void* managed_handler, realm_value_t message, util::Logger::Level level); using UserCallbackT = void(void* tcs_ptr, SharedSyncUser* user, MarshaledAppError err); using VoidCallbackT = void(void* tcs_ptr, MarshaledAppError err); using StringCallbackT = void(void* tcs_ptr, realm_value_t response, MarshaledAppError err); @@ -56,7 +55,6 @@ namespace realm { std::string s_device_name; std::string s_device_version; - std::function s_log_message_callback; std::function s_user_callback; std::function s_void_callback; std::function s_string_callback; @@ -85,10 +83,6 @@ namespace realm { bool metadata_mode_has_value; - util::Logger::Level log_level; - - void* managed_logger; - void* managed_http_client; uint64_t sync_connect_timeout_ms; @@ -101,20 +95,6 @@ namespace realm { uint64_t sync_fast_reconnect_limit; }; - - class SyncLogger : public util::Logger { - public: - SyncLogger(void* delegate) - : managed_logger(delegate) - { - } - protected: - void do_log(util::Logger::Level level, const std::string& message) override final { - s_log_message_callback(managed_logger, to_capi(Mixed(message)), level); - } - private: - void* managed_logger; - }; } } @@ -129,7 +109,6 @@ extern "C" { UserCallbackT* user_callback, VoidCallbackT* void_callback, StringCallbackT* string_callback, - LogMessageCallbackT* log_message_callback, ApiKeysCallbackT* api_keys_callback) { s_framework = Utf16StringAccessor(framework, framework_len); @@ -157,7 +136,6 @@ extern "C" { s_user_callback = wrap_managed_callback(user_callback); s_void_callback = wrap_managed_callback(void_callback); s_string_callback = wrap_managed_callback(string_callback); - s_log_message_callback = wrap_managed_callback(log_message_callback); s_api_keys_callback = wrap_managed_callback(api_keys_callback); realm::binding::s_can_call_managed = true; @@ -195,7 +173,6 @@ extern "C" { } SyncClientConfig sync_client_config; - sync_client_config.log_level = app_config.log_level; sync_client_config.base_file_path = Utf16StringAccessor(app_config.base_file_path, app_config.base_file_path_len); sync_client_config.timeouts.connection_linger_time = app_config.sync_connection_linger_time_ms; sync_client_config.timeouts.connect_timeout = app_config.sync_connect_timeout_ms; @@ -219,15 +196,6 @@ extern "C" { sync_client_config.custom_encryption_key = std::vector(key.begin(), key.end()); } - void* managed_logger = app_config.managed_logger; - if (managed_logger) { - sync_client_config.logger_factory = [managed_logger](util::Logger::Level level) { - auto logger = std::make_shared(managed_logger); - logger->set_level_threshold(level); - return logger; - }; - } - return new SharedApp(App::get_shared_app(std::move(config), std::move(sync_client_config))); }); } diff --git a/wrappers/src/marshalling.cpp b/wrappers/src/marshalling.cpp index 7083681804..f5e9f7762c 100644 --- a/wrappers/src/marshalling.cpp +++ b/wrappers/src/marshalling.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "marshalling.hpp" #include "error_handling.hpp" @@ -33,6 +34,7 @@ namespace binding { } using namespace realm; +using namespace realm::util; //stringdata is utf8 //cshapbuffer is a c# stringbuilder buffer marshalled as utf16 bufsize is the size of the csharp buffer measured in 16 bit words. The buffer is in fact one char larger than that, to make room for a terminating null character @@ -68,7 +70,7 @@ size_t realm::binding::stringdata_to_csharpstringbuffer(StringData str, uint16_t size_t size = Xcode::find_utf16_buf_size(in_begin, in_end);//Figure how much space is actually needed if (in_begin != in_end) { - realm::binding::log_message(util::format("BAD UTF8 DATA IN stringdata_tocsharpbuffer: %1", str.data())); + Logger::get_default_logger()->log(Logger::Level::warn, "BAD UTF8 DATA IN stringdata_tocsharpbuffer: %1", str.data()); return -1;//bad uft8 data } if (size > bufsize) diff --git a/wrappers/src/shared_realm_cs.cpp b/wrappers/src/shared_realm_cs.cpp index 21ba8621f5..872b44d84d 100644 --- a/wrappers/src/shared_realm_cs.cpp +++ b/wrappers/src/shared_realm_cs.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -41,12 +42,13 @@ using namespace realm; using namespace realm::binding; using namespace realm::sync; +using namespace realm::util; using OpenRealmCallbackT = void(void* task_completion_source, ThreadSafeReference* ref, NativeException::Marshallable ex); using RealmChangedT = void(void* managed_state_handle); using GetNativeSchemaT = void(SchemaForMarshaling schema, void* managed_callback); using ReleaseGCHandleT = void(void* managed_handle); -using LogMessageT = void(realm_value_t message, util::Logger::Level level); +using LogMessageT = void(realm_string_t message, util::Logger::Level level); using MigrationCallbackT = void*(realm::SharedRealm* old_realm, realm::SharedRealm* new_realm, Schema* migration_schema, SchemaForMarshaling, uint64_t schema_version, void* managed_migration_handle); using HandleTaskCompletionCallbackT = void(void* tcs_ptr, bool invoke_async, NativeException::Marshallable ex); using SharedSyncSession = std::shared_ptr; @@ -96,10 +98,13 @@ namespace binding { s_realm_changed(m_managed_state_handle.handle()); } - void log_message(std::string message, util::Logger::Level level) - { - s_log_message(to_capi(Mixed(message)), level); - } + class DotNetLogger : public Logger { + protected: + void do_log(Level level, const std::string& message) override final + { + s_log_message(to_capi(message), level); + } + }; } Realm::Config get_shared_realm_config(Configuration configuration, SyncConfiguration sync_configuration, SchemaObject* objects, int objects_length, SchemaProperty* properties, uint8_t* encryption_key) @@ -250,6 +255,13 @@ REALM_EXPORT void shared_realm_install_callbacks( s_initialize_data = wrap_managed_callback(initialize_data); realm::binding::s_can_call_managed = true; + + Logger::set_default_logger(std::make_shared()); + Logger::set_default_level_threshold(Logger::Level::info); +} + +REALM_EXPORT void shared_realm_set_log_level(Logger::Level level) { + Logger::set_default_level_threshold(level); } REALM_EXPORT SharedRealm* shared_realm_open(Configuration configuration, SchemaObject* objects, int objects_length, SchemaProperty* properties, uint8_t* encryption_key, NativeException::Marshallable& ex) @@ -330,10 +342,8 @@ REALM_EXPORT SharedRealm* shared_realm_open(Configuration configuration, SchemaO auto realm = Realm::get_shared_realm(std::move(config)); if (!configuration.use_legacy_guid_representation && requires_guid_representation_fix(realm)) { if (configuration.read_only) { - static constexpr char message_format[] = "Realm at path %1 may contain legacy guid values but is opened as readonly so it cannot be migrated. This is only an issue if the file was created with Realm.NET prior to 10.10.0 and uses Guid properties. See the 10.10.0 release notes for more information."; - log_message( - util::format(message_format, realm->config().path), - realm::util::Logger::Level::warn); + static constexpr char message_format[] = "Realm at path %1 may contain legacy Guid values but is opened as readonly so it cannot be migrated. This is only an issue if the file was created with Realm.NET prior to 10.10.0 and uses Guid properties. See the 10.10.0 release notes for more information."; + Logger::get_default_logger()->log(Logger::Level::warn, message_format, realm->config().path); } else { bool found_non_v4_uuid = false; @@ -341,15 +351,11 @@ REALM_EXPORT SharedRealm* shared_realm_open(Configuration configuration, SchemaO apply_guid_representation_fix(realm, found_non_v4_uuid, found_guid_columns); if (found_non_v4_uuid) { static constexpr char message_format[] = "Realm at path %1 was found to contain Guid values in little-endian format and was automatically migrated to store them in big-endian format."; - log_message( - util::format(message_format, realm->config().path), - realm::util::Logger::Level::info); + Logger::get_default_logger()->log(Logger::Level::info, message_format, realm->config().path); } else if (found_guid_columns) { static constexpr char message_format[] = "Realm at path %1 was not marked as having migrated its Guid values, but none of the values appeared to be in little-endian format. The Realm was marked as migrated, but the values have not been modified."; - log_message( - util::format(message_format, realm->config().path), - realm::util::Logger::Level::warn); + Logger::get_default_logger()->log(Logger::Level::warn, message_format, realm->config().path); } } } diff --git a/wrappers/src/shared_realm_cs.hpp b/wrappers/src/shared_realm_cs.hpp index e95bd740e4..21a61d8936 100644 --- a/wrappers/src/shared_realm_cs.hpp +++ b/wrappers/src/shared_realm_cs.hpp @@ -183,8 +183,6 @@ class CSharpBindingContext : public BindingContext { TcsRegistryWithVersion m_pending_refresh_callbacks; }; -void log_message(std::string message, util::Logger::Level level = util::Logger::Level::info); - } // namespace bindings } // namespace realm diff --git a/wrappers/src/sync_session_cs.cpp b/wrappers/src/sync_session_cs.cpp index fb901e956a..4e7ab585aa 100644 --- a/wrappers/src/sync_session_cs.cpp +++ b/wrappers/src/sync_session_cs.cpp @@ -200,10 +200,10 @@ REALM_EXPORT void realm_syncsession_report_error_for_testing(const SharedSyncSes return; } - SyncError error{ error_code, std::move(message), is_fatal }; + sync::SessionErrorInfo error{ error_code, std::move(message), is_fatal }; error.server_requests_action = static_cast(server_requests_action); - SyncSession::OnlyForTesting::handle_error(*session, error); + SyncSession::OnlyForTesting::handle_error(*session, std::move(error)); } REALM_EXPORT void realm_syncsession_stop(const SharedSyncSession& session, NativeException::Marshallable& ex) From 176a10c04b0fcb7f1d1c8b548198afe7a2ae21d5 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Mon, 27 Mar 2023 18:59:49 +0200 Subject: [PATCH 2/5] Update Core --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 087e203642..3d5df049a2 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 087e203642d070450e33486f2d71450b82e2802c +Subproject commit 3d5df049a234af398082f23d54e1c358d7d751a5 From 50c9c693ecb2a0be90495f0a832c2b9e6a266f73 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Tue, 28 Mar 2023 03:38:52 +0200 Subject: [PATCH 3/5] Minor cleanup --- Realm/Realm/Logging/Logger.cs | 12 +++++------ Tests/Realm.Tests/Database/InstanceTests.cs | 22 ++++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Realm/Realm/Logging/Logger.cs b/Realm/Realm/Logging/Logger.cs index 7ca0e7d9d0..a8f5f7ea82 100644 --- a/Realm/Realm/Logging/Logger.cs +++ b/Realm/Realm/Logging/Logger.cs @@ -36,7 +36,7 @@ public abstract class Logger private readonly Lazy _gcHandle; private static Logger? _defaultLogger; - private static LogLevel logLevel = LogLevel.Info; + private static LogLevel _logLevel = LogLevel.Info; /// /// Gets a that outputs messages to the default console. For most project types, that will be @@ -89,10 +89,10 @@ public abstract class Logger /// The log level for Realm-originating messages. public static LogLevel LogLevel { - get => logLevel; + get => _logLevel; set { - logLevel = value; + _logLevel = value; SharedRealmHandle.SetLogLevel(value); } } @@ -206,10 +206,10 @@ internal class InMemoryLogger : Logger protected override void LogImpl(LogLevel level, string message) { lock (_builder) - { - _builder.AppendLine(FormatLog(level, message)); - } + { + _builder.AppendLine(FormatLog(level, message)); } + } public string GetLog() { diff --git a/Tests/Realm.Tests/Database/InstanceTests.cs b/Tests/Realm.Tests/Database/InstanceTests.cs index 016ebc41ca..9d3e9647ab 100644 --- a/Tests/Realm.Tests/Database/InstanceTests.cs +++ b/Tests/Realm.Tests/Database/InstanceTests.cs @@ -1281,25 +1281,20 @@ public void Logger_ChangeLevel_ReflectedImmediately() TestHelpers.AssertRegex(logger.GetLog(), expectedLog); Assert.That(logger.GetLog(), Does.Not.Contain("Debug")); - WriteObject(); - // We're at info level, so we don't expect any statements. - Assert.That(logger.GetLog(), Is.Empty); + WriteAndVerifyLogs(); Logger.LogLevel = LogLevel.Debug; - WriteObject(); // We're at Debug level now, so we should see the write message. var expectedWriteLog = new Regex("Debug: DB: .* Commit of size [^ ]* done in [^ ]* us"); - TestHelpers.AssertRegex(logger.GetLog(), expectedWriteLog); + WriteAndVerifyLogs(expectedWriteLog); // Revert back to Info level and make sure we don't log anything Logger.LogLevel = LogLevel.Info; - WriteObject(); - - Assert.That(logger.GetLog(), Is.Empty); + WriteAndVerifyLogs(); - void WriteObject() + void WriteAndVerifyLogs(Regex? expectedRegex = null) { logger.Clear(); @@ -1307,6 +1302,15 @@ void WriteObject() { realm.Add(new IntPropertyObject()); }); + + if (expectedRegex == null) + { + Assert.That(logger.GetLog(), Is.Empty); + } + else + { + TestHelpers.AssertRegex(logger.GetLog(), expectedRegex); + } } } From 48276bcc2deac3402894ce8d901bf024d1a4d950 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Tue, 28 Mar 2023 03:43:22 +0200 Subject: [PATCH 4/5] Fix changelog --- CHANGELOG.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3279f6ec..7d26b6c081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,28 +50,16 @@ ### Enhancements * Added nullability annotations to the Realm assembly. Now methods returning reference types are correctly annotated to indicate whether the returned value may or may not be null. (Issue [#3248](https://github.com/realm/realm-dotnet/issues/3248)) * Replacing a value at an index (i.e. `myList[1] = someObj`) will now correctly `CollectionChange` notifications with the `Replace` action. (Issue [#2854](https://github.com/realm/realm-dotnet/issues/2854)) +* It is now possible to change the log level at any point of the application's lifetime. (PR [#3277](https://github.com/realm/realm-dotnet/pull/3277)) +* Some log messages have been added to the Core database. Events, such as opening a Realm or committing a transaction will now be logged. (Issue [#2910](https://github.com/realm/realm-dotnet/issues/2910)) ### Fixed -### Compatibility -* Realm Studio: 12.0.0 or later. - -### Internal -* Using Core x.y.z. - -## vNext (TBD) - -### Enhancements -* None - -### Fixed -* None - ### Compatibility * Realm Studio: 13.0.0 or later. ### Internal -* Using Core x.y.z. +* Using Core 13.8.0. ## 10.21.0 (2023-03-24) From f2417dcf5440836903bb9cc97e83ebe0654a0add Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Fri, 31 Mar 2023 14:02:16 +0200 Subject: [PATCH 5/5] Update Core --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 3d5df049a2..a12241b0f3 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 3d5df049a234af398082f23d54e1c358d7d751a5 +Subproject commit a12241b0f3ee92752b83b4981f3afb862b6ffa42