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

[redis] Support manual connection management #1193

Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity!, StackExchange.Redis.Profiling.IProfiledCommand!>?
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<System.Diagnostics.Activity!, StackExchange.Redis.Profiling.IProfiledCommand!>?
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<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>? configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>? configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<System.IServiceProvider!, OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity!, StackExchange.Redis.Profiling.IProfiledCommand!>?
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<System.Diagnostics.Activity!, StackExchange.Redis.Profiling.IProfiledCommand!>?
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<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>? configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, StackExchange.Redis.IConnectionMultiplexer! connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, StackExchange.Redis.IConnectionMultiplexer? connection, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>? configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentationOptions!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Action<System.IServiceProvider!, OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisInstrumentation!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -174,7 +174,7 @@ internal static class RedisProfilerEntryToActivityConverter
return activity;
}

public static void DrainSession(Activity? parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisCallsInstrumentationOptions options)
public static void DrainSession(Activity? parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisInstrumentationOptions options)
{
foreach (var command in sessionCommands)
{
Expand Down
62 changes: 62 additions & 0 deletions src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<IConnectionMultiplexer>(
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);
Comment on lines +120 to +128
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings against this pattern, but it does seem a bit awkward to me. Given that we add instrumentation to a tracer provider it might feel more natural if we had a way to ask the tracer provider for a handle to the instrumentation. Something like

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddRedisInstrumentation()
    .Build();

using var connection1 = ConnectionMultiplexer.Connect("localhost:6379");
tracerProvider.GetInstrumentation<StackExchangeRedisInstrumentation>().AddConnection(connection1);

But I also don't feel strongly about adding a GetInstrumentation method to TracerProvider either since this Redis instrumentation is the only instrumentation where this seems to make sense today - and of course most of our instrumentation classes are not public anyways.

This is just food for thought, at the end of the day I understand this is the advanced scenario.

Copy link
Member Author

@CodeBlanch CodeBlanch May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a very interesting idea. We had the need somewhere (PrometheusExporter?) to find the registered exporter so this type of thing does seem to have some utility. But I'm not sure we have seen enough need to justify a public API.

This particular API (ConfigureRedisInstrumentation) isn't actually needed at all. Users could do this...

        var builder = Sdk.CreateTracerProviderBuilder()
            .AddRedisInstrumentation();

        StackExchangeRedisInstrumentation redisInstrumentation = null;

        ((IDeferredTracerProviderBuilder)builder).Configure((sp, builder) =>
            redisInstrumentation = sp.GetRequiredService<StackExchangeRedisInstrumentation>());

        using var tracerProvider = builder.Build();

I just thought it was so nasty we should have a helper to clean it up 🤣

Using IServiceCollection style it is much nicer:

appBuilder.Services.AddOpenTelemetry().WithTracing(builder => builder.AddRedisInstrumentation());

var app = appBuilder.Build();
var instrumentation = app.Services.GetRequiredService<StackExchangeRedisInstrumentation>();

The reason the first one is so nasty is in the "Sdk.Create" style we don't expose the IServiceProvider directly so it is kind of hard to get at. Might also be a useful public API addition to expose the IServiceProvider behind the provider instance?


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
Expand Down
Loading