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

Experimenting with implicit OpenTelemetry initialization #3229

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 33 additions & 11 deletions samples/Sentry.Samples.OpenTelemetry.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,47 @@
*/

using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;

var serviceName = "Sentry.Samples.OpenTelemetry.Console";
var serviceVersion = "1.0.0";
var activitySource = Sentry.OpenTelemetry.TracerProviderBuilderExtensions.DefaultActivitySource;

SentrySdk.Init(options =>
{
// options.Dsn = "... Your DSN ...";
options.Dsn = "https://[email protected]/5428537";
options.TracesSampleRate = 1.0;
options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information
});

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource(serviceName)
.ConfigureResource(resource =>
resource.AddService(
serviceName: serviceName,
serviceVersion: serviceVersion))
.AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry
.Build();
// using var tracerProvider = Sdk.CreateTracerProviderBuilder()
// .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry
// .Build();

Console.WriteLine("Hello World!");
Question();
Console.WriteLine("Goodbye cruel world...");

void Question()
{
using var disposable = SentrySdk.PushScope();
SentrySdk.ConfigureScope(scope =>
{
scope.AddBreadcrumb("Asking the question...");
scope.SetTag("Question", "Meaning of life");
using var task = activitySource.StartActivity("Question");
Thread.Sleep(100); // simulate some work
Answer();
});
}

void Answer()
{
SentrySdk.ConfigureScope(scope =>
{
scope.AddBreadcrumb("Giving the answer...");
scope.SetTag("Answer", "42");
using var task = activitySource.StartActivity("Answer");
Thread.Sleep(100); // simulate some work
});
}
76 changes: 76 additions & 0 deletions src/Sentry.OpenTelemetry/InternalTracerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using OpenTelemetry;
using OpenTelemetry.Trace;
using Sentry.Extensibility;

namespace Sentry.OpenTelemetry;

internal static class InternalTracerProvider
{
internal static IHub? FallbackHub;
private static TracerProvider? FallbackTracerProvider;
private static bool WasInitializedExternally;

internal static bool InitializedExternally
{
get => WasInitializedExternally;
set
{
if (value)
{
ClearProvider();
}

WasInitializedExternally = value;
}
}

public static void InitializeFallbackTracerProvider(this SentryOptions options)
{
try
{
ClearProvider();
// This is a dummy/naive example. It works for something like a Console app. Wiring up an ASP.NET Core app
// should be done using the OpenTelemetryBuilder extensions... If we knew OpenTelemetry was being used as
// the trace implementation, we could do something like this in the SentryOptions class, rather than in an
// extension method, and override it in SentryAspNetCoreOptions etc. to be platform specific.
options.PostInitCallbacks.Add(hub =>
{
FallbackHub = hub;
FallbackTracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSentryInternal(true) // <-- Configure OpenTelemetry to send traces to Sentry
.Build();
});
}
catch (InternalTracerProviderException)
{
options.LogDebug("OpenTelemetry has been initialized externally: skipping auto-initialization");
}
}

public static void CancelInitialization()
{
throw new InternalTracerProviderException();
}

public static void ClearProvider()
{
FallbackHub = null;
FallbackTracerProvider?.Dispose();
FallbackTracerProvider = null;
}

internal class InternalTracerProviderException : Exception
{
public InternalTracerProviderException(string message) : base(message)
{
}

public InternalTracerProviderException() : base()
{
}

public InternalTracerProviderException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}
3 changes: 3 additions & 0 deletions src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
using Sentry.Extensibility;

namespace Sentry.OpenTelemetry;

Expand Down Expand Up @@ -53,5 +55,6 @@ public static void UseOpenTelemetry(this SentryOptions options)
options.AddTransactionProcessor(
new OpenTelemetryTransactionProcessor()
);
options.InitializeFallbackTracerProvider();
}
}
23 changes: 22 additions & 1 deletion src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace Sentry.OpenTelemetry;
/// </summary>
public static class TracerProviderBuilderExtensions
{
/// <summary>
/// The default <see cref="ActivitySource"/> for spans created by Sentry's instrumentation
/// </summary>
public static readonly ActivitySource DefaultActivitySource = new("Sentry");

/// <summary>
/// Ensures OpenTelemetry trace information is sent to Sentry.
/// </summary>
Expand All @@ -29,6 +34,22 @@ public static class TracerProviderBuilderExtensions
/// <returns>The supplied <see cref="TracerProviderBuilder"/> for chaining.</returns>
public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder, TextMapPropagator? defaultTextMapPropagator = null)
{
return tracerProviderBuilder.AddSentryInternal(false, defaultTextMapPropagator);
}

internal static TracerProviderBuilder AddSentryInternal(this TracerProviderBuilder tracerProviderBuilder, bool autoInitialized = false, TextMapPropagator? defaultTextMapPropagator = null)
{
// Don't automatically initialize if the user has already initialized
if (autoInitialized && InternalTracerProvider.InitializedExternally)
{
InternalTracerProvider.CancelInitialization();
}
if (!autoInitialized)
{
InternalTracerProvider.InitializedExternally = true;
}
tracerProviderBuilder.AddSource(DefaultActivitySource.Name);

defaultTextMapPropagator ??= new SentryPropagator();
Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator);
return tracerProviderBuilder.AddProcessor(ImplementationFactory);
Expand All @@ -45,7 +66,7 @@ internal static BaseProcessor<Activity> ImplementationFactory(IServiceProvider s
enrichers.Add(new AspNetCoreEnricher(userFactory));
}

var hub = services.GetService<IHub>() ?? SentrySdk.CurrentHub;
var hub = services.GetService<IHub>() ?? InternalTracerProvider.FallbackHub ?? SentrySdk.CurrentHub;
if (hub.IsEnabled)
{
return new SentrySpanProcessor(hub, enrichers);
Expand Down
Loading