From 8593bc3b1ce17eea8cf3feb0282e6cec023ed679 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Thu, 10 Apr 2025 07:14:28 +0300 Subject: [PATCH 1/4] chore(Orleans.Redis): Use [LoggerMessage] --- .../RedisGrainDirectory.cs | 109 +++++++++++++----- .../Storage/RedisGrainStorage.cs | 76 ++++++------ .../Storage/RedisReminderTable.cs | 18 ++- 3 files changed, 135 insertions(+), 68 deletions(-) diff --git a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs index 0d8472265db..56d7b2d2ad7 100644 --- a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs +++ b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Net; using System.Text; using System.Text.Json; using System.Threading; @@ -14,7 +15,7 @@ namespace Orleans.GrainDirectory.Redis { - public class RedisGrainDirectory : IGrainDirectory, ILifecycleParticipant + public partial class RedisGrainDirectory : IGrainDirectory, ILifecycleParticipant { private readonly RedisGrainDirectoryOptions _directoryOptions; private readonly ClusterOptions _clusterOptions; @@ -44,8 +45,7 @@ public RedisGrainDirectory( { var result = (string?)await _database.StringGetAsync(GetKey(grainId)); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug("Lookup {GrainId}: {Result}", grainId, string.IsNullOrWhiteSpace(result) ? "null" : result); + LogDebugLookup(grainId, string.IsNullOrWhiteSpace(result) ? "null" : result); if (string.IsNullOrWhiteSpace(result)) return default; @@ -54,7 +54,7 @@ public RedisGrainDirectory( } catch (Exception ex) { - _logger.LogError(ex, "Lookup failed for {GrainId}", grainId); + LogErrorLookupFailed(ex, grainId); if (IsRedisException(ex)) throw new OrleansException($"Lookup failed for {grainId} : {ex}"); @@ -64,12 +64,12 @@ public RedisGrainDirectory( } public Task Register(GrainAddress address) => Register(address, null); - + public async Task Register(GrainAddress address, GrainAddress? previousAddress) { const string RegisterScript = """ - local cur = redis.call('GET', KEYS[1]) + local cur = redis.call('GET', KEYS[1]) local success = true if cur ~= false then local typedCur = cjson.decode(cur) @@ -82,7 +82,7 @@ public RedisGrainDirectory( redis.call('SET', KEYS[1], ARGV[1]) if ARGV[3] ~= '-1' then redis.call('EXPIRE', KEYS[1], ARGV[3]) - end + end return nil end @@ -101,24 +101,18 @@ public RedisGrainDirectory( if (entryString is null) { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("Registered {GrainId} ({Address})", address.GrainId, value); - } + LogDebugRegistered(address.GrainId, value); return address; } - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("Failed to register {GrainId} ({Address}) in directory: Conflicted with existing value, {Result}", address.GrainId, value, entryString); - } + LogDebugRegisterFailed(address.GrainId, value, entryString); return JsonSerializer.Deserialize(entryString); } catch (Exception ex) { - _logger.LogError(ex, "Failed to register {GrainId} ({Address}) in directory", address.GrainId, value); + LogErrorRegisterFailed(ex, address.GrainId, value); if (IsRedisException(ex)) { @@ -135,7 +129,7 @@ public async Task Unregister(GrainAddress address) { const string DeleteScript = """ - local cur = redis.call('GET', KEYS[1]) + local cur = redis.call('GET', KEYS[1]) if cur ~= false then local typedCur = cjson.decode(cur) if typedCur.ActivationId == ARGV[1] then @@ -153,16 +147,13 @@ public async Task Unregister(GrainAddress address) keys: new RedisKey[] { GetKey(address.GrainId) }, values: new RedisValue[] { address.ActivationId.ToString() }); - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("Unregister {GrainId} ({Address}): {Result}", address.GrainId, JsonSerializer.Serialize(address), (result != 0) ? "OK" : "Conflict"); - } + LogDebugUnregister(address.GrainId, JsonSerializer.Serialize(address), (result != 0) ? "OK" : "Conflict"); } catch (Exception ex) { var value = JsonSerializer.Serialize(address); - _logger.LogError(ex, "Unregister failed for {GrainId} ({Address})", address.GrainId, value); + LogErrorUnregisterFailed(ex, address.GrainId, value); if (IsRedisException(ex)) throw new OrleansException($"Unregister failed for {address.GrainId} ({value}) : {ex}"); @@ -209,16 +200,82 @@ private async Task Uninitialize(CancellationToken arg) #region Logging private void LogConnectionRestored(object? sender, ConnectionFailedEventArgs e) - => _logger.LogInformation(e.Exception, "Connection to {EndPoint} failed: {FailureType}", e.EndPoint, e.FailureType); + => LogInfoConnectionRestored(e.Exception, e.EndPoint, e.FailureType); private void LogConnectionFailed(object? sender, ConnectionFailedEventArgs e) - => _logger.LogError(e.Exception, "Connection to {EndPoint} failed: {FailureType}", e.EndPoint, e.FailureType); + => LogErrorConnectionFailed(e.Exception, e.EndPoint, e.FailureType); private void LogErrorMessage(object? sender, RedisErrorEventArgs e) - => _logger.LogError(e.Message); + => LogErrorRedisMessage(e.Message); private void LogInternalError(object? sender, InternalErrorEventArgs e) - => _logger.LogError(e.Exception, "Internal error"); + => LogErrorInternalError(e.Exception); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Lookup {GrainId}: {Result}" + )] + private partial void LogDebugLookup(GrainId grainId, string result); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Lookup failed for {GrainId}" + )] + private partial void LogErrorLookupFailed(Exception exception, GrainId grainId); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Registered {GrainId} ({Address})" + )] + private partial void LogDebugRegistered(GrainId grainId, string address); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Failed to register {GrainId} ({Address}) in directory: Conflicted with existing value, {Result}" + )] + private partial void LogDebugRegisterFailed(GrainId grainId, string address, string result); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to register {GrainId} ({Address}) in directory" + )] + private partial void LogErrorRegisterFailed(Exception exception, GrainId grainId, string address); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Unregister {GrainId} ({Address}): {Result}" + )] + private partial void LogDebugUnregister(GrainId grainId, string address, string result); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Unregister failed for {GrainId} ({Address})" + )] + private partial void LogErrorUnregisterFailed(Exception exception, GrainId grainId, string address); + + [LoggerMessage( + Level = LogLevel.Information, + Message = "Connection to {EndPoint} restored: {FailureType}" + )] + private partial void LogInfoConnectionRestored(Exception? exception, EndPoint? endPoint, ConnectionFailureType failureType); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Connection to {EndPoint} failed: {FailureType}" + )] + private partial void LogErrorConnectionFailed(Exception? exception, EndPoint? endPoint, ConnectionFailureType failureType); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "{Message}" + )] + private partial void LogErrorRedisMessage(string message); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Internal error" + )] + private partial void LogErrorInternalError(Exception? exception); #endregion // These exceptions are not serializable by the client diff --git a/src/Redis/Orleans.Persistence.Redis/Storage/RedisGrainStorage.cs b/src/Redis/Orleans.Persistence.Redis/Storage/RedisGrainStorage.cs index 47606763790..0c8115fd14a 100644 --- a/src/Redis/Orleans.Persistence.Redis/Storage/RedisGrainStorage.cs +++ b/src/Redis/Orleans.Persistence.Redis/Storage/RedisGrainStorage.cs @@ -20,7 +20,7 @@ namespace Orleans.Persistence /// /// Redis-based grain storage provider /// - public class RedisGrainStorage : IGrainStorage, ILifecycleParticipant + public partial class RedisGrainStorage : IGrainStorage, ILifecycleParticipant { private readonly string _serviceId; private readonly RedisValue _ttl; @@ -65,42 +65,22 @@ public void Participate(ISiloLifecycle lifecycle) private async Task Init(CancellationToken cancellationToken) { - var timer = Stopwatch.StartNew(); + var startTime = Stopwatch.GetTimestamp(); try { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug( - "RedisGrainStorage {Name} is initializing: ServiceId={ServiceId} DeleteOnClear={DeleteOnClear}", - _name, - _serviceId, - _options.DeleteStateOnClear); - } + LogDebugInitializing(_name, _serviceId, _options.DeleteStateOnClear); _connection = await _options.CreateMultiplexer(_options).ConfigureAwait(false); _db = _connection.GetDatabase(); - if (_logger.IsEnabled(LogLevel.Debug)) - { - timer.Stop(); - _logger.LogDebug( - "Init: Name={Name} ServiceId={ServiceId}, initialized in {ElapsedMilliseconds} ms", - _name, - _serviceId, - timer.Elapsed.TotalMilliseconds.ToString("0.00")); - } + var elapsed = Stopwatch.GetElapsedTime(startTime); + LogDebugInitialized(_name, _serviceId, elapsed.TotalMilliseconds); } catch (Exception ex) { - timer.Stop(); - _logger.LogError( - ex, - "Init: Name={Name} ServiceId={ServiceId}, errored in {ElapsedMilliseconds} ms.", - _name, - _serviceId, - timer.Elapsed.TotalMilliseconds.ToString("0.00")); - + var elapsed = Stopwatch.GetElapsedTime(startTime); + LogErrorInitFailed(ex, _name, _serviceId, elapsed.TotalMilliseconds); throw new RedisStorageException(Invariant($"{ex.GetType()}: {ex.Message}")); } } @@ -139,11 +119,7 @@ public async Task ReadStateAsync(string grainType, GrainId grainId, IGrainSta } catch (Exception exception) { - _logger.LogError( - "Failed to read grain state for {GrainType} grain with ID {GrainId} and storage key {Key}.", - grainType, - grainId, - key); + LogErrorReadStateFailed(exception, grainType, grainId, key); throw new RedisStorageException(Invariant($"Failed to read grain state for {grainType} with ID {grainId} and storage key {key}. {exception.GetType()}: {exception.Message}")); } } @@ -186,11 +162,7 @@ public async Task WriteStateAsync(string grainType, GrainId grainId, IGrainSt } catch (Exception exception) when (exception is not InconsistentStateException) { - _logger.LogError( - "Failed to write grain state for {GrainType} grain with ID {GrainId} and storage key {Key}.", - grainType, - grainId, - key); + LogErrorWriteStateFailed(exception, grainType, grainId, key); throw new RedisStorageException( Invariant($"Failed to write grain state for {grainType} grain with ID {grainId} and storage key {key}. {exception.GetType()}: {exception.Message}")); } @@ -288,5 +260,35 @@ private async Task Close(CancellationToken cancellationToken) } private T CreateInstance() => _activatorProvider.GetActivator().Create(); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "RedisGrainStorage {Name} is initializing: ServiceId={ServiceId} DeleteOnClear={DeleteOnClear}" + )] + private partial void LogDebugInitializing(string name, string serviceId, bool deleteOnClear); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Init: Name={Name} ServiceId={ServiceId}, initialized in {ElapsedMilliseconds} ms" + )] + private partial void LogDebugInitialized(string name, string serviceId, double elapsedMilliseconds); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Init: Name={Name} ServiceId={ServiceId}, errored in {ElapsedMilliseconds} ms." + )] + private partial void LogErrorInitFailed(Exception exception, string name, string serviceId, double elapsedMilliseconds); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to read grain state for {GrainType} grain with ID {GrainId} and storage key {Key}." + )] + private partial void LogErrorReadStateFailed(Exception exception, string grainType, GrainId grainId, RedisKey key); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to write grain state for {GrainType} grain with ID {GrainId} and storage key {Key}." + )] + private partial void LogErrorWriteStateFailed(Exception exception, string grainType, GrainId grainId, RedisKey key); } } diff --git a/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs b/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs index e355f942fd4..f78df39182e 100644 --- a/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs +++ b/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs @@ -18,7 +18,7 @@ namespace Orleans.Reminders.Redis { - internal class RedisReminderTable : IReminderTable + internal partial class RedisReminderTable : IReminderTable { private readonly RedisKey _hashSetKey; private readonly RedisReminderTableOptions _redisOptions; @@ -175,10 +175,7 @@ public async Task UpsertRow(ReminderEntry entry) try { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("UpsertRow entry = {Entry}, ETag = {ETag}", entry.ToString(), entry.ETag); - } + LogDebugUpsertRow(entry, entry.ETag); var (newETag, value) = ConvertFromEntry(entry); var (from, to) = GetFilter(entry.GrainId, entry.ReminderName); @@ -247,5 +244,16 @@ private static ReminderEntry ConvertToEntry(string reminderValue) return (eTag, JsonConvert.SerializeObject(segments, _jsonSettings)[1..^1]); } + + private readonly struct ReminderEntryLogValue(ReminderEntry entry) + { + public override string ToString() => entry.ToString(); + } + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "UpsertRow entry = {Entry}, ETag = {ETag}" + )] + private partial void LogDebugUpsertRow(ReminderEntryLogValue entry, string eTag); } } From bdb87ce4abfaf41aaec7f99974014843426cb33b Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Thu, 10 Apr 2025 18:45:38 +0300 Subject: [PATCH 2/4] chore(Orleans.Redis): Use [LoggerMessage] --- src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs b/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs index f78df39182e..80df076b138 100644 --- a/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs +++ b/src/Redis/Orleans.Reminders.Redis/Storage/RedisReminderTable.cs @@ -175,7 +175,7 @@ public async Task UpsertRow(ReminderEntry entry) try { - LogDebugUpsertRow(entry, entry.ETag); + LogDebugUpsertRow(new(entry), entry.ETag); var (newETag, value) = ConvertFromEntry(entry); var (from, to) = GetFilter(entry.GrainId, entry.ReminderName); From bdf27e4260c410a60359748c601cae6269ed46c0 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Sun, 20 Apr 2025 06:49:59 +0300 Subject: [PATCH 3/4] chore(Orleans.Redis): Use [LoggerMessage] --- .../Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs index 56d7b2d2ad7..26baa003d7e 100644 --- a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs +++ b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs @@ -147,7 +147,7 @@ public async Task Unregister(GrainAddress address) keys: new RedisKey[] { GetKey(address.GrainId) }, values: new RedisValue[] { address.ActivationId.ToString() }); - LogDebugUnregister(address.GrainId, JsonSerializer.Serialize(address), (result != 0) ? "OK" : "Conflict"); + LogDebugUnregister(address.GrainId, new(address), (result != 0) ? "OK" : "Conflict"); } catch (Exception ex) { @@ -241,11 +241,16 @@ private void LogInternalError(object? sender, InternalErrorEventArgs e) )] private partial void LogErrorRegisterFailed(Exception exception, GrainId grainId, string address); + private readonly struct GrainAddressLogRecord(GrainAddress address) + { + public override string ToString() => JsonSerializer.Serialize(address); + } + [LoggerMessage( Level = LogLevel.Debug, Message = "Unregister {GrainId} ({Address}): {Result}" )] - private partial void LogDebugUnregister(GrainId grainId, string address, string result); + private partial void LogDebugUnregister(GrainId grainId, GrainAddressLogRecord address, string result); [LoggerMessage( Level = LogLevel.Error, From f529ad2a9da085152afc5a307345b08beaddb2a1 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Sun, 20 Apr 2025 07:13:57 +0300 Subject: [PATCH 4/4] chore(Orleans.Redis): Use [LoggerMessage] --- .../Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs index 26baa003d7e..dfe7ea49dcc 100644 --- a/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs +++ b/src/Redis/Orleans.GrainDirectory.Redis/RedisGrainDirectory.cs @@ -151,12 +151,10 @@ public async Task Unregister(GrainAddress address) } catch (Exception ex) { - var value = JsonSerializer.Serialize(address); - - LogErrorUnregisterFailed(ex, address.GrainId, value); + LogErrorUnregisterFailed(ex, address.GrainId, new(address)); if (IsRedisException(ex)) - throw new OrleansException($"Unregister failed for {address.GrainId} ({value}) : {ex}"); + throw new OrleansException($"Unregister failed for {address.GrainId} ({JsonSerializer.Serialize(address)}) : {ex}"); else throw; } @@ -256,7 +254,7 @@ private readonly struct GrainAddressLogRecord(GrainAddress address) Level = LogLevel.Error, Message = "Unregister failed for {GrainId} ({Address})" )] - private partial void LogErrorUnregisterFailed(Exception exception, GrainId grainId, string address); + private partial void LogErrorUnregisterFailed(Exception exception, GrainId grainId, GrainAddressLogRecord address); [LoggerMessage( Level = LogLevel.Information,