Skip to content

Commit b3aeb61

Browse files
authored
chore: Replaces string interpolation with structured logging - PhysicalConnection & PhysicalBridge (#2929)
* chore: Replaces string interpolation with structured logging - PhysicalConnection & PhysicalBridge Replaces string interpolation with structured logging Migrates logging calls from string interpolation to structured logging using LoggerMessage attributes. This improves performance by avoiding string allocation and enables better log analysis through structured data. Adds new logging extension methods for PhysicalBridge operations including connection establishment, failures, dead socket detection, and resurrection attempts. * Replaces manual logging with structured logger methods Introduces dedicated LoggerMessage attributes for PhysicalConnection operations to improve logging performance and consistency. Replaces string interpolation and manual log calls with strongly-typed logging methods that provide better compile-time safety and runtime performance through source generation. Covers connection lifecycle events including endpoint validation, connection attempts, TLS configuration, socket operations, and connection establishment.
1 parent 3c236a4 commit b3aeb61

File tree

3 files changed

+102
-16
lines changed

3 files changed

+102
-16
lines changed

src/StackExchange.Redis/LoggerExtensions.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,90 @@ internal static void LogWithThreadPoolStats(this ILogger? log, string message)
579579
EventId = 86,
580580
Message = "{Server}: Auto-configured (HELLO) role: {Role}")]
581581
internal static partial void LogInformationAutoConfiguredHelloRole(this ILogger logger, ServerEndPointLogValue server, string role);
582+
583+
// PhysicalBridge logging methods
584+
[LoggerMessage(
585+
Level = LogLevel.Information,
586+
EventId = 87,
587+
Message = "{EndPoint}: OnEstablishingAsync complete")]
588+
internal static partial void LogInformationOnEstablishingComplete(this ILogger logger, EndPointLogValue endPoint);
589+
590+
[LoggerMessage(
591+
Level = LogLevel.Information,
592+
EventId = 88,
593+
Message = "{ErrorMessage}")]
594+
internal static partial void LogInformationConnectionFailureRequested(this ILogger logger, Exception exception, string errorMessage);
595+
596+
[LoggerMessage(
597+
Level = LogLevel.Error,
598+
EventId = 89,
599+
Message = "{ErrorMessage}")]
600+
internal static partial void LogErrorConnectionIssue(this ILogger logger, Exception exception, string errorMessage);
601+
602+
[LoggerMessage(
603+
Level = LogLevel.Warning,
604+
EventId = 90,
605+
Message = "Dead socket detected, no reads in {LastReadSecondsAgo} seconds with {TimeoutCount} timeouts, issuing disconnect")]
606+
internal static partial void LogWarningDeadSocketDetected(this ILogger logger, long lastReadSecondsAgo, long timeoutCount);
607+
608+
[LoggerMessage(
609+
Level = LogLevel.Information,
610+
EventId = 91,
611+
Message = "Resurrecting {Bridge} (retry: {RetryCount})")]
612+
internal static partial void LogInformationResurrecting(this ILogger logger, PhysicalBridge bridge, long retryCount);
613+
614+
[LoggerMessage(
615+
Level = LogLevel.Information,
616+
EventId = 92,
617+
Message = "{BridgeName}: Connecting...")]
618+
internal static partial void LogInformationConnecting(this ILogger logger, string bridgeName);
619+
620+
[LoggerMessage(
621+
Level = LogLevel.Error,
622+
EventId = 93,
623+
Message = "{BridgeName}: Connect failed: {ErrorMessage}")]
624+
internal static partial void LogErrorConnectFailed(this ILogger logger, Exception exception, string bridgeName, string errorMessage);
625+
626+
// PhysicalConnection logging methods
627+
[LoggerMessage(
628+
Level = LogLevel.Error,
629+
EventId = 94,
630+
Message = "No endpoint")]
631+
internal static partial void LogErrorNoEndpoint(this ILogger logger, Exception exception);
632+
633+
[LoggerMessage(
634+
Level = LogLevel.Information,
635+
EventId = 95,
636+
Message = "{EndPoint}: BeginConnectAsync")]
637+
internal static partial void LogInformationBeginConnectAsync(this ILogger logger, EndPointLogValue endPoint);
638+
639+
[LoggerMessage(
640+
Level = LogLevel.Information,
641+
EventId = 96,
642+
Message = "{EndPoint}: Starting read")]
643+
internal static partial void LogInformationStartingRead(this ILogger logger, EndPointLogValue endPoint);
644+
645+
[LoggerMessage(
646+
Level = LogLevel.Error,
647+
EventId = 97,
648+
Message = "{EndPoint}: (socket shutdown)")]
649+
internal static partial void LogErrorSocketShutdown(this ILogger logger, Exception exception, EndPointLogValue endPoint);
650+
651+
[LoggerMessage(
652+
Level = LogLevel.Information,
653+
EventId = 98,
654+
Message = "Configuring TLS")]
655+
internal static partial void LogInformationConfiguringTLS(this ILogger logger);
656+
657+
[LoggerMessage(
658+
Level = LogLevel.Information,
659+
EventId = 99,
660+
Message = "TLS connection established successfully using protocol: {SslProtocol}")]
661+
internal static partial void LogInformationTLSConnectionEstablished(this ILogger logger, System.Security.Authentication.SslProtocols sslProtocol);
662+
663+
[LoggerMessage(
664+
Level = LogLevel.Information,
665+
EventId = 100,
666+
Message = "{BridgeName}: Connected")]
667+
internal static partial void LogInformationConnected(this ILogger logger, string bridgeName);
582668
}

