diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/net462/PublicAPI.Unshipped.txt index 09483f805d..c76bae94e0 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,16 +1,22 @@ -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action? -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.get -> System.TimeSpan -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.StackExchangeRedisCallsInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.EnrichActivityWithTimingEvents.get -> bool -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.EnrichActivityWithTimingEvents.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.AddConnection(StackExchange.Redis.IConnectionMultiplexer! connection) -> System.IDisposable! +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.AddConnection(string! name, StackExchange.Redis.IConnectionMultiplexer! connection) -> System.IDisposable! +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.Dispose() -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.Enrich.get -> System.Action? +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.Enrich.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.EnrichActivityWithTimingEvents.get -> bool +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.EnrichActivityWithTimingEvents.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.FlushInterval.get -> System.TimeSpan +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.FlushInterval.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.SetVerboseDatabaseStatements.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.StackExchangeRedisInstrumentationOptions() -> void OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder) -> OpenTelemetry.Trace.TracerProviderBuilder! static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action? configure) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action? configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 09483f805d..c76bae94e0 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,16 +1,22 @@ -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action? -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.get -> System.TimeSpan -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.set -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.StackExchangeRedisCallsInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.EnrichActivityWithTimingEvents.get -> bool -OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.EnrichActivityWithTimingEvents.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.AddConnection(StackExchange.Redis.IConnectionMultiplexer! connection) -> System.IDisposable! +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.AddConnection(string! name, StackExchange.Redis.IConnectionMultiplexer! connection) -> System.IDisposable! +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation.Dispose() -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.Enrich.get -> System.Action? +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.Enrich.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.EnrichActivityWithTimingEvents.get -> bool +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.EnrichActivityWithTimingEvents.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.FlushInterval.get -> System.TimeSpan +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.FlushInterval.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.SetVerboseDatabaseStatements.set -> void +OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions.StackExchangeRedisInstrumentationOptions() -> void OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder) -> OpenTelemetry.Trace.TracerProviderBuilder! static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action? configure) -> OpenTelemetry.Trace.TracerProviderBuilder! -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action? configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/CHANGELOG.md index 4ee8f6ee7b..9aa2cc6049 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/CHANGELOG.md @@ -7,6 +7,17 @@ options. ([#1183](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1183)) +* **\*\*BREAKING CHANGE\*\*** Renamed the + `StackExchangeRedisCallsInstrumentationOptions` class to + `StackExchangeRedisInstrumentationOptions`. + ([#1193](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1193)) + +* Added a new extension `TracerProviderBuilder.ConfigureRedisInstrumentation` + which can be used to obtain the `StackExchangeRedisInstrumentation` instance + in order to dynamically add connections for instrumentation after the + `TracerProvider` has been created. + ([#1193](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1193)) + ## 1.0.0-rc9.8 Released 2023-Feb-27 diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryToActivityConverter.cs b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryToActivityConverter.cs index c4bca0e8ef..00dc74295e 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryToActivityConverter.cs +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryToActivityConverter.cs @@ -68,19 +68,19 @@ internal static class RedisProfilerEntryToActivityConverter }); }); - public static Activity? ProfilerCommandToActivity(Activity? parentActivity, IProfiledCommand command, StackExchangeRedisCallsInstrumentationOptions options) + public static Activity? ProfilerCommandToActivity(Activity? parentActivity, IProfiledCommand command, StackExchangeRedisInstrumentationOptions options) { var name = command.Command; // Example: SET; if (string.IsNullOrEmpty(name)) { - name = StackExchangeRedisCallsInstrumentation.ActivityName; + name = StackExchangeRedisConnectionInstrumentation.ActivityName; } - var activity = StackExchangeRedisCallsInstrumentation.ActivitySource.StartActivity( + var activity = StackExchangeRedisConnectionInstrumentation.ActivitySource.StartActivity( name, ActivityKind.Client, parentActivity?.Context ?? default, - StackExchangeRedisCallsInstrumentation.CreationTags, + StackExchangeRedisConnectionInstrumentation.CreationTags, startTime: command.CommandCreated); if (activity == null) @@ -105,7 +105,7 @@ internal static class RedisProfilerEntryToActivityConverter // Total: // command.ElapsedTime; // 00:00:32.4988020 - activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString()); + activity.SetTag(StackExchangeRedisConnectionInstrumentation.RedisFlagsKeyName, command.Flags.ToString()); if (options.SetVerboseDatabaseStatements) { @@ -149,7 +149,7 @@ internal static class RedisProfilerEntryToActivityConverter } } - activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName, command.Db); + activity.SetTag(StackExchangeRedisConnectionInstrumentation.RedisDatabaseIndexKeyName, command.Db); // TODO: deal with the re-transmission // command.RetransmissionOf; @@ -174,7 +174,7 @@ internal static class RedisProfilerEntryToActivityConverter return activity; } - public static void DrainSession(Activity? parentActivity, IEnumerable sessionCommands, StackExchangeRedisCallsInstrumentationOptions options) + public static void DrainSession(Activity? parentActivity, IEnumerable sessionCommands, StackExchangeRedisInstrumentationOptions options) { foreach (var command in sessionCommands) { diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md index 0bf36733e7..d5ccbcc3de 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md @@ -71,6 +71,68 @@ the `ConfigureServices` of your `Startup` class. Refer to documentation for For an ASP.NET application, adding instrumentation is typically done in the `Global.asax.cs`. Refer to documentation for [OpenTelemetry.Instrumentation.AspNet](../OpenTelemetry.Instrumentation.AspNet/README.md). +## Specify the Redis connection + +The following sections cover different ways to specify the `StackExchange.Redis` +connection(s) that will be instrumented and captured by OpenTelemetry. + +### Pass a Redis connection when calling AddRedisInstrumentation + +The simplest thing to do is pass a created connection to the +`AddRedisInstrumentation` extension method: + +```csharp +using var connection = ConnectionMultiplexer.Connect("localhost:6379"); + +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddRedisInstrumentation(connection) + .Build(); +``` + +Whatever connection is specified will be collected by OpenTelemetry. + +### Use the application IServiceProvider + +Users using the `OpenTelemetry.Extensions.Hosting` package may prefer to manage +the Redis connection via the application `IServiceCollection`. To support this +scenario, if a connection is not passed to the `AddRedisInstrumentation` +extension manually one will be resolved one using the `IServiceProvider`: + +```csharp +appBuilder.Services.AddSingleton( + sp => MyRedisConnectionHelper.CreateConnection(sp)); + +appBuilder.Services + .AddOpenTelemetry() + .WithTracing(tracing => tracing.AddRedisInstrumentation()); +``` + +Whatever connection is found in the `IServiceProvider` will be collected by +OpenTelemetry. + +### Interact with StackExchangeRedisInstrumentation directly + +For full control of the Redis connection(s) being instrumented the +`ConfigureRedisInstrumentation` extension is provided to expose the +`StackExchangeRedisInstrumentation` class directly: + +```csharp +StackExchangeRedisInstrumentation redisInstrumentation = null; + +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddRedisInstrumentation() + .ConfigureRedisInstrumentation(instrumentation => redisInstrumentation = instrumentation) + .Build(); + +using var connection1 = ConnectionMultiplexer.Connect("localhost:6379"); +redisInstrumentation.AddConnection(connection1); + +using var connection2 = ConnectionMultiplexer.Connect("localhost:6380"); +redisInstrumentation.AddConnection(connection2); +``` + +Connections may be added or removed at any time. + ## Advanced configuration This instrumentation can be configured to change the default behavior by using diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentation.cs b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisConnectionInstrumentation.cs similarity index 86% rename from src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentation.cs rename to src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisConnectionInstrumentation.cs index 63872364a0..4d09b762d1 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentation.cs +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisConnectionInstrumentation.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,15 +25,15 @@ namespace OpenTelemetry.Instrumentation.StackExchangeRedis; /// -/// Redis calls instrumentation. +/// StackExchange.Redis instrumentation. /// -internal class StackExchangeRedisCallsInstrumentation : IDisposable +internal sealed class StackExchangeRedisConnectionInstrumentation : IDisposable { internal const string RedisDatabaseIndexKeyName = "db.redis.database_index"; internal const string RedisFlagsKeyName = "db.redis.flags"; - internal static readonly string ActivitySourceName = typeof(StackExchangeRedisCallsInstrumentation).Assembly.GetName().Name; + internal static readonly string ActivitySourceName = typeof(StackExchangeRedisConnectionInstrumentation).Assembly.GetName().Name; internal static readonly string ActivityName = ActivitySourceName + ".Execute"; - internal static readonly Version Version = typeof(StackExchangeRedisCallsInstrumentation).Assembly.GetName().Version; + internal static readonly Version Version = typeof(StackExchangeRedisConnectionInstrumentation).Assembly.GetName().Version; internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Version.ToString()); internal static readonly IEnumerable> CreationTags = new[] { @@ -43,22 +43,22 @@ internal class StackExchangeRedisCallsInstrumentation : IDisposable internal readonly ConcurrentDictionary<(ActivityTraceId TraceId, ActivitySpanId SpanId), (Activity Activity, ProfilingSession Session)> Cache = new(); - private readonly StackExchangeRedisCallsInstrumentationOptions options; + private readonly StackExchangeRedisInstrumentationOptions options; private readonly EventWaitHandle stopHandle = new(false, EventResetMode.ManualReset); private readonly Thread drainThread; private readonly ProfilingSession defaultSession = new(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// to instrument. /// Configuration options for redis instrumentation. - public StackExchangeRedisCallsInstrumentation(IConnectionMultiplexer connection, StackExchangeRedisCallsInstrumentationOptions options) + public StackExchangeRedisConnectionInstrumentation(IConnectionMultiplexer connection, StackExchangeRedisInstrumentationOptions options) { Guard.ThrowIfNull(connection); - this.options = options ?? new StackExchangeRedisCallsInstrumentationOptions(); + this.options = options ?? new StackExchangeRedisInstrumentationOptions(); this.drainThread = new Thread(this.DrainEntries) { diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentation.cs b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentation.cs new file mode 100644 index 0000000000..cee5940681 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentation.cs @@ -0,0 +1,107 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using Microsoft.Extensions.Options; +using OpenTelemetry.Internal; +using StackExchange.Redis; + +namespace OpenTelemetry.Instrumentation.StackExchangeRedis; + +/// +/// StackExchange.Redis instrumentation. +/// +public sealed class StackExchangeRedisInstrumentation : IDisposable +{ + private readonly IOptionsMonitor options; + + internal StackExchangeRedisInstrumentation( + IOptionsMonitor options) + { + this.options = options; + } + + internal List InstrumentedConnections { get; } = new(); + + /// + /// Adds an to the instrumentation. + /// + /// . + /// to cancel the registration. + public IDisposable AddConnection(IConnectionMultiplexer connection) + => this.AddConnection(Options.DefaultName, connection); + + /// + /// Adds an to the instrumentation. + /// + /// Name to use when retrieving options. + /// . + /// to cancel the registration. + public IDisposable AddConnection(string name, IConnectionMultiplexer connection) + { + Guard.ThrowIfNull(name); + Guard.ThrowIfNull(connection); + + var options = this.options.Get(name); + + lock (this.InstrumentedConnections) + { + var instrumentation = new StackExchangeRedisConnectionInstrumentation(connection, options); + + this.InstrumentedConnections.Add(instrumentation); + + return new StackExchangeRedisConnectionInstrumentationRegistration(() => + { + lock (this.InstrumentedConnections) + { + if (this.InstrumentedConnections.Remove(instrumentation)) + { + instrumentation.Dispose(); + } + } + }); + } + } + + /// + public void Dispose() + { + lock (this.InstrumentedConnections) + { + foreach (var instrumentation in this.InstrumentedConnections) + { + instrumentation.Dispose(); + } + + this.InstrumentedConnections.Clear(); + } + } + + private sealed class StackExchangeRedisConnectionInstrumentationRegistration : IDisposable + { + private readonly Action disposalAction; + + public StackExchangeRedisConnectionInstrumentationRegistration( + Action disposalAction) + { + this.disposalAction = disposalAction; + } + + public void Dispose() + { + this.disposalAction(); + } + } +} diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentationOptions.cs similarity index 78% rename from src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentationOptions.cs rename to src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentationOptions.cs index 0e27e838d5..71a317e756 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisCallsInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/StackExchangeRedisInstrumentationOptions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ namespace OpenTelemetry.Instrumentation.StackExchangeRedis; /// /// Options for StackExchange.Redis instrumentation. /// -public class StackExchangeRedisCallsInstrumentationOptions +public class StackExchangeRedisInstrumentationOptions { /// /// Gets or sets the maximum time that should elapse between flushing the internal buffer of Redis profiling sessions and creating objects. Default value: 00:00:10. @@ -31,7 +31,7 @@ public class StackExchangeRedisCallsInstrumentationOptions public TimeSpan FlushInterval { get; set; } = TimeSpan.FromSeconds(10); /// - /// Gets or sets a value indicating whether or not the should use reflection to get more detailed tag values. Default value: False. + /// Gets or sets a value indicating whether or not the should use reflection to get more detailed tag values. Default value: False. /// public bool SetVerboseDatabaseStatements { get; set; } @@ -45,7 +45,7 @@ public class StackExchangeRedisCallsInstrumentationOptions public Action? Enrich { get; set; } /// - /// Gets or sets a value indicating whether or not the should enrich Activity with entries about the Redis command processing/lifetime. Defaults to true. + /// Gets or sets a value indicating whether or not the should enrich Activity with entries about the Redis command processing/lifetime. Defaults to true. /// public bool EnrichActivityWithTimingEvents { get; set; } = true; } diff --git a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/TracerProviderBuilderExtensions.cs index 3bd9947930..842b3053c7 100644 --- a/src/OpenTelemetry.Instrumentation.StackExchangeRedis/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.StackExchangeRedis/TracerProviderBuilderExtensions.cs @@ -15,6 +15,7 @@ // using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using OpenTelemetry.Instrumentation.StackExchangeRedis; using OpenTelemetry.Internal; @@ -67,7 +68,7 @@ public static TracerProviderBuilder AddRedisInstrumentation( /// The instance of to chain the calls. public static TracerProviderBuilder AddRedisInstrumentation( this TracerProviderBuilder builder, - Action configure) + Action configure) { Guard.ThrowIfNull(configure); @@ -84,7 +85,7 @@ public static TracerProviderBuilder AddRedisInstrumentation( public static TracerProviderBuilder AddRedisInstrumentation( this TracerProviderBuilder builder, IConnectionMultiplexer connection, - Action configure) + Action configure) { Guard.ThrowIfNull(connection); Guard.ThrowIfNull(configure); @@ -109,33 +110,89 @@ public static TracerProviderBuilder AddRedisInstrumentation( this TracerProviderBuilder builder, string? name, IConnectionMultiplexer? connection, - Action? configure) + Action? configure) { Guard.ThrowIfNull(builder); name ??= Options.DefaultName; + builder.AddRedisInstrumentationSharedServices(); + if (configure != null) { - builder.ConfigureServices(services => services.Configure(name, configure)); + builder.ConfigureServices(services => + { + services.Configure(name, configure); + }); } return builder - .AddSource(StackExchangeRedisCallsInstrumentation.ActivitySourceName) + .AddSource(StackExchangeRedisConnectionInstrumentation.ActivitySourceName) .AddInstrumentation(sp => { - if (connection == null) + var instrumentation = sp.GetRequiredService(); + + connection ??= sp.GetService(); + + if (connection != null) { - connection = sp.GetService(); - if (connection == null) - { - throw new InvalidOperationException($"StackExchange.Redis {nameof(IConnectionMultiplexer)} could not be resolved through application {nameof(IServiceProvider)}"); - } + instrumentation.AddConnection(name, connection); } - return new StackExchangeRedisCallsInstrumentation( - connection, - sp.GetRequiredService>().Get(name)); + return instrumentation; }); } + + /// + /// Registers a callback for configuring Redis instrumentation. + /// + /// being configured. + /// Callback to configure instrumentation. + /// The instance of to chain the calls. + public static TracerProviderBuilder ConfigureRedisInstrumentation( + this TracerProviderBuilder builder, + Action configure) + { + Guard.ThrowIfNull(configure); + + return ConfigureRedisInstrumentation(builder, (sp, instrumentation) => configure(instrumentation)); + } + + /// + /// Registers a callback for configuring Redis instrumentation. + /// + /// being configured. + /// Callback to configure instrumentation. + /// The instance of to chain the calls. + public static TracerProviderBuilder ConfigureRedisInstrumentation( + this TracerProviderBuilder builder, + Action configure) + { + Guard.ThrowIfNull(configure); + + if (builder is not IDeferredTracerProviderBuilder deferredTracerProviderBuilder) + { + throw new NotSupportedException("ConfigureRedisInstrumentation is not supported on the supplied builder type."); + } + + builder.AddRedisInstrumentationSharedServices(); + + deferredTracerProviderBuilder.Configure( + (sp, builder) => configure(sp, sp.GetRequiredService())); + + return builder; + } + + private static TracerProviderBuilder AddRedisInstrumentationSharedServices( + this TracerProviderBuilder builder) + { + Guard.ThrowIfNull(builder); + + return builder.ConfigureServices(services => + { + services.TryAddSingleton( + sp => new StackExchangeRedisInstrumentation( + sp.GetRequiredService>())); + }); + } } diff --git a/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Implementation/RedisProfilerEntryToActivityConverterTests.cs b/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Implementation/RedisProfilerEntryToActivityConverterTests.cs index 784f9a528b..f92bf20aaa 100644 --- a/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Implementation/RedisProfilerEntryToActivityConverterTests.cs +++ b/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Implementation/RedisProfilerEntryToActivityConverterTests.cs @@ -63,7 +63,7 @@ public void ProfilerCommandToActivity_UsesCommandAsName() profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); profiledCommand.Setup(m => m.Command).Returns("SET"); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.Equal("SET", result.DisplayName); } @@ -76,7 +76,7 @@ public void ProfilerCommandToActivity_UsesTimestampAsStartTime() var profiledCommand = new Mock(); profiledCommand.Setup(m => m.CommandCreated).Returns(now.DateTime); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.Equal(now, result.StartTimeUtc); } @@ -88,7 +88,7 @@ public void ProfilerCommandToActivity_SetsDbTypeAttributeAsRedis() var profiledCommand = new Mock(); profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeDbSystem)); Assert.Equal("redis", result.GetTagValue(SemanticConventions.AttributeDbSystem)); @@ -102,7 +102,7 @@ public void ProfilerCommandToActivity_UsesCommandAsDbStatementAttribute() profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); profiledCommand.Setup(m => m.Command).Returns("SET"); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeDbStatement)); Assert.Equal("SET", result.GetTagValue(SemanticConventions.AttributeDbStatement)); @@ -118,10 +118,10 @@ public void ProfilerCommandToActivity_UsesFlagsForFlagsAttribute() CommandFlags.NoRedirect; profiledCommand.Setup(m => m.Flags).Returns(expectedFlags); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); - Assert.NotNull(result.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName)); - Assert.Equal("PreferMaster, FireAndForget, NoRedirect", result.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName)); + Assert.NotNull(result.GetTagValue(StackExchangeRedisConnectionInstrumentation.RedisFlagsKeyName)); + Assert.Equal("PreferMaster, FireAndForget, NoRedirect", result.GetTagValue(StackExchangeRedisConnectionInstrumentation.RedisFlagsKeyName)); } [Fact] @@ -136,7 +136,7 @@ public void ProfilerCommandToActivity_UsesIpEndPointAsEndPoint() profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); profiledCommand.Setup(m => m.EndPoint).Returns(ipLocalEndPoint); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerIp)); Assert.Equal($"{address}.0.0.0", result.GetTagValue(SemanticConventions.AttributeNetPeerIp)); @@ -154,7 +154,7 @@ public void ProfilerCommandToActivity_UsesDnsEndPointAsEndPoint() profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); profiledCommand.Setup(m => m.EndPoint).Returns(dnsEndPoint); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerName)); Assert.Equal(dnsEndPoint.Host, result.GetTagValue(SemanticConventions.AttributeNetPeerName)); @@ -172,7 +172,7 @@ public void ProfilerCommandToActivity_UsesOtherEndPointAsEndPoint() profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow); profiledCommand.Setup(m => m.EndPoint).Returns(unixEndPoint); - var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions()); + var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisInstrumentationOptions()); Assert.NotNull(result.GetTagValue(SemanticConventions.AttributePeerService)); Assert.Equal(unixEndPoint.ToString(), result.GetTagValue(SemanticConventions.AttributePeerService)); diff --git a/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/StackExchangeRedisCallsInstrumentationTests.cs b/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/StackExchangeRedisCallsInstrumentationTests.cs index 55cdb58011..d0be5f00d2 100644 --- a/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/StackExchangeRedisCallsInstrumentationTests.cs +++ b/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/StackExchangeRedisCallsInstrumentationTests.cs @@ -19,6 +19,7 @@ using System.Net; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Moq; using OpenTelemetry.Tests; using OpenTelemetry.Trace; @@ -107,16 +108,24 @@ public void SuccessfulCommandTest(string value) }; connectionOptions.EndPoints.Add(RedisEndPoint); - using var connection = ConnectionMultiplexer.Connect(connectionOptions); - + IConnectionMultiplexer connection = null; var activityProcessor = new Mock>(); var sampler = new TestSampler(); using (Sdk.CreateTracerProviderBuilder() - .AddProcessor(activityProcessor.Object) - .SetSampler(sampler) - .AddRedisInstrumentation(connection, c => c.SetVerboseDatabaseStatements = false) - .Build()) + .ConfigureServices(services => + { + services.TryAddSingleton(sp => + { + return connection = ConnectionMultiplexer.Connect(connectionOptions); + }); + }) + .AddProcessor(activityProcessor.Object) + .SetSampler(sampler) + .AddRedisInstrumentation(c => c.SetVerboseDatabaseStatements = false) + .Build()) { + Assert.NotNull(connection); + var db = connection.GetDatabase(); bool set = db.StringSet("key1", value, TimeSpan.FromSeconds(60)); @@ -149,7 +158,7 @@ public async void ProfilerSessionUsesTheSameDefault() var connection = ConnectionMultiplexer.Connect(connectionOptions); - using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions()); + using var instrumentation = new StackExchangeRedisConnectionInstrumentation(connection, new StackExchangeRedisInstrumentationOptions()); var profilerFactory = instrumentation.GetProfilerSessionsFactory(); var first = profilerFactory(); var second = profilerFactory(); @@ -164,28 +173,36 @@ public async void ProfilerSessionUsesTheSameDefault() [InlineData("value1")] public void CanEnrichActivityFromCommand(string value) { + StackExchangeRedisInstrumentation instrumentation = null; + var connectionOptions = new ConfigurationOptions { AbortOnConnectFail = true, }; connectionOptions.EndPoints.Add(RedisEndPoint); - using var connection = ConnectionMultiplexer.Connect(connectionOptions); var activityProcessor = new Mock>(); var sampler = new TestSampler(); - using (Sdk.CreateTracerProviderBuilder() - .AddProcessor(activityProcessor.Object) - .SetSampler(sampler) - .AddRedisInstrumentation(connection, c => c.Enrich = (activity, command) => - { - if (command.ElapsedTime < TimeSpan.FromMilliseconds(100)) - { - activity.AddTag("is_fast", true); - } - }) - .Build()) + + var builder = Sdk.CreateTracerProviderBuilder() + .AddProcessor(activityProcessor.Object) + .SetSampler(sampler) + .AddRedisInstrumentation(c => c.Enrich = (activity, command) => + { + if (command.ElapsedTime < TimeSpan.FromMilliseconds(100)) + { + activity.AddTag("is_fast", true); + } + }) + .ConfigureRedisInstrumentation(i => instrumentation = i); + + using (builder.Build()) { + Assert.NotNull(instrumentation); + + instrumentation.AddConnection(connection); + var db = connection.GetDatabase(); bool set = db.StringSet("key1", value, TimeSpan.FromSeconds(60)); @@ -219,7 +236,7 @@ public void CheckCacheIsFlushedProperly() var connection = ConnectionMultiplexer.Connect(connectionOptions); - using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions()); + using var instrumentation = new StackExchangeRedisConnectionInstrumentation(connection, new StackExchangeRedisInstrumentationOptions()); var profilerFactory = instrumentation.GetProfilerSessionsFactory(); // start a root level activity @@ -259,7 +276,7 @@ public async Task ProfilerSessionsHandleMultipleSpans() var connection = ConnectionMultiplexer.Connect(connectionOptions); - using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions()); + using var instrumentation = new StackExchangeRedisConnectionInstrumentation(connection, new StackExchangeRedisInstrumentationOptions()); var profilerFactory = instrumentation.GetProfilerSessionsFactory(); // start a root level activity @@ -306,14 +323,6 @@ public void StackExchangeRedis_BadArgs() { TracerProviderBuilder builder = null; Assert.Throws(() => builder.AddRedisInstrumentation(connection: null)); - - var activityProcessor = new Mock>(); - var exception = Assert.Throws(() => - Sdk.CreateTracerProviderBuilder() - .AddProcessor(activityProcessor.Object) - .AddRedisInstrumentation(name: null, connection: null, configure: null) - .Build()); - Assert.Equal("StackExchange.Redis IConnectionMultiplexer could not be resolved through application IServiceProvider", exception.Message); } [Fact] @@ -334,7 +343,7 @@ public void StackExchangeRedis_DependencyInjection_Success() connectionMultiplexerPickedFromDI = true; return ConnectionMultiplexer.Connect(connectionOptions); }); - services.Configure(options => + services.Configure(options => { optionsPickedFromDI = true; }); @@ -349,15 +358,49 @@ public void StackExchangeRedis_DependencyInjection_Success() } [Fact] - public void StackExchangeRedis_DependencyInjection_Failure() + public void StackExchangeRedis_StackExchangeRedisInstrumentation_Test() { - var services = new ServiceCollection(); + StackExchangeRedisInstrumentation instrumentation = null; - services.AddOpenTelemetry().WithTracing(builder => builder.AddRedisInstrumentation()); + var connectionOptions = new ConfigurationOptions + { + AbortOnConnectFail = false, + }; + connectionOptions.EndPoints.Add("localhost"); - using var serviceProvider = services.BuildServiceProvider(); + using var connection = ConnectionMultiplexer.Connect(connectionOptions); + + var activityProcessor = new Mock>(); + var sampler = new TestSampler(); + + var builder = Sdk.CreateTracerProviderBuilder() + .AddProcessor(activityProcessor.Object) + .SetSampler(sampler) + .AddRedisInstrumentation(c => c.Enrich = (activity, command) => + { + if (command.ElapsedTime < TimeSpan.FromMilliseconds(100)) + { + activity.AddTag("is_fast", true); + } + }) + .ConfigureRedisInstrumentation(i => instrumentation = i); + + using (builder.Build()) + { + Assert.NotNull(instrumentation); + + var registration = instrumentation.AddConnection(connection); + + Assert.NotEmpty(instrumentation.InstrumentedConnections); + + registration.Dispose(); + + Assert.Empty(instrumentation.InstrumentedConnections); + + instrumentation.AddConnection(connection); + } - Assert.Throws(() => serviceProvider.GetRequiredService()); + Assert.Empty(instrumentation.InstrumentedConnections); } private static void VerifyActivityData(Activity activity, bool isSet, EndPoint endPoint, bool setCommandKey = false) @@ -389,7 +432,7 @@ private static void VerifyActivityData(Activity activity, bool isSet, EndPoint e Assert.Equal(Status.Unset, activity.GetStatus()); Assert.Equal("redis", activity.GetTagValue(SemanticConventions.AttributeDbSystem)); - Assert.Equal(0, activity.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName)); + Assert.Equal(0, activity.GetTagValue(StackExchangeRedisConnectionInstrumentation.RedisDatabaseIndexKeyName)); if (endPoint is IPEndPoint ipEndPoint) {