From 607039675b6f02579e4223d8d766c81cae0644be Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Thu, 24 Jul 2025 18:03:19 +0300 Subject: [PATCH] Replaces string interpolation with structured logging Converts manual log message formatting to LoggerMessage source generator methods for better performance and structured logging support. Improves logging efficiency by eliminating string allocations when logging is disabled and provides consistent structured data for log analysis tools. --- .../ConnectionMultiplexer.Sentinel.cs | 2 +- .../ConnectionMultiplexer.cs | 12 ++--- src/StackExchange.Redis/EndPointCollection.cs | 6 +-- src/StackExchange.Redis/LoggerExtensions.cs | 44 +++++++++++++++++++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs index 88e42ed5e..b1bf7371c 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs @@ -396,7 +396,7 @@ internal void SwitchPrimary(EndPoint? switchBlame, ConnectionMultiplexer connect var logger = Logger.With(writer); if (connection.RawConfig.ServiceName is not string serviceName) { - logger?.LogInformation("Service name not defined."); + logger?.LogInformationServiceNameNotDefined(); return; } diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.cs b/src/StackExchange.Redis/ConnectionMultiplexer.cs index 146f7d2d3..bf1b75e25 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.cs @@ -1335,14 +1335,16 @@ internal void GetStatus(ILogger? log) if (log == null) return; var tmp = GetServerSnapshot(); - log?.LogInformation("Endpoint Summary:"); + log.LogInformationEndpointSummaryHeader(); foreach (var server in tmp) { - log?.LogInformation(" " + server.Summary()); - log?.LogInformation(" " + server.GetCounters().ToString()); - log?.LogInformation(" " + server.GetProfile()); + log.LogInformationServerSummary(server.Summary(), server.GetCounters(), server.GetProfile()); } - log?.LogInformation($"Sync timeouts: {Interlocked.Read(ref syncTimeouts)}; async timeouts: {Interlocked.Read(ref asyncTimeouts)}; fire and forget: {Interlocked.Read(ref fireAndForgets)}; last heartbeat: {LastHeartbeatSecondsAgo}s ago"); + log.LogInformationTimeoutsSummary( + Interlocked.Read(ref syncTimeouts), + Interlocked.Read(ref asyncTimeouts), + Interlocked.Read(ref fireAndForgets), + LastHeartbeatSecondsAgo); } private void ActivateAllServers(ILogger? log) diff --git a/src/StackExchange.Redis/EndPointCollection.cs b/src/StackExchange.Redis/EndPointCollection.cs index cf4c844c1..359f6d811 100644 --- a/src/StackExchange.Redis/EndPointCollection.cs +++ b/src/StackExchange.Redis/EndPointCollection.cs @@ -209,12 +209,12 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, ILo } else { - log?.LogInformation($"Using DNS to resolve '{dns.Host}'..."); + log?.LogInformationUsingDnsToResolve(dns.Host); var ips = await Dns.GetHostAddressesAsync(dns.Host).ObserveErrors().ForAwait(); if (ips.Length == 1) { ip = ips[0]; - log?.LogInformation($"'{dns.Host}' => {ip}"); + log?.LogInformationDnsResolutionResult(dns.Host, ip); cache[dns.Host] = ip; this[i] = new IPEndPoint(ip, dns.Port); } @@ -223,7 +223,7 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, ILo catch (Exception ex) { multiplexer.OnInternalError(ex); - log?.LogError(ex, ex.Message); + log?.LogErrorDnsResolution(ex, ex.Message); } } } diff --git a/src/StackExchange.Redis/LoggerExtensions.cs b/src/StackExchange.Redis/LoggerExtensions.cs index 759c3949b..be51733ce 100644 --- a/src/StackExchange.Redis/LoggerExtensions.cs +++ b/src/StackExchange.Redis/LoggerExtensions.cs @@ -665,4 +665,48 @@ internal static void LogWithThreadPoolStats(this ILogger? log, string message) EventId = 100, Message = "{BridgeName}: Connected")] internal static partial void LogInformationConnected(this ILogger logger, string bridgeName); + + // ConnectionMultiplexer GetStatus logging methods + [LoggerMessage( + Level = LogLevel.Information, + EventId = 101, + Message = "Endpoint Summary:")] + internal static partial void LogInformationEndpointSummaryHeader(this ILogger logger); + + [LoggerMessage( + Level = LogLevel.Information, + EventId = 102, + Message = "Server summary: {ServerSummary}, counters: {ServerCounters}, profile: {ServerProfile}")] + internal static partial void LogInformationServerSummary(this ILogger logger, string serverSummary, ServerCounters serverCounters, string serverProfile); + + [LoggerMessage( + Level = LogLevel.Information, + EventId = 105, + Message = "Sync timeouts: {SyncTimeouts}; async timeouts: {AsyncTimeouts}; fire and forget: {FireAndForgets}; last heartbeat: {LastHeartbeatSecondsAgo}s ago")] + internal static partial void LogInformationTimeoutsSummary(this ILogger logger, long syncTimeouts, long asyncTimeouts, long fireAndForgets, long lastHeartbeatSecondsAgo); + + // EndPointCollection logging methods + [LoggerMessage( + Level = LogLevel.Information, + EventId = 106, + Message = "Using DNS to resolve '{DnsHost}'...")] + internal static partial void LogInformationUsingDnsToResolve(this ILogger logger, string dnsHost); + + [LoggerMessage( + Level = LogLevel.Information, + EventId = 107, + Message = "'{DnsHost}' => {IpAddress}")] + internal static partial void LogInformationDnsResolutionResult(this ILogger logger, string dnsHost, IPAddress ipAddress); + + [LoggerMessage( + Level = LogLevel.Error, + EventId = 108, + Message = "{ErrorMessage}")] + internal static partial void LogErrorDnsResolution(this ILogger logger, Exception exception, string errorMessage); + + [LoggerMessage( + Level = LogLevel.Information, + EventId = 109, + Message = "Service name not defined.")] + internal static partial void LogInformationServiceNameNotDefined(this ILogger logger); }