src/StackExchange.Redis/PhysicalBridge.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ internal async Task OnConnectedAsync(PhysicalConnection connection, ILogger? log
431431
{
432432
ConnectedAt ??= DateTime.UtcNow;
433433
await ServerEndPoint.OnEstablishingAsync(connection, log).ForAwait();
434-
log?.LogInformation($"{Format.ToString(ServerEndPoint)}: OnEstablishingAsync complete");
434+
log?.LogInformationOnEstablishingComplete(new(ServerEndPoint.EndPoint));
435435
}
436436
else
437437
{
@@ -457,11 +457,11 @@ internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailur
457457
{
458458
if (wasRequested)
459459
{
460-
Multiplexer.Logger?.LogInformation(innerException, innerException.Message);
460+
Multiplexer.Logger?.LogInformationConnectionFailureRequested(innerException, innerException.Message);
461461
}
462462
else
463463
{
464-
Multiplexer.Logger?.LogError(innerException, innerException.Message);
464+
Multiplexer.Logger?.LogErrorConnectionIssue(innerException, innerException.Message);
465465
}
466466
Trace($"OnConnectionFailed: {connection}");
467467
// If we're configured to, fail all pending backlogged messages
@@ -589,7 +589,7 @@ internal void OnHeartbeat(bool ifConnectedOnly)
589589
Interlocked.Increment(ref connectTimeoutRetryCount);
590590
var ex = ExceptionFactory.UnableToConnect(Multiplexer, "ConnectTimeout", Name);
591591
LastException = ex;
592-
Multiplexer.Logger?.LogError(ex, ex.Message);
592+
Multiplexer.Logger?.LogErrorConnectionIssue(ex, ex.Message);
593593
Trace("Aborting connect");
594594
// abort and reconnect
595595
var snapshot = physical;
@@ -671,7 +671,7 @@ internal void OnHeartbeat(bool ifConnectedOnly)
671671
// This is meant to address the scenario we see often in Linux configs where TCP retries will happen for 15 minutes.
672672
// To us as a client, we'll see the socket as green/open/fine when writing but we'll bet getting nothing back.
673673
// Since we can't depend on the pipe to fail in that case, we want to error here based on the criteria above so we reconnect broken clients much faster.
674-
tmp.BridgeCouldBeNull?.Multiplexer.Logger?.LogWarning($"Dead socket detected, no reads in {tmp.LastReadSecondsAgo} seconds with {timedOutThisHeartbeat} timeouts, issuing disconnect");
674+
tmp.BridgeCouldBeNull?.Multiplexer.Logger?.LogWarningDeadSocketDetected(tmp.LastReadSecondsAgo, timedOutThisHeartbeat);
675675
OnDisconnected(ConnectionFailureType.SocketFailure, tmp, out _, out State oldState);
676676
tmp.Dispose(); // Cleanup the existing connection/socket if any, otherwise it will wait reading indefinitely
677677
}
@@ -691,7 +691,7 @@ internal void OnHeartbeat(bool ifConnectedOnly)
691691
// Increment count here, so that we don't re-enter in Connecting case up top - we don't want to re-enter and log there.
692692
Interlocked.Increment(ref connectTimeoutRetryCount);
693693

694-
Multiplexer.Logger?.LogInformation($"Resurrecting {ToString()} (retry: {connectTimeoutRetryCount})");
694+
Multiplexer.Logger?.LogInformationResurrecting(this, connectTimeoutRetryCount);
695695
Multiplexer.OnResurrecting(ServerEndPoint.EndPoint, ConnectionType);
696696
TryConnect(null);
697697
}
@@ -1453,7 +1453,7 @@ private bool ChangeState(State oldState, State newState)
14531453
{
14541454
if (!Multiplexer.IsDisposed)
14551455
{
1456-
log?.LogInformation($"{Name}: Connecting...");
1456+
log?.LogInformationConnecting(Name);
14571457
Multiplexer.Trace("Connecting...", Name);
14581458
if (ChangeState(State.Disconnected, State.Connecting))
14591459
{
@@ -1470,7 +1470,7 @@ private bool ChangeState(State oldState, State newState)
14701470
}
14711471
catch (Exception ex)
14721472
{
1473-
log?.LogError(ex, $"{Name}: Connect failed: {ex.Message}");
1473+
log?.LogErrorConnectFailed(ex, Name, ex.Message);
14741474
Multiplexer.Trace("Connect failed: " + ex.Message, Name);
14751475
ChangeState(State.Disconnected);
14761476
OnInternalError(ex);

src/StackExchange.Redis/PhysicalConnection.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ internal async Task BeginConnectAsync(ILogger? log)
109109
var endpoint = bridge?.ServerEndPoint?.EndPoint;
110110
if (bridge == null || endpoint == null)
111111
{
112-
log?.LogError(new ArgumentNullException(nameof(endpoint)), "No endpoint");
112+
log?.LogErrorNoEndpoint(new ArgumentNullException(nameof(endpoint)));
113113
return;
114114
}
115115

@@ -135,7 +135,7 @@ internal async Task BeginConnectAsync(ILogger? log)
135135
}
136136
}
137137
bridge.Multiplexer.OnConnecting(endpoint, bridge.ConnectionType);
138-
log?.LogInformation($"{Format.ToString(endpoint)}: BeginConnectAsync");
138+
log?.LogInformationBeginConnectAsync(new(endpoint));
139139

140140
CancellationTokenSource? timeoutSource = null;
141141
try
@@ -184,7 +184,7 @@ internal async Task BeginConnectAsync(ILogger? log)
184184
}
185185
else if (await ConnectedAsync(x, log, bridge.Multiplexer.SocketManager!).ForAwait())
186186
{
187-
log?.LogInformation($"{Format.ToString(endpoint)}: Starting read");
187+
log?.LogInformationStartingRead(new(endpoint));
188188
try
189189
{
190190
StartReading();
@@ -204,7 +204,7 @@ internal async Task BeginConnectAsync(ILogger? log)
204204
}
205205
catch (ObjectDisposedException ex)
206206
{
207-
log?.LogError(ex, $"{Format.ToString(endpoint)}: (socket shutdown)");
207+
log?.LogErrorSocketShutdown(ex, new(endpoint));
208208
try { RecordConnectionFailed(ConnectionFailureType.UnableToConnect, isInitialConnect: true); }
209209
catch (Exception inner)
210210
{
@@ -1563,7 +1563,7 @@ internal async ValueTask<bool> ConnectedAsync(Socket? socket, ILogger? log, Sock
15631563

15641564
if (config.Ssl)
15651565
{
1566-
log?.LogInformation("Configuring TLS");
1566+
log?.LogInformationConfiguringTLS();
15671567
var host = config.SslHost;
15681568
if (host.IsNullOrWhiteSpace())
15691569
{
@@ -1599,10 +1599,10 @@ internal async ValueTask<bool> ConnectedAsync(Socket? socket, ILogger? log, Sock
15991599
{
16001600
Debug.WriteLine(ex.Message);
16011601
bridge.Multiplexer.SetAuthSuspect(ex);
1602-
bridge.Multiplexer.Logger?.LogError(ex, ex.Message);
1602+
bridge.Multiplexer.Logger?.LogErrorConnectionIssue(ex, ex.Message);
16031603
throw;
16041604
}
1605-
log?.LogInformation($"TLS connection established successfully using protocol: {ssl.SslProtocol}");
1605+
log?.LogInformationTLSConnectionEstablished(ssl.SslProtocol);
16061606
}
16071607
catch (AuthenticationException authexception)
16081608
{
@@ -1625,7 +1625,7 @@ internal async ValueTask<bool> ConnectedAsync(Socket? socket, ILogger? log, Sock
16251625

16261626
_ioPipe = pipe;
16271627

1628-
log?.LogInformation($"{bridge.Name}: Connected ");
1628+
log?.LogInformationConnected(bridge.Name);
16291629

16301630
await bridge.OnConnectedAsync(this, log).ForAwait();
16311631
return true;

0 commit comments

Comments
 (0)