Skip to content

Commit

Permalink
RNET-1161: Implement support for using a log level for a specific log…
Browse files Browse the repository at this point in the history
… category (#3634)

* Implement 'LogCategory' classes.

* Update Core wrapper for setting log level.

* Update 'SharedRealmHandle' for logging and setting log level and category.

* Update 'Logger' classes to account for category.

* Replace static '_logLevel' member with call into Core.

* Update signature in 'UnityLogger'.

* Replace use of 'LeafLogCategory' with the base.

* Remove 'LogCategory' getter.

* Update todo comments.

* Add first tests.

* Log with SDK category if not provided.

* Pass parent category to create name.

* Marshal log category names from Core.

* Test matching Core category names.

* Add API docs.

* Add more tests.

* Show category hierarchy in API docs.

* Add CHANGELOG entry.

* Change arg. order to not break 'LogImpl()' API.

* Deprecate old APIs.

* Update CHANGELOG.

* Point to updated Core.

* Change marshaled vector from heap-allocated to global.

* Update 'shared_realm_get_log_level'.

* Update usage of now-deprecated APIs.

* Update API doc.

* Use braced initializer when returning marshaled vector.

* Try to fix Windows marshaling.

* Make marshallable struct unnested.

* Make marshalable nested.

* Fix Windows marshaling once and for all.

* Fix Windows marshaling twice and for all

* Deprecate 'Logger' and use its base class.

* Check log level for 'Realm.SDK' in 'SyncSocketProvider'.

* Move marshalling from 'MarshaledVector' onto 'TypeErasedMarshaledVector'.

* Minor update to API docs.

* Update 'cref's to resolve doc links.

* Add a log API for Core-only messages to avoid checking level.

* Make arg order in wrapper consistent.

* Call non-level-check log impl from 'Log()'.

* Rename 'CoreLog' to 'LogAnyLevel' to suit current call pattern.

* Remove internal 'LogDefault()'.

* Update CHANGELOG.

---------

Co-authored-by: nirinchev <[email protected]>
  • Loading branch information
elle-j and nirinchev authored Jul 12, 2024
1 parent 4bc3057 commit 84940b3
Show file tree
Hide file tree
Showing 27 changed files with 732 additions and 169 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
## vNext (TBD)

### Deprecations
* The `Logger` has been deprecated in favor of `RealmLogger`, which `Logger` currently derives from. (PR [#3634](https://github.com/realm/realm-dotnet/pull/3634))
* The `Logger.LogLevel` `set` and `get` accessors have been deprecated. Please use `RealmLogger.SetLogLevel()` and `RealmLogger.GetLogLevel()` (see **Enhancements** below).
* The `Logger.Function(Action<LogLevel, string> logFunction)` have been deprecated. Please use `RealmLogger.Function(Action<LogLevel, LogCategory, string> logFunction)` (see **Enhancements** below).

### Enhancements
* Allow `ShouldCompactOnLaunch` to be set on `SyncConfiguration`, not only `RealmConfiguration`. (Issue [#3617](https://github.com/realm/realm-dotnet/issues/3617))
* Reduce the size of the local transaction log produced by creating objects, improving the performance of insertion-heavy transactions (Core 14.10.0).
* Performance has been improved for range queries on integers and timestamps. Requires that you use the "BETWEEN" operation in `Realm.All<T>().Filter(...)`. (Core 14.10.1)
* Allowed `ShouldCompactOnLaunch` to be set on `SyncConfiguration`, not only `RealmConfiguration`. (Issue [#3617](https://github.com/realm/realm-dotnet/issues/3617))
* Introduced a `LogCategory` and allowed for more control over which category of messages should be logged and at which criticality level. (PR [#3634](https://github.com/realm/realm-dotnet/pull/3634))
* Allowed setting and getting a `LogLevel` for a given `LogCategory`. The hierarchy of categories starts at `LogCategory.Realm`.
```csharp
RealmLogger.SetLogLevel(LogLevel.Warn, LogCategory.Realm.Sync);
RealmLogger.GetLogLevel(LogCategory.Realm.Sync.Client.Session); // LogLevel.Warn
```
* Added a function logger that accepts a callback that will receive the `LogLevel`, `LogCategory`, and the message when invoked.
```csharp
RealmLogger.Default = RealmLogger.Function((level, category, message) => /* custom implementation */);
```
* Added a `RealmLogger.Log()` overload taking a category. The pre-existing `Log()` API will implicitly log at `LogCategory.Realm.SDK`.
```csharp
RealmLogger.Default.Log(LogLevel.Warn, LogCategory.Realm, "A warning message");
```

### Fixed
* A `ForCurrentlyOutstandingWork` progress notifier would not immediately call its callback after registration. Instead you would have to wait for some data to be received to get your first update - if you were already caught up when you registered the notifier you could end up waiting a long time for the server to deliver a download that would call/expire your notifier. (Core 14.8.0)
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm.UnityUtils/Initializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static void Initialize()
Platform.DeviceInfo = new UnityDeviceInfo();
Platform.BundleId = Application.productName;
InteropConfig.AddPotentialStorageFolder(FileHelper.GetStorageFolder());
Realms.Logging.Logger.Console = new UnityLogger();
Realms.Logging.RealmLogger.Console = new UnityLogger();
Application.quitting += () =>
{
NativeCommon.CleanupNativeResources("Application is exiting");
Expand Down
9 changes: 6 additions & 3 deletions Realm/Realm.UnityUtils/UnityLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@

namespace UnityUtils
{
public class UnityLogger : Logger
/// <summary>
/// A <see cref="RealmLogger"/> that outputs messages via UnityEngine.
/// </summary>
public class UnityLogger : RealmLogger
{
protected override void LogImpl(LogLevel level, string message)
protected override void LogImpl(LogLevel level, LogCategory category, string message)
{
var toLog = FormatLog(level, message);
var toLog = FormatLog(level, category!, message);
switch (level)
{
case LogLevel.Fatal:
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/RealmHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ protected override bool ReleaseHandle()
}
catch(Exception ex)
{
Logger.Default.Log(LogLevel.Error, $"An error occurred while closing native handle. Please file an issue at https://github.com/realm/realm-dotnet/issues. Error: {ex}");
RealmLogger.Default.Log(LogLevel.Error, $"An error occurred while closing native handle. Please file an issue at https://github.com/realm/realm-dotnet/issues. Error: {ex}");
Debug.Fail($"Failed to close native handle: {ex}");

// it would be really bad if we got an exception in here. We must not pass it on, but have to return false
Expand Down
8 changes: 4 additions & 4 deletions Realm/Realm/Handles/SessionHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ private static void HandleSessionError(IntPtr sessionHandlePtr, SyncError error,
}
catch (Exception ex)
{
Logger.Default.Log(LogLevel.Warn, $"An error has occurred while handling a session error: {ex}");
RealmLogger.Default.Log(LogLevel.Warn, $"An error has occurred while handling a session error: {ex}");
}
}

Expand Down Expand Up @@ -359,7 +359,7 @@ private static IntPtr NotifyBeforeClientReset(IntPtr beforeFrozen, IntPtr manage
catch (Exception ex)
{
var handlerType = syncConfig is null ? "ClientResetHandler" : syncConfig.ClientResetHandler.GetType().Name;
Logger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnBeforeReset during a client reset: {ex}");
RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnBeforeReset during a client reset: {ex}");

var exHandle = GCHandle.Alloc(ex);
return GCHandle.ToIntPtr(exHandle);
Expand Down Expand Up @@ -397,7 +397,7 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after,
catch (Exception ex)
{
var handlerType = syncConfig is null ? "ClientResetHandler" : syncConfig.ClientResetHandler.GetType().Name;
Logger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnAfterReset during a client reset: {ex}");
RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnAfterReset during a client reset: {ex}");

var exHandle = GCHandle.Alloc(ex);
return GCHandle.ToIntPtr(exHandle);
Expand Down Expand Up @@ -460,7 +460,7 @@ private static void HandleSessionPropertyChangedCallback(IntPtr managedSessionHa
}
catch (Exception ex)
{
Logger.Default.Log(LogLevel.Error, $"An error has occurred while raising a property changed event: {ex}");
RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while raising a property changed event: {ex}");
}
}

Expand Down
36 changes: 31 additions & 5 deletions Realm/Realm/Handles/SharedRealmHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -48,6 +49,17 @@ internal class SharedRealmHandle : StandaloneHandle

private static class NativeMethods
{
// This is a wrapper struct around MarshaledVector since P/Invoke doesn't like it
// when the MarshaledVector is returned as the top-level return value from a native
// function. This only manifests in .NET Framework and is not an issue with Mono/.NET.
// The native return value is MarshaledVector without the wrapper because they are binary
// compatible.
[StructLayout(LayoutKind.Sequential)]
public struct CategoryNamesContainer
{
public MarshaledVector<StringValue> CategoryNames;
}

#pragma warning disable IDE0049 // Use built-in type alias
#pragma warning disable SA1121 // Use built-in type alias

Expand All @@ -64,7 +76,7 @@ private static class NativeMethods
public delegate void DisposeGCHandleCallback(IntPtr handle);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogMessageCallback(StringValue message, LogLevel level);
public delegate void LogMessageCallback(LogLevel level, StringValue categoryName, StringValue message);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void HandleTaskCompletionCallback(IntPtr tcs_ptr, [MarshalAs(UnmanagedType.U1)] bool invoke_async, NativeException ex);
Expand Down Expand Up @@ -223,8 +235,14 @@ 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_get_log_level", CallingConvention = CallingConvention.Cdecl)]
public static extern LogLevel get_log_level([MarshalAs(UnmanagedType.LPWStr)] string category_name, IntPtr category_name_len);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_set_log_level", CallingConvention = CallingConvention.Cdecl)]
public static extern bool set_log_level(LogLevel level);
public static extern void set_log_level(LogLevel level, [MarshalAs(UnmanagedType.LPWStr)] string category_name, IntPtr category_name_len);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_log_category_names", CallingConvention = CallingConvention.Cdecl)]
public static extern CategoryNamesContainer get_log_category_names();

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_operating_system", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_operating_system(IntPtr buffer, IntPtr buffer_length);
Expand Down Expand Up @@ -271,7 +289,15 @@ public static unsafe void Initialize()
notifyObject, notifyDictionary, onMigration, shouldCompact, handleTaskCompletion, onInitialization);
}

public static void SetLogLevel(LogLevel level) => NativeMethods.set_log_level(level);
public static LogLevel GetLogLevel(LogCategory category) => NativeMethods.get_log_level(category.Name, (IntPtr)category.Name.Length);

public static void SetLogLevel(LogLevel level, LogCategory category) => NativeMethods.set_log_level(level, category.Name, (IntPtr)category.Name.Length);

public static string[] GetLogCategoryNames() => NativeMethods.get_log_category_names()
.CategoryNames
.ToEnumerable()
.Select(name => name.ToDotnetString()!)
.ToArray();

[Preserve]
protected SharedRealmHandle(IntPtr handle) : base(handle)
Expand Down Expand Up @@ -822,9 +848,9 @@ private static void OnDisposeGCHandle(IntPtr handle)
}

[MonoPInvokeCallback(typeof(NativeMethods.LogMessageCallback))]
private static void LogMessage(StringValue message, LogLevel level)
private static void LogMessage(LogLevel level, StringValue categoryName, StringValue message)
{
Logger.LogDefault(level, message!);
RealmLogger.Default.LogAnyLevel(level, LogCategory.FromName(categoryName!), message!);
}

[MonoPInvokeCallback(typeof(NativeMethods.MigrationCallback))]
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/SyncUserHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ private static void HandleUserChanged(IntPtr managedUserHandle)
}
catch (Exception ex)
{
Logger.Default.Log(LogLevel.Error, $"An error has occurred while raising User.Changed event: {ex}");
RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while raising User.Changed event: {ex}");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Helpers/Argument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static T ValidateNotNull<T>(T value, string paramName)

public static void AssertDebug(string message)
{
Logger.LogDefault(LogLevel.Error, $"{message} {OpenIssueText}");
RealmLogger.Default.Log(LogLevel.Error, $"{message} {OpenIssueText}");

#if DEBUG
throw new Exception(message);
Expand Down
Loading

0 comments on commit 84940b3

Please sign in to comment.