Repository navigation
feat(serilog)!: the Sentry sink no longer initializes the SDK #5573
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
36fde27
5385388
f7f48a5
9f8ec8c
c72c26e
0aada87
5acfa5e
3307c30
72d27f9
9d27112
ed73567
a1bf334
7b88c80
53f4144
bbf2ffd
fbbaa39
5f4f1d1
2925072
656118c
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 |
|---|---|---|
| @@ -1,91 +1,97 @@ | ||
| using Serilog.Debugging; | ||
|
|
||
| namespace Sentry.Serilog; | ||
|
|
||
| /// <summary> | ||
| /// Sentry Sink for Serilog | ||
| /// </summary> | ||
| /// <inheritdoc cref="IDisposable" /> | ||
| /// <inheritdoc cref="ILogEventSink" /> | ||
| internal sealed partial class SentrySink : ILogEventSink, IDisposable | ||
| internal sealed partial class SentrySink : ILogEventSink | ||
| { | ||
| private readonly IDisposable? _sdkDisposable; | ||
| private readonly SentrySerilogOptions _options; | ||
|
|
||
| internal static readonly SdkVersion NameAndVersion | ||
| = typeof(SentrySink).Assembly.GetNameAndVersion(); | ||
|
|
||
| private static readonly SdkVersion Sdk = new() | ||
| { | ||
| Name = SdkName, | ||
| Version = NameAndVersion.Version, | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Serilog SDK name. | ||
| /// </summary> | ||
| public const string SdkName = "sentry.dotnet.serilog"; | ||
|
|
||
| private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name; | ||
|
|
||
| private readonly Func<IHub> _hubAccessor; | ||
| private readonly ISystemClock _clock; | ||
| private readonly UninitializedSdkWarning _uninitializedSdkWarning; | ||
|
|
||
| public SentrySink( | ||
| SentrySerilogOptions options, | ||
| IDisposable? sdkDisposable) | ||
| private int _registeredScopeEventProcessor; | ||
|
|
||
| internal const string UninitializedSdkMessage = | ||
| "Sentry: the Serilog sink dropped a log event because Sentry is not initialized, but a DSN was found " + | ||
| "in the environment or in an assembly attribute. The sink no longer initializes the SDK: call " + | ||
| "SentrySdk.Init (or UseSentry via one of the integrations) at startup and UseSerilog() on those options. " + | ||
| "See https://docs.sentry.io/platforms/dotnet/guides/serilog/"; | ||
|
|
||
| public SentrySink(SentrySerilogOptions options) | ||
| : this( | ||
| options, | ||
| () => HubAdapter.Instance, | ||
| sdkDisposable, | ||
| SystemClock.Clock) | ||
| { | ||
| } | ||
|
|
||
| internal SentrySink( | ||
| SentrySerilogOptions options, | ||
| Func<IHub> hubAccessor, | ||
| IDisposable? sdkDisposable, | ||
| ISystemClock clock) | ||
| ISystemClock clock, | ||
| UninitializedSdkWarning? uninitializedSdkWarning = null) | ||
| { | ||
| _options = options; | ||
| _hubAccessor = hubAccessor; | ||
| _clock = clock; | ||
| _sdkDisposable = sdkDisposable; | ||
| _uninitializedSdkWarning = uninitializedSdkWarning | ||
| ?? new UninitializedSdkWarning(message => SelfLog.WriteLine("{0}", message)); | ||
|
|
||
| if (hubAccessor() is { IsEnabled: true } hub && hub.GetSentryOptions() is { } sentryOptions) | ||
| { | ||
| EnsureSerilogScopeEventProcessor(sentryOptions); | ||
| } | ||
| } | ||
|
|
||
| private static AsyncLocal<bool> isReentrant = new(); | ||
|
|
||
| public void Emit(LogEvent logEvent) | ||
| { | ||
| // Must precede the reentrancy check below to avoid an infinite recursion | ||
| logEvent.TryGetSourceContext(out var context); | ||
| if (SentrySdkNamespaces.IsSentrySdk(context)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (isReentrant.Value) | ||
| { | ||
| _options.DiagnosticLogger?.LogError($"Reentrant log event detected. Logging when inside the scope of another log event can cause a StackOverflowException. LogEventInfo.Message: {logEvent.MessageTemplate.Text}"); | ||
| _hubAccessor()?.GetSentryOptions()?.DiagnosticLogger?.LogError($"Reentrant log event detected. Logging when inside the scope of another log event can cause a StackOverflowException. LogEventInfo.Message: {logEvent.MessageTemplate.Text}"); | ||
| return; | ||
| } | ||
|
|
||
| isReentrant.Value = true; | ||
| try | ||
| { | ||
| InnerEmit(logEvent); | ||
| InnerEmit(logEvent, context); | ||
| } | ||
| finally | ||
| { | ||
| isReentrant.Value = false; | ||
| } | ||
| } | ||
|
|
||
| private void InnerEmit(LogEvent logEvent) | ||
| private void InnerEmit(LogEvent logEvent, string? context) | ||
| { | ||
| if (logEvent.TryGetSourceContext(out var context)) | ||
| if (_hubAccessor() is not { IsEnabled: true } hub) | ||
| { | ||
| if (SentrySdkNamespaces.IsSentrySdk(context)) | ||
| if (logEvent.Level >= _options.MinimumEventLevel) | ||
| { | ||
| return; | ||
| _uninitializedSdkWarning.WarnOnce(UninitializedSdkMessage); | ||
| } | ||
|
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. Shutdown logs warn SDK never initializedLow Severity A disabled hub after Additional Locations (1)Reviewed by Cursor Bugbot for commit 2925072. Configure here.
Collaborator
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. Highly unlikely... that implies the SDK has been initialised, the app has been running and the SDK closed all before a single log is made (since the warning only ever fires once - it will only be triggered by the first log message if the SDK is not initialised at that time). |
||
|
|
||
| return; | ||
| } | ||
|
|
||
| if (_hubAccessor() is not { IsEnabled: true } hub) | ||
| var options = hub.GetSentryOptions(); | ||
| if (options is not null) | ||
| { | ||
| return; | ||
| EnsureSerilogScopeEventProcessor(options); | ||
| } | ||
|
|
||
| var exception = logEvent.Exception; | ||
|
|
@@ -106,17 +112,6 @@ private void InnerEmit(LogEvent logEvent) | |
| Level = logEvent.Level.ToSentryLevel() | ||
| }; | ||
|
|
||
| if (evt.Sdk is { } sdk) | ||
| { | ||
| sdk.Name = SdkName; | ||
| sdk.Version = NameAndVersion.Version; | ||
|
|
||
| if (NameAndVersion.Version is { } version) | ||
| { | ||
| sdk.AddPackage(ProtocolPackageName, version); | ||
| } | ||
| } | ||
|
|
||
| evt.SetExtras(GetLoggingEventProperties(logEvent)); | ||
|
|
||
| hub.CaptureEvent(evt); | ||
|
|
@@ -151,16 +146,31 @@ private void InnerEmit(LogEvent logEvent) | |
| level: logEvent.Level.ToBreadcrumbLevel()); | ||
| } | ||
|
|
||
| // Read the options from the Hub, rather than the Sink's Serilog-Options. In cases where Sentry's Serilog-Sink is | ||
| // added without a DSN (i.e., without initializing the SDK) and the SDK is initialized differently (e.g., through | ||
| // ASP.NET Core), only the Hub's Sentry-Options have the actual user-defined values configured. | ||
| var options = hub.GetSentryOptions(); | ||
| if (options is not null) | ||
| { | ||
| CaptureStructuredLog(hub, options, logEvent, formatted, template); | ||
| } | ||
| } | ||
|
|
||
| private void EnsureSerilogScopeEventProcessor(SentryOptions options) | ||
| { | ||
| if (Interlocked.Exchange(ref _registeredScopeEventProcessor, 1) != 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (!options.TryUseSerilog()) | ||
| { | ||
|
sentry[bot] marked this conversation as resolved.
sentry[bot] marked this conversation as resolved.
|
||
| return; | ||
| } | ||
|
|
||
| options.LogWarning( | ||
| "The Sentry sink for Serilog registered the Serilog scope event processor automatically, because " + | ||
| "UseSerilog() was not called on the options used to initialise Sentry. Events captured before the sink " + | ||
| "received its first log event will not have properties from the Serilog LogContext applied. Call " + | ||
| "UseSerilog() when initialising Sentry to apply them to every event."); | ||
| } | ||
|
|
||
| private string FormatLogEvent(LogEvent logEvent) | ||
| { | ||
| if (_options.TextFormatter is { } formatter) | ||
|
|
@@ -188,6 +198,4 @@ private string FormatLogEvent(LogEvent logEvent) | |
| } | ||
| } | ||
| } | ||
|
|
||
| public void Dispose() => _sdkDisposable?.Dispose(); | ||
| } | ||


Uh oh!
There was an error while loading. Please reload this page.