Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ILogger for ConnectionMultiplexer #2051

Merged
merged 44 commits into from
Aug 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6d1b16f
Options cleanup and defaulting
NickCraver Mar 19, 2022
6608c95
Move to uncloned ConfigurationOptions
NickCraver Mar 20, 2022
0cf1e75
Simplify Validate
NickCraver Mar 20, 2022
c70d83a
Update comments per-memoized config options
NickCraver Mar 20, 2022
ab4ed36
Reflect HighPrioritySocketThreads as retired
NickCraver Mar 20, 2022
581d88f
Obsolete message updates
NickCraver Mar 20, 2022
c7070be
Add mutability tests
NickCraver Mar 20, 2022
073bd08
Comment tweaks
NickCraver Mar 20, 2022
a8c934c
Add release notes
NickCraver Mar 20, 2022
84f2b96
Promote obsoletes for 3.0 deprecation
NickCraver Mar 20, 2022
f34ac25
WIP: ILogger bits!
NickCraver Mar 22, 2022
7629f71
Merge branch 'main' into craver/ilogger
NickCraver Mar 29, 2022
d1f905f
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Oct 24, 2022
0b5c814
Merge in NRTs
NickCraver Oct 24, 2022
75f298b
Add initial test bits
NickCraver Oct 24, 2022
56cd4c4
Add co Clone/DefaultOptions and some test checks on that
NickCraver Oct 24, 2022
e32f4a2
When *we* close the socket, it's an info message not an error
NickCraver Oct 24, 2022
1c24b77
Merge branch 'main' into craver/ilogger
NickCraver Oct 24, 2022
befb0fe
Make some test more resilient
NickCraver Oct 26, 2022
5f7e0f3
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Oct 26, 2022
c91422e
Merge branch 'main' into craver/ilogger
NickCraver May 9, 2023
4e2a6ad
Fix new log line
NickCraver May 9, 2023
7142d6c
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Jun 20, 2023
a76ceca
Move to ILoggerFactory to support more use cases.
NickCraver Jun 20, 2023
ee7450f
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Jul 1, 2023
c5fb56a
Revert "Move to ILoggerFactory to support more use cases."
NickCraver Jul 4, 2023
30fee1b
Memoize on ConnectionMultiplexer
NickCraver Jul 4, 2023
9614fbe
Reduce LogProxy dependence
NickCraver Jul 4, 2023
87051de
Refactor LogProxy => ILogger wrapper
NickCraver Jul 4, 2023
cab1f78
Fix GetStatus
NickCraver Jul 4, 2023
6d4a6c6
Add wrapping example
NickCraver Jul 4, 2023
d23b0cb
Tests: up tolerance on AbortOnConnectFailTests
NickCraver Jul 4, 2023
03820fb
Add to docs
NickCraver Jul 9, 2023
4a314c2
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Jul 10, 2023
aef0d49
Eliminate double timestamps + simplify
NickCraver Jul 10, 2023
b53f2aa
Fix disposal on TextWriter handles
NickCraver Jul 25, 2023
168c4f6
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Jul 28, 2023
ceeb3b0
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Jul 28, 2023
a4ded47
Fix logging, add more for Role test that's failing
NickCraver Aug 4, 2023
d0c9295
ILoggerFactory on the config side
NickCraver Aug 8, 2023
25c658f
AppVeyor: add .trx to artifacts
NickCraver Aug 12, 2023
99bfc5e
Upgrade actions/setup-dotnet
NickCraver Aug 12, 2023
94b7030
Doc updates!
NickCraver Aug 19, 2023
c1b54cf
Merge remote-tracking branch 'origin/main' into craver/ilogger
NickCraver Aug 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v1
- name: Install .NET SDK
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
6.0.x
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v1
# - name: Install .NET SDK
# uses: actions/setup-dotnet@v1
# uses: actions/setup-dotnet@v3
# with:
# dotnet-version: |
# 6.0.x
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.13.1" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.0.0-alpha" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4-beta1.22362.3" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ build_script:
test: off
artifacts:
- path: .\.nupkgs\*.nupkg
- path: '**\*.trx'

deploy:
- provider: NuGet
Expand Down
2 changes: 2 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ The `ConfigurationOptions` object has a wide range of properties, all of which a
| setlib={bool} | `SetClientLibrary` | `true` | Whether to attempt to use `CLIENT SETINFO` to set the library name/version on the connection |

