-
Notifications
You must be signed in to change notification settings - Fork 890
Support for enriching activities #4284
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
Changes from all commits
e33930c
172f1db
cb58126
fe93d1d
dd77ff0
21a616a
48badfe
3f8b652
13f75f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using System; | ||
|
|
||
| namespace Npgsql.OpenTelemetry; | ||
|
|
||
| sealed class NpgsqlTracingInstrumentation : IDisposable | ||
| { | ||
| readonly NpgsqlTracingOptions _originalOptions; | ||
|
|
||
| public NpgsqlTracingInstrumentation(NpgsqlTracingOptions options) | ||
| { | ||
| _originalOptions = NpgsqlActivitySource.Options; | ||
| NpgsqlActivitySource.Options = options; | ||
| } | ||
|
|
||
| public void Dispose() => NpgsqlActivitySource.Options = _originalOptions; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| using System; | ||
| using Npgsql.OpenTelemetry; | ||
| using OpenTelemetry.Trace; | ||
|
|
||
| // ReSharper disable once CheckNamespace | ||
|
|
@@ -14,6 +15,12 @@ public static class TracerProviderBuilderExtensions | |
| /// </summary> | ||
| public static TracerProviderBuilder AddNpgsql( | ||
| this TracerProviderBuilder builder, | ||
| Action<NpgsqlTracingOptions>? options = null) | ||
| => builder.AddSource("Npgsql"); | ||
| Action<NpgsqlTracingOptions>? configure = null) | ||
| { | ||
| var options = new NpgsqlTracingOptions(); | ||
| configure?.Invoke(options); | ||
| return builder | ||
| .AddSource("Npgsql") | ||
| .AddInstrumentation(() => new NpgsqlTracingInstrumentation(options)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know the OpenTelemetry very well - what's the advantage of AddInstrumentation with NpgsqlTracingInstrumentation here over simply injecting NpgsqlActivitySource.Options directly in this method?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we only want to set
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that documentation section only relevant when the instrumentation library is separate/external, because the main library isn't instrumented? In other words, I think what we're doing qualifies as part of "make every library observable out of the box by having them call OpenTelemetry API directly" (the first sentence). On a related note, I don't think this config stuff qualifies as "state management", which is what I think the AddInstrumentation API is about. But I'm far from an expert on all this - let me know how you see things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think regardless of if the documentation section is intended to be for just separate instrumentations, the behaviour it gives us is desirable here: only set |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,18 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
|
|
||
| namespace Npgsql; | ||
|
|
||
| /// <summary> | ||
| /// Options to configure Npgsql's support for OpenTelemetry tracing. | ||
| /// Currently no options are available. | ||
| /// </summary> | ||
| public class NpgsqlTracingOptions | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets an action to enrich a Command Execution Activity. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <see href="https://www.npgsql.org/doc/diagnostics/tracing.html"/> | ||
| /// </remarks> | ||
|
Haydabase marked this conversation as resolved.
|
||
| public Action<Activity, string, object>? EnrichCommandExecution { get; set; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using NUnit.Framework; | ||
| using OpenTelemetry; | ||
| using OpenTelemetry.Trace; | ||
|
|
||
| namespace Npgsql.Tests.OpenTelemetry; | ||
|
|
||
| [NonParallelizable] | ||
| public class NpgsqlTracingOptionsTests : TestBase | ||
| { | ||
| [Test] | ||
| public void CommandExecution_start_stop() | ||
| { | ||
| using (var conn = OpenConnection()) | ||
| { | ||
| conn.ExecuteScalar("SELECT 1"); | ||
| } | ||
|
|
||
| Assert.That(_enrichInvocations, Has.Count.EqualTo(2)); | ||
|
|
||
| var (startActivity, startEventName, startObject) = _enrichInvocations[0]; | ||
| Assert.That(startEventName, Is.EqualTo("OnStartActivity")); | ||
| Assert.That(startObject, Is.TypeOf<NpgsqlCommand>().With.Property("CommandText").EqualTo("SELECT 1")); | ||
| Assert.That(startActivity.Kind, Is.EqualTo(ActivityKind.Client)); | ||
|
|
||
| var (stopActivity, stopEventName, stopObject) = _enrichInvocations[1]; | ||
| Assert.That(stopEventName, Is.EqualTo("OnStopActivity")); | ||
| Assert.That(stopObject, Is.SameAs(startObject)); | ||
| Assert.That(stopActivity, Is.SameAs(startActivity)); | ||
| } | ||
|
|
||
| [Test] | ||
| public void CommandExecution_start_exception() | ||
| { | ||
| var exception = Assert.Throws<PostgresException>(() => | ||
| { | ||
| using var conn = OpenConnection(); | ||
| conn.ExecuteScalar("BO SELECTA"); | ||
| }); | ||
|
|
||
| Assert.That(_enrichInvocations, Has.Count.EqualTo(2)); | ||
|
|
||
| var (startActivity, startEventName, startObject) = _enrichInvocations[0]; | ||
| Assert.That(startEventName, Is.EqualTo("OnStartActivity")); | ||
| Assert.That(startObject, Is.TypeOf<NpgsqlCommand>().With.Property("CommandText").EqualTo("BO SELECTA")); | ||
| Assert.That(startActivity.Kind, Is.EqualTo(ActivityKind.Client)); | ||
|
|
||
| var (stopActivity, stopEventName, stopObject) = _enrichInvocations[1]; | ||
| Assert.That(stopEventName, Is.EqualTo("OnException")); | ||
| Assert.That(stopObject, Is.TypeOf<ValueTuple<NpgsqlCommand, Exception>>()); | ||
| var (stopCommand, stopException) = (ValueTuple<NpgsqlCommand, Exception>)stopObject; | ||
| Assert.That(stopCommand.CommandText, Is.EqualTo("BO SELECTA")); | ||
| Assert.That(stopException, Is.SameAs(exception)); | ||
| Assert.That(stopActivity, Is.SameAs(startActivity)); | ||
| } | ||
|
|
||
| [Test] | ||
| public void CommandExecution_start_exception_patternmatch() | ||
| { | ||
| var exception = Assert.Throws<PostgresException>(() => | ||
| { | ||
| using var conn = OpenConnection(); | ||
| conn.ExecuteScalar("BO SELECTA"); | ||
| }); | ||
|
|
||
| Assert.That(_enrichInvocations, Has.Count.EqualTo(2)); | ||
| var (_, stopEventName, stopObject) = _enrichInvocations[1]; | ||
|
|
||
| switch (stopEventName, stopObject) | ||
| { | ||
| case ("OnException", (NpgsqlCommand stopCommand, Exception stopException)): | ||
| Assert.That(stopCommand.CommandText, Is.EqualTo("BO SELECTA")); | ||
| Assert.That(stopException, Is.SameAs(exception)); | ||
| break; | ||
| default: | ||
| Assert.Fail($"{nameof(stopEventName)}: '{stopEventName}', {nameof(stopObject)}.GetType(): '{stopObject.GetType()}'"); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| [SetUp] | ||
| public void SetUp() | ||
| { | ||
| _enrichInvocations.Clear(); | ||
| _tracerProvider = Sdk.CreateTracerProviderBuilder() | ||
| .AddNpgsql(o => o.EnrichCommandExecution = (activity, eventName, rawObject) => _enrichInvocations.Add((activity, eventName, rawObject))) | ||
| .Build(); | ||
| } | ||
|
|
||
| [TearDown] | ||
| public void TearDown() => _tracerProvider.Dispose(); | ||
|
|
||
| TracerProvider _tracerProvider = null!; | ||
|
|
||
| readonly List<(Activity activity, string eventName, object rawObject)> _enrichInvocations = new(); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.