Additional code-only options:
- LoggerFactory (`ILoggerFactory`) - Default: `null`
- The logger to use for connection events (not per command), e.g. connection log, disconnects, reconnects, server errors.
- ReconnectRetryPolicy (`IReconnectRetryPolicy`) - Default: `ReconnectRetryPolicy = ExponentialRetry(ConnectTimeout / 2);`
- Determines how often a multiplexer will try to reconnect after a failure
- BacklogPolicy - Default: `BacklogPolicy = BacklogPolicy.Default;`
Expand Down
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Current package versions:

- Fix [#2507](https://github.com/StackExchange/StackExchange.Redis/issues/2507): Pub/sub with multi-item payloads should be usable ([#2508 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2508))
- Add: connection-id tracking (internal only, no public API) ([#2508 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2508))
- Add: `ConfigurationOptions.LoggerFactory` for logging to an `ILoggerFactory` (e.g. `ILogger`) all connection and error events ([#2051 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2051))
- Fix [#2467](https://github.com/StackExchange/StackExchange.Redis/issues/2467): Add StreamGroupInfo EntriesRead and Lag ([#2510 by tvdias](https://github.com/StackExchange/StackExchange.Redis/pull/2510))

## 2.6.122
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace StackExchange.Redis.Configuration
{
Expand Down Expand Up @@ -156,6 +157,12 @@ public static DefaultOptionsProvider GetProvider(EndPoint endpoint)
/// </summary>
public virtual TimeSpan KeepAliveInterval => TimeSpan.FromSeconds(60);

/// <summary>
/// The <see cref="ILoggerFactory"/> to get loggers for connection events.
/// Note: changes here only affect <see cref="ConnectionMultiplexer"/>s created after.
/// </summary>
public virtual ILoggerFactory? LoggerFactory => null;

/// <summary>
/// Type of proxy to use (if any); for example <see cref="Proxy.Twemproxy"/>.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/StackExchange.Redis/ConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StackExchange.Redis.Configuration;

namespace StackExchange.Redis
Expand Down Expand Up @@ -161,6 +162,8 @@ public static string TryNormalize(string value)

private BacklogPolicy? backlogPolicy;

private ILoggerFactory? loggerFactory;

/// <summary>
/// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication; note
/// that this cannot be specified in the configuration-string.
Expand Down Expand Up @@ -448,6 +451,16 @@ public int KeepAlive
set => keepAlive = value;
}

/// <summary>
/// The <see cref="ILoggerFactory"/> to get loggers for connection events.
/// Note: changes here only affect <see cref="ConnectionMultiplexer"/>s created after.
/// </summary>
public ILoggerFactory? LoggerFactory
{
get => loggerFactory ?? Defaults.LoggerFactory;
set => loggerFactory = value;
}

/// <summary>
/// The username to use to authenticate with the server.
/// </summary>
Expand Down Expand Up @@ -675,6 +688,7 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow
checkCertificateRevocation = checkCertificateRevocation,
BeforeSocketConnect = BeforeSocketConnect,
EndPoints = EndPoints.Clone(),
LoggerFactory = LoggerFactory,
#if NETCOREAPP3_1_OR_GREATER
SslClientAuthenticationOptions = SslClientAuthenticationOptions,
#endif
Expand Down
81 changes: 39 additions & 42 deletions src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Pipelines.Sockets.Unofficial;
using Microsoft.Extensions.Logging;
using Pipelines.Sockets.Unofficial;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -19,8 +20,8 @@ public partial class ConnectionMultiplexer
/// <summary>
/// Initializes the connection as a Sentinel connection and adds the necessary event handlers to track changes to the managed primaries.
/// </summary>
/// <param name="logProxy">The writer to log to, if any.</param>
internal void InitializeSentinel(LogProxy? logProxy)
/// <param name="log">The <see cref="ILogger"/> to log to, if any.</param>
internal void InitializeSentinel(ILogger? log)
{
if (ServerSelectionStrategy.ServerType != ServerType.Sentinel)
{
Expand Down Expand Up @@ -65,7 +66,7 @@ internal void InitializeSentinel(LogProxy? logProxy)
// we need to reconfigure to make sure we still have a subscription to the +switch-master channel
ConnectionFailed += (sender, e) =>
// Reconfigure to get subscriptions back online
ReconfigureAsync(first: false, reconfigureAll: true, logProxy, e.EndPoint, "Lost sentinel connection", false).Wait();
ReconfigureAsync(first: false, reconfigureAll: true, log, e.EndPoint, "Lost sentinel connection", false).Wait();

// Subscribe to new sentinels being added
if (sub.SubscribedEndpoint(RedisChannel.Literal("+sentinel")) == null)
Expand Down Expand Up @@ -142,12 +143,12 @@ private static ConnectionMultiplexer SentinelPrimaryConnect(ConfigurationOptions
/// for the specified <see cref="ConfigurationOptions.ServiceName"/> in the config and returns a managed connection to the current primary server.
/// </summary>
/// <param name="configuration">The configuration options to use for this multiplexer.</param>
/// <param name="log">The <see cref="TextWriter"/> to log to.</param>
private static async Task<ConnectionMultiplexer> SentinelPrimaryConnectAsync(ConfigurationOptions configuration, TextWriter? log = null)
/// <param name="writer">The <see cref="TextWriter"/> to log to.</param>
private static async Task<ConnectionMultiplexer> SentinelPrimaryConnectAsync(ConfigurationOptions configuration, TextWriter? writer = null)
{
var sentinelConnection = await SentinelConnectAsync(configuration, log).ForAwait();
var sentinelConnection = await SentinelConnectAsync(configuration, writer).ForAwait();

var muxer = sentinelConnection.GetSentinelMasterConnection(configuration, log);
var muxer = sentinelConnection.GetSentinelMasterConnection(configuration, writer);
// Set reference to sentinel connection so that we can dispose it
muxer.sentinelConnection = sentinelConnection;

Expand Down Expand Up @@ -375,49 +376,45 @@ internal void OnManagedConnectionFailed(object? sender, ConnectionFailedEventArg
/// </summary>
/// <param name="switchBlame">The endpoint responsible for the switch.</param>
/// <param name="connection">The connection that should be switched over to a new primary endpoint.</param>
/// <param name="log">The writer to log to, if any.</param>
internal void SwitchPrimary(EndPoint? switchBlame, ConnectionMultiplexer connection, TextWriter? log = null)
/// <param name="writer">The writer to log to, if any.</param>
internal void SwitchPrimary(EndPoint? switchBlame, ConnectionMultiplexer connection, TextWriter? writer = null)
{
if (log == null) log = TextWriter.Null;

using (var logProxy = LogProxy.TryCreate(log))
var logger = Logger.With(writer);
if (connection.RawConfig.ServiceName is not string serviceName)
{
if (connection.RawConfig.ServiceName is not string serviceName)
{
logProxy?.WriteLine("Service name not defined.");
return;
}
logger?.LogInformation("Service name not defined.");
return;
}

// Get new primary - try twice
EndPoint newPrimaryEndPoint = GetConfiguredPrimaryForService(serviceName)
?? GetConfiguredPrimaryForService(serviceName)
?? throw new RedisConnectionException(ConnectionFailureType.UnableToConnect,
$"Sentinel: Failed connecting to switch primary for service: {serviceName}");
// Get new primary - try twice
EndPoint newPrimaryEndPoint = GetConfiguredPrimaryForService(serviceName)
?? GetConfiguredPrimaryForService(serviceName)
?? throw new RedisConnectionException(ConnectionFailureType.UnableToConnect,
$"Sentinel: Failed connecting to switch primary for service: {serviceName}");

connection.currentSentinelPrimaryEndPoint = newPrimaryEndPoint;
connection.currentSentinelPrimaryEndPoint = newPrimaryEndPoint;

if (!connection.servers.Contains(newPrimaryEndPoint))
{
EndPoint[]? replicaEndPoints = GetReplicasForService(serviceName)
?? GetReplicasForService(serviceName);
if (!connection.servers.Contains(newPrimaryEndPoint))
{
EndPoint[]? replicaEndPoints = GetReplicasForService(serviceName)
?? GetReplicasForService(serviceName);

connection.servers.Clear();
connection.EndPoints.Clear();
connection.EndPoints.TryAdd(newPrimaryEndPoint);
if (replicaEndPoints is not null)
connection.servers.Clear();
connection.EndPoints.Clear();
connection.EndPoints.TryAdd(newPrimaryEndPoint);
if (replicaEndPoints is not null)
{
foreach (var replicaEndPoint in replicaEndPoints)
{
foreach (var replicaEndPoint in replicaEndPoints)
{
connection.EndPoints.TryAdd(replicaEndPoint);
}
connection.EndPoints.TryAdd(replicaEndPoint);
}
Trace($"Switching primary to {newPrimaryEndPoint}");
// Trigger a reconfigure
connection.ReconfigureAsync(first: false, reconfigureAll: false, logProxy, switchBlame,
$"Primary switch {serviceName}", false, CommandFlags.PreferMaster).Wait();

UpdateSentinelAddressList(serviceName);
}
Trace($"Switching primary to {newPrimaryEndPoint}");
// Trigger a reconfigure
connection.ReconfigureAsync(first: false, reconfigureAll: false, logger, switchBlame,
$"Primary switch {serviceName}", false, CommandFlags.PreferMaster).Wait();

UpdateSentinelAddressList(serviceName);
}
}

Expand Down
Loading