From 36fde274b235d6fcf795199b308ae6eb3d7d1256 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 14 Sep 2026 12:42:15 +1200 Subject: [PATCH 01/42] feat: Serilog sink no longer initializes the SDK The Sentry sink for Serilog now only configures the sink. Sentry must be initialized separately (SentrySdk.Init, UseSentry, etc). - SentrySerilogOptions no longer derives from SentryOptions and only carries sink settings; InitializeSdk is removed - Remove the WriteTo.Sentry(string dsn, ...) overload - Rename ApplySerilogScopeToEvents() to UseSerilog(), make it idempotent - The sink logs a one-time diagnostic warning when UseSerilog() was not called on the options used to initialize Sentry Part of #5245 Co-Authored-By: Claude Opus 5 --- .../Program.cs | 20 +- samples/Sentry.Samples.Serilog/Program.cs | 29 +- src/Sentry.Serilog/SentryOptionExtensions.cs | 25 +- src/Sentry.Serilog/SentrySerilogOptions.cs | 14 +- src/Sentry.Serilog/SentrySink.cs | 43 ++- src/Sentry.Serilog/SentrySinkExtensions.cs | 285 +----------------- ...iApprovalTests.Run.DotNet10_0.verified.txt | 34 +-- ...iApprovalTests.Run.DotNet11_0.verified.txt | 34 +-- ...piApprovalTests.Run.DotNet8_0.verified.txt | 34 +-- ...piApprovalTests.Run.DotNet9_0.verified.txt | 34 +-- .../ApiApprovalTests.Run.Net4_8.verified.txt | 34 +-- ...rationTests.Simple.DotNet10_0.verified.txt | 20 +- ...grationTests.Simple.DotNet8_0.verified.txt | 20 +- ...grationTests.Simple.DotNet9_0.verified.txt | 20 +- .../IntegrationTests.verify.cs | 183 +++++------ .../SentryOptionExtensionsTests.cs | 25 ++ .../SentrySerilogSinkExtensionsTests.cs | 112 ++----- .../SentrySinkTests.Structured.cs | 6 +- test/Sentry.Serilog.Tests/SentrySinkTests.cs | 44 ++- .../SerilogAspNetSentrySdkTestFixture.cs | 2 +- 20 files changed, 338 insertions(+), 680 deletions(-) create mode 100644 test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs b/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs index 65d3d64732..9fe8854732 100644 --- a/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs +++ b/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs @@ -1,3 +1,4 @@ +using Sentry.Serilog; using Serilog; using Serilog.Events; @@ -14,28 +15,29 @@ public static WebApplication BuildWebApp(string[] args) c.Enrich.FromLogContext() .MinimumLevel.Debug() .WriteTo.Console() - // Add Sentry integration with Serilog + // Add Sentry integration with Serilog. This only configures the sink - Sentry itself is + // initialised by UseSentry below. .WriteTo.Sentry(s => { // Sets the minimum log level required to add a log message as breadcrumb s.MinimumBreadcrumbLevel = LogEventLevel.Debug; // Set the minimum level for messages to be sent out as events to Sentry s.MinimumEventLevel = LogEventLevel.Error; - // When configuring Sentry's Serilog integration in combination with other integrations that - // initialize the Sentry SDK (like ASP.NET Core or MAUI) we need to tell it not to reinitialize - // Sentry... we just want it to set up the Serilog sink - s.InitializeSdk = false; })); // Add Sentry integration // It can be defined via configuration (including `appsettings.json`) - // or coded explicitly, via parameter like: - // .UseSentry("dsn") or .UseSentry(o => o.Dsn = ""; o.Release = "1.0"; ...) + // or coded explicitly, in the options callback: o.Dsn = ""; o.Release = "1.0"; ... + builder.WebHost.UseSentry(o => + { #if !SENTRY_DSN_DEFINED_IN_ENV - builder.WebHost.UseSentry(SamplesShared.Dsn); + o.Dsn = SamplesShared.Dsn; #else - builder.WebHost.UseSentry(EnvironmentVariables.Dsn); + o.Dsn = EnvironmentVariables.Dsn; #endif + // Apply properties from the Serilog LogContext to Sentry events + o.UseSerilog(); + }); // The App: var webApplication = builder.Build(); diff --git a/samples/Sentry.Samples.Serilog/Program.cs b/samples/Sentry.Samples.Serilog/Program.cs index 59ed09548d..26d91f9676 100644 --- a/samples/Sentry.Samples.Serilog/Program.cs +++ b/samples/Sentry.Samples.Serilog/Program.cs @@ -1,3 +1,4 @@ +using Sentry.Serilog; using Serilog; using Serilog.Context; using Serilog.Events; @@ -7,29 +8,35 @@ internal static class Program { private static void Main() { + // Initialise Sentry. The Serilog sink below doesn't do this for you. + using var _ = SentrySdk.Init(options => + { +#if !SENTRY_DSN_DEFINED_IN_ENV + // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = SamplesShared.Dsn; +#endif + + options.AttachStacktrace = true; + // send PII like the username of the user logged in to the device + options.SendDefaultPii = true; + // Apply properties from the Serilog LogContext (like MyTaskId below) to Sentry events + options.UseSerilog(); + }); + Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Debug() .WriteTo.Console() - // Other overloads exist, for example, configure the SDK with only the DSN or no parameters at all. + // Other overloads exist, for example, configuring the sink with no parameters at all. .WriteTo.Sentry(options => { -#if !SENTRY_DSN_DEFINED_IN_ENV - // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - options.Dsn = SamplesShared.Dsn; -#endif - // Debug and higher are stored as breadcrumbs (default os Information) options.MinimumBreadcrumbLevel = LogEventLevel.Debug; // Error and higher is sent as event (default is Error) options.MinimumEventLevel = LogEventLevel.Error; - options.AttachStacktrace = true; - // send PII like the username of the user logged in to the device - options.SendDefaultPii = true; // Optional Serilog text formatter used to format LogEvent to string. If TextFormatter is set, FormatProvider is ignored. options.TextFormatter = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); - // Other configuration }) .CreateLogger(); diff --git a/src/Sentry.Serilog/SentryOptionExtensions.cs b/src/Sentry.Serilog/SentryOptionExtensions.cs index 94dfbc5212..662ef30b87 100644 --- a/src/Sentry.Serilog/SentryOptionExtensions.cs +++ b/src/Sentry.Serilog/SentryOptionExtensions.cs @@ -6,16 +6,25 @@ namespace Sentry.Serilog; public static class SentryOptionExtensions { /// - /// Ensures Serilog scope properties get applied to Sentry events. If you are not initialising Sentry when - /// configuring the Sentry sink for Serilog then you should call this method in the options callback for whichever - /// Sentry integration you are using to initialise Sentry. + /// Enables the Serilog integration, so that properties from the Serilog LogContext get applied to all Sentry + /// events. /// - /// - /// - /// - public static T ApplySerilogScopeToEvents(this T options) where T : SentryOptions + /// + /// Call this in the options callback of whichever method you use to initialise Sentry (for example + /// SentrySdk.Init or UseSentry). The Sentry sink for Serilog does not initialise Sentry, so it cannot + /// do this for you. Calling this more than once has no additional effect. + /// + /// The options used to initialise Sentry. + public static void UseSerilog(this SentryOptions options) { + if (options.HasSerilogScopeEventProcessor()) + { + return; + } + options.AddEventProcessor(new SerilogScopeEventProcessor(options)); - return options; } + + internal static bool HasSerilogScopeEventProcessor(this SentryOptions options) + => options.EventProcessors.Exists(processor => processor.Type == typeof(SerilogScopeEventProcessor)); } diff --git a/src/Sentry.Serilog/SentrySerilogOptions.cs b/src/Sentry.Serilog/SentrySerilogOptions.cs index b06432736a..65a4ee544a 100644 --- a/src/Sentry.Serilog/SentrySerilogOptions.cs +++ b/src/Sentry.Serilog/SentrySerilogOptions.cs @@ -1,16 +1,14 @@ namespace Sentry.Serilog; /// -/// Sentry Options for Serilog logging +/// Options for the Sentry sink for Serilog. /// -/// -public class SentrySerilogOptions : SentryOptions +/// +/// These options only configure the sink. The Sentry SDK itself is configured and initialised separately, using +/// SentrySdk.Init or another Sentry integration (such as ASP.NET Core or MAUI). +/// +public class SentrySerilogOptions { - /// - /// Whether to initialize this SDK through this integration - /// - public bool InitializeSdk { get; set; } = true; - /// /// Minimum log level to send an event. /// diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 9ce57c691b..bfd406045b 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -3,11 +3,9 @@ namespace Sentry.Serilog; /// /// Sentry Sink for Serilog /// -/// /// -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 @@ -29,13 +27,12 @@ internal static readonly SdkVersion NameAndVersion private readonly Func _hubAccessor; private readonly ISystemClock _clock; - public SentrySink( - SentrySerilogOptions options, - IDisposable? sdkDisposable) + private volatile bool _checkedUseSerilog; + + public SentrySink(SentrySerilogOptions options) : this( options, () => HubAdapter.Instance, - sdkDisposable, SystemClock.Clock) { } @@ -43,13 +40,11 @@ public SentrySink( internal SentrySink( SentrySerilogOptions options, Func hubAccessor, - IDisposable? sdkDisposable, ISystemClock clock) { _options = options; _hubAccessor = hubAccessor; _clock = clock; - _sdkDisposable = sdkDisposable; } private static AsyncLocal isReentrant = new(); @@ -58,7 +53,7 @@ public void Emit(LogEvent logEvent) { 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; } @@ -88,6 +83,12 @@ private void InnerEmit(LogEvent logEvent) return; } + var options = hub.GetSentryOptions(); + if (options is not null) + { + WarnIfUseSerilogNotCalled(options); + } + var exception = logEvent.Exception; var template = logEvent.MessageTemplate.Text; var formatted = FormatLogEvent(logEvent); @@ -151,16 +152,28 @@ 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 WarnIfUseSerilogNotCalled(SentryOptions options) + { + if (_checkedUseSerilog) + { + return; + } + + _checkedUseSerilog = true; + if (!options.HasSerilogScopeEventProcessor()) + { + options.LogWarning( + "The Sentry sink for Serilog is in use, but UseSerilog() was not called on the options used to initialise Sentry. " + + "Properties from the Serilog LogContext will not be applied to Sentry events."); + } + } + private string FormatLogEvent(LogEvent logEvent) { if (_options.TextFormatter is { } formatter) @@ -188,6 +201,4 @@ private string FormatLogEvent(LogEvent logEvent) } } } - - public void Dispose() => _sdkDisposable?.Dispose(); } diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index 7c164ac6b4..d35e247cf2 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -8,143 +8,12 @@ namespace Serilog; [EditorBrowsable(EditorBrowsableState.Never)] public static class SentrySinkExtensions { - /// - /// Initialize Sentry and add the SentrySink for Serilog. - /// - /// The logger configuration . - /// The Sentry DSN (required). - /// Minimum log level to record a breadcrumb. - /// Minimum log level to send an event. - /// The Serilog format provider. - /// The Serilog text formatter. - /// Whether to include default Personal Identifiable information. - /// Whether to report the as the User affected in the event. - /// Gets or sets the name of the server running the application. - /// Whether to send the stack trace of a event captured without an exception. - /// Gets or sets the maximum breadcrumbs. - /// The rate to sample events. - /// The release version of the application. - /// The environment the application is running. - /// The maximum number of events to keep while the worker attempts to send them. - /// How long to wait for events to be sent before shutdown. - /// Decompression methods accepted. - /// The level of which to compress the before sending to Sentry. - /// Whether the body compression is buffered and the request 'Content-Length' known in advance. - /// Whether to log diagnostics messages. - /// The diagnostics level to be used. - /// What mode to use for reporting referenced assemblies in each event sent to sentry. Defaults to - /// What modes to use for event automatic de-duplication. - /// Default tags to add to all events. - /// Ignored. Structured logs are always sent. To drop logs, use and return . - /// The minimum level for events passed through the sink. Ignored when is specified. - /// A switch allowing the pass-through minimum level to be changed at runtime. - /// - /// This sample shows how each item may be set from within a configuration file: - /// - /// { - /// "Serilog": { - /// "Using": [ - /// "Serilog", - /// "Sentry", - /// ], - /// "WriteTo": [{ - /// "Name": "Sentry", - /// "Args": { - /// "dsn": "https://MY-DSN@sentry.io", - /// "minimumBreadcrumbLevel": "Verbose", - /// "minimumEventLevel": "Error", - /// "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}", - /// "sendDefaultPii": false, - /// "isEnvironmentUser": false, - /// "serverName": "MyServerName", - /// "attachStackTrace": false, - /// "maxBreadcrumbs": 20, - /// "sampleRate": 0.5, - /// "release": "0.0.1", - /// "environment": "staging", - /// "maxQueueItems": 100, - /// "shutdownTimeout": "00:00:05", - /// "decompressionMethods": "GZip", - /// "requestBodyCompressionLevel": "NoCompression", - /// "requestBodyCompressionBuffered": false, - /// "debug": false, - /// "diagnosticLevel": "Debug", - /// "reportAssembliesMode": ReportAssembliesMode.None, - /// "deduplicateMode": "All", - /// "defaultTags": { - /// "key-1", "value-1", - /// "key-2", "value-2" - /// } - /// } - /// } - /// ] - /// } - /// } - /// - /// - public static LoggerConfiguration Sentry( - this LoggerSinkConfiguration loggerConfiguration, - string dsn, - LogEventLevel? minimumBreadcrumbLevel = null, - LogEventLevel? minimumEventLevel = null, - IFormatProvider? formatProvider = null, - ITextFormatter? textFormatter = null, - bool? sendDefaultPii = null, - bool? isEnvironmentUser = null, - string? serverName = null, - bool? attachStackTrace = null, - int? maxBreadcrumbs = null, - float? sampleRate = null, - string? release = null, - string? environment = null, - int? maxQueueItems = null, - TimeSpan? shutdownTimeout = null, - DecompressionMethods? decompressionMethods = null, - CompressionLevel? requestBodyCompressionLevel = null, - bool? requestBodyCompressionBuffered = null, - bool? debug = null, - SentryLevel? diagnosticLevel = null, - ReportAssembliesMode? reportAssembliesMode = null, - DeduplicateMode? deduplicateMode = null, - Dictionary? defaultTags = null, - bool? enableLogs = null, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch? levelSwitch = null) - { - return loggerConfiguration.Sentry(o => ConfigureSentrySerilogOptions(o, - dsn, - minimumEventLevel, - minimumBreadcrumbLevel, - formatProvider, - textFormatter, - sendDefaultPii, - isEnvironmentUser, - serverName, - attachStackTrace, - maxBreadcrumbs, - sampleRate, - release, - environment, - maxQueueItems, - shutdownTimeout, - decompressionMethods, - requestBodyCompressionLevel, - requestBodyCompressionBuffered, - debug, - diagnosticLevel, - reportAssembliesMode, - deduplicateMode, - defaultTags, - enableLogs, - restrictedToMinimumLevel, - levelSwitch)); - } - /// /// Adds a Sentry Sink for Serilog. /// - /// Note this overload doesn't initialize Sentry for you, so you'll need to have already done so. Alternatively you - /// can use use the overload of this extension method, passing a DSN string in the first argument. + /// This doesn't initialise Sentry. Initialise Sentry separately, using SentrySdk.Init or another Sentry + /// integration (such as ASP.NET Core or MAUI), and call on the + /// options used to do so. /// /// /// The logger configuration . @@ -186,49 +55,23 @@ public static LoggerConfiguration Sentry( LoggingLevelSwitch? levelSwitch = null) { return loggerConfiguration.Sentry(o => ConfigureSentrySerilogOptions(o, - null, minimumEventLevel, minimumBreadcrumbLevel, formatProvider, textFormatter, - restrictedToMinimumLevel: restrictedToMinimumLevel, - levelSwitch: levelSwitch)); + restrictedToMinimumLevel, + levelSwitch)); } internal static void ConfigureSentrySerilogOptions( SentrySerilogOptions sentrySerilogOptions, - string? dsn, LogEventLevel? minimumEventLevel = null, LogEventLevel? minimumBreadcrumbLevel = null, IFormatProvider? formatProvider = null, ITextFormatter? textFormatter = null, - bool? sendDefaultPii = null, - bool? isEnvironmentUser = null, - string? serverName = null, - bool? attachStackTrace = null, - int? maxBreadcrumbs = null, - float? sampleRate = null, - string? release = null, - string? environment = null, - int? maxQueueItems = null, - TimeSpan? shutdownTimeout = null, - DecompressionMethods? decompressionMethods = null, - CompressionLevel? requestBodyCompressionLevel = null, - bool? requestBodyCompressionBuffered = null, - bool? debug = null, - SentryLevel? diagnosticLevel = null, - ReportAssembliesMode? reportAssembliesMode = null, - DeduplicateMode? deduplicateMode = null, - Dictionary? defaultTags = null, - bool? enableLogs = null, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch? levelSwitch = null) { - if (dsn is not null) - { - sentrySerilogOptions.Dsn = dsn; - } - if (minimumEventLevel.HasValue) { sentrySerilogOptions.MinimumEventLevel = minimumEventLevel.Value; @@ -249,115 +92,17 @@ internal static void ConfigureSentrySerilogOptions( sentrySerilogOptions.TextFormatter = textFormatter; } - if (sendDefaultPii.HasValue) - { - sentrySerilogOptions.SendDefaultPii = sendDefaultPii.Value; - } - - if (isEnvironmentUser.HasValue) - { - sentrySerilogOptions.IsEnvironmentUser = isEnvironmentUser.Value; - } - - if (!string.IsNullOrWhiteSpace(serverName)) - { - sentrySerilogOptions.ServerName = serverName; - } - - if (attachStackTrace.HasValue) - { - sentrySerilogOptions.AttachStacktrace = attachStackTrace.Value; - } - - if (maxBreadcrumbs.HasValue) - { - sentrySerilogOptions.MaxBreadcrumbs = maxBreadcrumbs.Value; - } - - if (sampleRate.HasValue) - { - sentrySerilogOptions.SampleRate = sampleRate; - } - - if (!string.IsNullOrWhiteSpace(release)) - { - sentrySerilogOptions.Release = release; - } - - if (!string.IsNullOrWhiteSpace(environment)) - { - sentrySerilogOptions.Environment = environment; - } - - if (maxQueueItems.HasValue) - { - sentrySerilogOptions.MaxQueueItems = maxQueueItems.Value; - } - - if (shutdownTimeout.HasValue) - { - sentrySerilogOptions.ShutdownTimeout = shutdownTimeout.Value; - } - - if (decompressionMethods.HasValue) - { - sentrySerilogOptions.DecompressionMethods = decompressionMethods.Value; - } - - if (requestBodyCompressionLevel.HasValue) - { - sentrySerilogOptions.RequestBodyCompressionLevel = requestBodyCompressionLevel.Value; - } - - if (requestBodyCompressionBuffered.HasValue) - { - sentrySerilogOptions.RequestBodyCompressionBuffered = requestBodyCompressionBuffered.Value; - } - - if (debug.HasValue) - { - sentrySerilogOptions.Debug = debug.Value; - } - - if (diagnosticLevel.HasValue) - { - sentrySerilogOptions.DiagnosticLevel = diagnosticLevel.Value; - } - - if (reportAssembliesMode.HasValue) - { - sentrySerilogOptions.ReportAssembliesMode = reportAssembliesMode.Value; - } - - if (deduplicateMode.HasValue) - { - sentrySerilogOptions.DeduplicateMode = deduplicateMode.Value; - } - sentrySerilogOptions.RestrictedToMinimumLevel = restrictedToMinimumLevel; sentrySerilogOptions.LevelSwitch = levelSwitch; - - // Serilog-specific items - sentrySerilogOptions.InitializeSdk = dsn is not null; // Inferred from the Sentry overload that is used - if (defaultTags?.Count > 0) - { - foreach (var tag in defaultTags) - { - sentrySerilogOptions.DefaultTags.Add(tag.Key, tag.Value); - } - } - - // This only works when the SDK is initialized using the LoggerSinkConfiguration extensions. If the SDK is - // initialized using some other integration then the processor will need to be added manually to whichever - // options are used to initialize the SDK. - if (sentrySerilogOptions.InitializeSdk) - { - sentrySerilogOptions.ApplySerilogScopeToEvents(); - } } /// - /// Add Sentry sink to Serilog. + /// Adds a Sentry Sink for Serilog. + /// + /// This doesn't initialise Sentry. Initialise Sentry separately, using SentrySdk.Init or another Sentry + /// integration (such as ASP.NET Core or MAUI), and call on the + /// options used to do so. + /// /// /// The logger configuration. /// The configure options callback. @@ -368,12 +113,6 @@ public static LoggerConfiguration Sentry( var options = new SentrySerilogOptions(); configureOptions?.Invoke(options); - IDisposable? sdkDisposable = null; - if (options.InitializeSdk) - { - sdkDisposable = SentrySdk.Init(options); - } - - return loggerConfiguration.Sink(new SentrySink(options, sdkDisposable), options.RestrictedToMinimumLevel, options.LevelSwitch); + return loggerConfiguration.Sink(new SentrySink(options), options.RestrictedToMinimumLevel, options.LevelSwitch); } } diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 6374c53e3e..b43e743529 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -3,14 +3,12 @@ namespace Sentry.Serilog { public static class SentryOptionExtensions { - public static T ApplySerilogScopeToEvents(this T options) - where T : Sentry.SentryOptions { } + public static void UseSerilog(this Sentry.SentryOptions options) { } } - public class SentrySerilogOptions : Sentry.SentryOptions + public class SentrySerilogOptions { public SentrySerilogOptions() { } public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } public Serilog.Core.LoggingLevelSwitch? LevelSwitch { get; set; } public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } @@ -24,33 +22,5 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null, - bool? enableLogs = default, - Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, - Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 6374c53e3e..b43e743529 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -3,14 +3,12 @@ namespace Sentry.Serilog { public static class SentryOptionExtensions { - public static T ApplySerilogScopeToEvents(this T options) - where T : Sentry.SentryOptions { } + public static void UseSerilog(this Sentry.SentryOptions options) { } } - public class SentrySerilogOptions : Sentry.SentryOptions + public class SentrySerilogOptions { public SentrySerilogOptions() { } public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } public Serilog.Core.LoggingLevelSwitch? LevelSwitch { get; set; } public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } @@ -24,33 +22,5 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null, - bool? enableLogs = default, - Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, - Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 6374c53e3e..b43e743529 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -3,14 +3,12 @@ namespace Sentry.Serilog { public static class SentryOptionExtensions { - public static T ApplySerilogScopeToEvents(this T options) - where T : Sentry.SentryOptions { } + public static void UseSerilog(this Sentry.SentryOptions options) { } } - public class SentrySerilogOptions : Sentry.SentryOptions + public class SentrySerilogOptions { public SentrySerilogOptions() { } public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } public Serilog.Core.LoggingLevelSwitch? LevelSwitch { get; set; } public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } @@ -24,33 +22,5 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null, - bool? enableLogs = default, - Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, - Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 6374c53e3e..b43e743529 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -3,14 +3,12 @@ namespace Sentry.Serilog { public static class SentryOptionExtensions { - public static T ApplySerilogScopeToEvents(this T options) - where T : Sentry.SentryOptions { } + public static void UseSerilog(this Sentry.SentryOptions options) { } } - public class SentrySerilogOptions : Sentry.SentryOptions + public class SentrySerilogOptions { public SentrySerilogOptions() { } public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } public Serilog.Core.LoggingLevelSwitch? LevelSwitch { get; set; } public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } @@ -24,33 +22,5 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null, - bool? enableLogs = default, - Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, - Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 6374c53e3e..b43e743529 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -3,14 +3,12 @@ namespace Sentry.Serilog { public static class SentryOptionExtensions { - public static T ApplySerilogScopeToEvents(this T options) - where T : Sentry.SentryOptions { } + public static void UseSerilog(this Sentry.SentryOptions options) { } } - public class SentrySerilogOptions : Sentry.SentryOptions + public class SentrySerilogOptions { public SentrySerilogOptions() { } public System.IFormatProvider? FormatProvider { get; set; } - public bool InitializeSdk { get; set; } public Serilog.Core.LoggingLevelSwitch? LevelSwitch { get; set; } public Serilog.Events.LogEventLevel MinimumBreadcrumbLevel { get; set; } public Serilog.Events.LogEventLevel MinimumEventLevel { get; set; } @@ -24,33 +22,5 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - public static Serilog.LoggerConfiguration Sentry( - this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, - string dsn, - Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, - Serilog.Events.LogEventLevel? minimumEventLevel = default, - System.IFormatProvider? formatProvider = null, - Serilog.Formatting.ITextFormatter? textFormatter = null, - bool? sendDefaultPii = default, - bool? isEnvironmentUser = default, - string? serverName = null, - bool? attachStackTrace = default, - int? maxBreadcrumbs = default, - float? sampleRate = default, - string? release = null, - string? environment = null, - int? maxQueueItems = default, - System.TimeSpan? shutdownTimeout = default, - System.Net.DecompressionMethods? decompressionMethods = default, - System.IO.Compression.CompressionLevel? requestBodyCompressionLevel = default, - bool? requestBodyCompressionBuffered = default, - bool? debug = default, - Sentry.SentryLevel? diagnosticLevel = default, - Sentry.ReportAssembliesMode? reportAssembliesMode = default, - Sentry.DeduplicateMode? deduplicateMode = default, - System.Collections.Generic.Dictionary? defaultTags = null, - bool? enableLogs = default, - Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, - Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt index 952d080225..902ed8c912 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt @@ -41,6 +41,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -95,6 +99,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 65 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -153,6 +161,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -203,8 +215,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, - ColumnNumber: 17, + LineNumber: 53, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, @@ -272,6 +284,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt index 952d080225..902ed8c912 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt @@ -41,6 +41,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -95,6 +99,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 65 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -153,6 +161,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -203,8 +215,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, - ColumnNumber: 17, + LineNumber: 53, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, @@ -272,6 +284,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt index 952d080225..902ed8c912 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt @@ -41,6 +41,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -95,6 +99,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 65 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -153,6 +161,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } @@ -203,8 +215,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 47, - ColumnNumber: 17, + LineNumber: 53, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, @@ -272,6 +284,10 @@ Extra: { inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, MyTaskId: 42 + }, + Tags: { + inventory: { SmallPotion = 3, BigPotion = 0, CheeseWheels = 512 }, + MyTaskId: 42 } } } diff --git a/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs b/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs index 61e3bd0cdf..614bb8b88b 100644 --- a/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.Serilog.Tests/IntegrationTests.verify.cs @@ -9,51 +9,58 @@ public Task Simple() { var transport = new RecordingTransport(); - var configuration = new LoggerConfiguration(); - configuration.Enrich.FromLogContext(); - configuration.MinimumLevel.Debug(); - configuration.WriteTo.Sentry( - _ => - { - _.TracesSampleRate = 1; - _.MinimumBreadcrumbLevel = LogEventLevel.Debug; - _.MinimumEventLevel = LogEventLevel.Debug; - _.Transport = transport; - _.Dsn = ValidDsn; - _.SendDefaultPii = true; - _.TextFormatter = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); - _.AttachStacktrace = false; - _.Release = "test-release"; - }); - - Log.Logger = configuration.CreateLogger(); - using (LogContext.PushProperty("MyTaskId", 42)) - using (LogContext.PushProperty( - "inventory", - new + using (SentrySdk.Init( + options => { - SmallPotion = 3, - BigPotion = 0, - CheeseWheels = 512 + options.TracesSampleRate = 1; + options.Transport = transport; + options.Dsn = ValidDsn; + options.SendDefaultPii = true; + options.AttachStacktrace = false; + options.Release = "test-release"; + options.UseSerilog(); })) { - Log.Verbose("Verbose message which is not sent."); - Log.Debug("Debug message stored as breadcrumb."); - Log.ForContext("MyTaskId", 65).Debug("Message with a different MyTaskId"); - Log.Error("Some event that includes the previous breadcrumbs"); - - try - { - throw new("Exception message"); - } - catch (Exception exception) + var configuration = new LoggerConfiguration(); + configuration.Enrich.FromLogContext(); + configuration.MinimumLevel.Debug(); + configuration.WriteTo.Sentry( + _ => + { + _.MinimumBreadcrumbLevel = LogEventLevel.Debug; + _.MinimumEventLevel = LogEventLevel.Debug; + _.TextFormatter = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); + }); + + Log.Logger = configuration.CreateLogger(); + using (LogContext.PushProperty("MyTaskId", 42)) + using (LogContext.PushProperty( + "inventory", + new + { + SmallPotion = 3, + BigPotion = 0, + CheeseWheels = 512 + })) { - exception.Data.Add("details", "Do work always throws."); - Log.Fatal(exception, "Error: with exception"); + Log.Verbose("Verbose message which is not sent."); + Log.Debug("Debug message stored as breadcrumb."); + Log.ForContext("MyTaskId", 65).Debug("Message with a different MyTaskId"); + Log.Error("Some event that includes the previous breadcrumbs"); + + try + { + throw new("Exception message"); + } + catch (Exception exception) + { + exception.Data.Add("details", "Do work always throws."); + Log.Fatal(exception, "Error: with exception"); + } } - } - Log.CloseAndFlush(); + Log.CloseAndFlush(); + } return Verify(transport.Envelopes) .UniqueForRuntimeAndVersion() @@ -64,31 +71,34 @@ public Task Simple() public Task LoggingInsideTheContextOfLogging() { var transport = new RecordingTransport(); + var diagnosticLogger = new InMemoryDiagnosticLogger(); - var configuration = new LoggerConfiguration(); + using (SentrySdk.Init( + options => + { + options.TracesSampleRate = 1; + options.Transport = transport; + options.DiagnosticLogger = diagnosticLogger; + options.Dsn = ValidDsn; + options.Debug = true; + options.AttachStacktrace = false; + options.Release = "test-release"; + options.UseSerilog(); + })) + { + var configuration = new LoggerConfiguration(); + configuration.WriteTo.Sentry(_ => { }); - var diagnosticLogger = new InMemoryDiagnosticLogger(); - configuration.WriteTo.Sentry( - _ => - { - _.TracesSampleRate = 1; - _.Transport = transport; - _.DiagnosticLogger = diagnosticLogger; - _.Dsn = ValidDsn; - _.Debug = true; - _.AttachStacktrace = false; - _.Release = "test-release"; - }); - - Log.Logger = configuration.CreateLogger(); - - SentrySdk.ConfigureScope( - scope => - { - scope.OnEvaluating += (_, _) => Log.Error("message from OnEvaluating"); - Log.Error("message"); - }); - Log.CloseAndFlush(); + Log.Logger = configuration.CreateLogger(); + + SentrySdk.ConfigureScope( + scope => + { + scope.OnEvaluating += (_, _) => Log.Error("message from OnEvaluating"); + Log.Error("message"); + }); + Log.CloseAndFlush(); + } return Verify( new @@ -105,30 +115,33 @@ public Task LoggingInsideTheContextOfLogging() public Task StructuredLogging() { var transport = new RecordingTransport(); - - var configuration = new LoggerConfiguration(); - configuration.MinimumLevel.Debug(); var diagnosticLogger = new InMemoryDiagnosticLogger(); - configuration.WriteTo.Sentry( - _ => - { - _.MinimumEventLevel = (LogEventLevel)int.MaxValue; - _.Transport = transport; - _.DiagnosticLogger = diagnosticLogger; - _.Dsn = ValidDsn; - _.Debug = true; - _.Environment = "test-environment"; - _.Release = "test-release"; - }); - - Log.Logger = configuration.CreateLogger(); - - Log.Debug("Debug message with a Scalar property: {Scalar}", 42); - Log.Information("Information message with a Sequence property: {Sequence}", new object[] { new int[] { 41, 42, 43 } }); - Log.Warning("Warning message with a Dictionary property: {Dictionary}", new Dictionary { { "key", "value" } }); - Log.Error("Error message with a Structure property: {Structure}", (Number: 42, Text: "42")); - - Log.CloseAndFlush(); + + using (SentrySdk.Init( + options => + { + options.Transport = transport; + options.DiagnosticLogger = diagnosticLogger; + options.Dsn = ValidDsn; + options.Debug = true; + options.Environment = "test-environment"; + options.Release = "test-release"; + options.UseSerilog(); + })) + { + var configuration = new LoggerConfiguration(); + configuration.MinimumLevel.Debug(); + configuration.WriteTo.Sentry(_ => _.MinimumEventLevel = (LogEventLevel)int.MaxValue); + + Log.Logger = configuration.CreateLogger(); + + Log.Debug("Debug message with a Scalar property: {Scalar}", 42); + Log.Information("Information message with a Sequence property: {Sequence}", new object[] { new int[] { 41, 42, 43 } }); + Log.Warning("Warning message with a Dictionary property: {Dictionary}", new Dictionary { { "key", "value" } }); + Log.Error("Error message with a Structure property: {Structure}", (Number: 42, Text: "42")); + + Log.CloseAndFlush(); + } var envelopes = transport.Envelopes; var logs = transport.Payloads.OfType() diff --git a/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs b/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs new file mode 100644 index 0000000000..209ef079fa --- /dev/null +++ b/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs @@ -0,0 +1,25 @@ +namespace Sentry.Serilog.Tests; + +public class SentryOptionExtensionsTests +{ + [Fact] + public void UseSerilog_AddsSerilogScopeEventProcessor() + { + var options = new SentryOptions(); + + options.UseSerilog(); + + options.GetAllEventProcessors().OfType().Should().ContainSingle(); + } + + [Fact] + public void UseSerilog_CalledTwice_AddsProcessorOnce() + { + var options = new SentryOptions(); + + options.UseSerilog(); + options.UseSerilog(); + + options.GetAllEventProcessors().OfType().Should().ContainSingle(); + } +} diff --git a/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs b/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs index ce2e2e1f46..fcbcf55d18 100644 --- a/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs @@ -1,33 +1,16 @@ +using Serilog.Formatting; + namespace Sentry.Serilog.Tests; public class SentrySerilogSinkExtensionsTests { private class Fixture { - public SentrySerilogOptions Options { get; } = new(); - - // Parameter values that are NOT set to the default values in SentryOptions or SentrySerilogOptions - public bool SendDefaultPii { get; } = true; - public bool IsEnvironmentUser { get; } = false; - public string ServerName { get; } = nameof(ConfigureSentrySerilogOptions_WithAllParameters_MakesAppropriateChangesToObject); - public bool AttachStackTrace { get; } = true; - public int MaxBreadcrumbs { get; } = 9; - public float SampleRate { get; } = 0.4f; - public string Release { get; } = nameof(ConfigureSentrySerilogOptions_WithAllParameters_MakesAppropriateChangesToObject); - public string Environment { get; } = nameof(ConfigureSentrySerilogOptions_WithAllParameters_MakesAppropriateChangesToObject); - public string Dsn { get; } = ValidDsn; - public int MaxQueueItems { get; } = 17; - public TimeSpan ShutdownTimeout { get; } = TimeSpan.FromDays(1.3); - public DecompressionMethods DecompressionMethods { get; } = DecompressionMethods.Deflate & DecompressionMethods.GZip; - public CompressionLevel RequestBodyCompressionLevel { get; } = CompressionLevel.NoCompression; - public bool RequestBodyCompressionBuffered { get; } = false; - public bool Debug { get; } = true; - public SentryLevel DiagnosticLevel { get; } = SentryLevel.Warning; - public ReportAssembliesMode ReportAssembliesMode { get; } = ReportAssembliesMode.None; - public DeduplicateMode DeduplicateMode { get; } = DeduplicateMode.SameExceptionInstance; - public bool InitializeSdk { get; } = false; + // Parameter values that are NOT set to the default values in SentrySerilogOptions public LogEventLevel MinimumEventLevel { get; } = LogEventLevel.Verbose; public LogEventLevel MinimumBreadcrumbLevel { get; } = LogEventLevel.Fatal; + public IFormatProvider FormatProvider { get; } = CultureInfo.InvariantCulture; + public ITextFormatter TextFormatter { get; } = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); public LogEventLevel RestrictedToMinimumLevel { get; } = LogEventLevel.Warning; public LoggingLevelSwitch LevelSwitch { get; } = new(LogEventLevel.Error); @@ -37,38 +20,13 @@ private class Fixture private readonly Fixture _fixture = new(); [Fact] - public void ConfigureSentrySerilogOptions_WithDsn_InitializeSdk() - { - var sut = Fixture.GetSut(); - - // Make the call with only the required parameter - SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, _fixture.Dsn); - - // Compare. I'm not sure how to deep compare--I don't see a nuget ref to that type - // of functionality and I'm hesitant to introduce new technologies with such a - // small commit. - _fixture.Options.Dsn = _fixture.Dsn; - AssertEqualDeep(_fixture.Options, sut); - Assert.True(sut.InitializeSdk); - } - - [Fact] - public void ConfigureSentrySerilogOptions_NoDsn_DontInitializeSdk() + public void ConfigureSentrySerilogOptions_NoParameters_LeavesDefaults() { var sut = Fixture.GetSut(); - // Make the call with only the required parameter - SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, null, minimumEventLevel: _fixture.MinimumEventLevel, - minimumBreadcrumbLevel: _fixture.MinimumBreadcrumbLevel); + SentrySinkExtensions.ConfigureSentrySerilogOptions(sut); - // Compare. I'm not sure how to deep compare--I don't see a nuget ref to that type - // of functionality and I'm hesitant to introduce new technologies with such a - // small commit. - _fixture.Options.InitializeSdk = false; // Since we're not passing in a DSN... would use a different overload otherwise - _fixture.Options.MinimumEventLevel = _fixture.MinimumEventLevel; - _fixture.Options.MinimumBreadcrumbLevel = _fixture.MinimumBreadcrumbLevel; - AssertEqualDeep(_fixture.Options, sut); - Assert.False(sut.InitializeSdk); + AssertEqualDeep(new SentrySerilogOptions(), sut); } [Fact] @@ -76,16 +34,15 @@ public void ConfigureSentrySerilogOptions_WithMultipleParameters_MakesAppropriat { var sut = Fixture.GetSut(); - SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, _fixture.Dsn, sendDefaultPii: _fixture.SendDefaultPii, - decompressionMethods: _fixture.DecompressionMethods, reportAssembliesMode: _fixture.ReportAssembliesMode, sampleRate: _fixture.SampleRate); + SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, minimumEventLevel: _fixture.MinimumEventLevel, + minimumBreadcrumbLevel: _fixture.MinimumBreadcrumbLevel); - // Assert - _fixture.Options.Dsn = _fixture.Dsn; - _fixture.Options.SendDefaultPii = _fixture.SendDefaultPii; - _fixture.Options.DecompressionMethods = _fixture.DecompressionMethods; - _fixture.Options.ReportAssembliesMode = _fixture.ReportAssembliesMode; - _fixture.Options.SampleRate = _fixture.SampleRate; - AssertEqualDeep(_fixture.Options, sut); + var expected = new SentrySerilogOptions + { + MinimumEventLevel = _fixture.MinimumEventLevel, + MinimumBreadcrumbLevel = _fixture.MinimumBreadcrumbLevel + }; + AssertEqualDeep(expected, sut); } [Fact] @@ -93,37 +50,14 @@ public void ConfigureSentrySerilogOptions_WithAllParameters_MakesAppropriateChan { var sut = Fixture.GetSut(); - SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, _fixture.Dsn, _fixture.MinimumEventLevel, - _fixture.MinimumBreadcrumbLevel, null, null, _fixture.SendDefaultPii, - _fixture.IsEnvironmentUser, _fixture.ServerName, _fixture.AttachStackTrace, _fixture.MaxBreadcrumbs, - _fixture.SampleRate, _fixture.Release, _fixture.Environment, _fixture.MaxQueueItems, - _fixture.ShutdownTimeout, _fixture.DecompressionMethods, _fixture.RequestBodyCompressionLevel, - _fixture.RequestBodyCompressionBuffered, _fixture.Debug, _fixture.DiagnosticLevel, - _fixture.ReportAssembliesMode, _fixture.DeduplicateMode, null, null, + SentrySinkExtensions.ConfigureSentrySerilogOptions(sut, _fixture.MinimumEventLevel, + _fixture.MinimumBreadcrumbLevel, _fixture.FormatProvider, _fixture.TextFormatter, _fixture.RestrictedToMinimumLevel, _fixture.LevelSwitch); - // Compare individual properties - Assert.Equal(_fixture.SendDefaultPii, sut.SendDefaultPii); - Assert.Equal(_fixture.IsEnvironmentUser, sut.IsEnvironmentUser); - Assert.Equal(_fixture.ServerName, sut.ServerName); - Assert.Equal(_fixture.AttachStackTrace, sut.AttachStacktrace); - Assert.Equal(_fixture.MaxBreadcrumbs, sut.MaxBreadcrumbs); - Assert.Equal(_fixture.SampleRate, sut.SampleRate); - Assert.Equal(_fixture.Release, sut.Release); - Assert.Equal(_fixture.Environment, sut.Environment); - Assert.Equal(_fixture.Dsn, sut.Dsn); - Assert.Equal(_fixture.MaxQueueItems, sut.MaxQueueItems); - Assert.Equal(_fixture.ShutdownTimeout, sut.ShutdownTimeout); - Assert.Equal(_fixture.DecompressionMethods, sut.DecompressionMethods); - Assert.Equal(_fixture.RequestBodyCompressionLevel, sut.RequestBodyCompressionLevel); - Assert.Equal(_fixture.RequestBodyCompressionBuffered, sut.RequestBodyCompressionBuffered); - Assert.Equal(_fixture.Debug, sut.Debug); - Assert.Equal(_fixture.DiagnosticLevel, sut.DiagnosticLevel); - Assert.Equal(_fixture.ReportAssembliesMode, sut.ReportAssembliesMode); - Assert.Equal(_fixture.DeduplicateMode, sut.DeduplicateMode); - Assert.True(sut.InitializeSdk); Assert.Equal(_fixture.MinimumEventLevel, sut.MinimumEventLevel); Assert.Equal(_fixture.MinimumBreadcrumbLevel, sut.MinimumBreadcrumbLevel); + Assert.Same(_fixture.FormatProvider, sut.FormatProvider); + Assert.Same(_fixture.TextFormatter, sut.TextFormatter); Assert.Equal(_fixture.RestrictedToMinimumLevel, sut.RestrictedToMinimumLevel); Assert.Same(_fixture.LevelSwitch, sut.LevelSwitch); } @@ -136,12 +70,11 @@ public void Sentry_WithRestrictedToMinimumLevel_ConfigureOptions_FiltersLogsBelo hub.IsEnabled.Returns(true); var options = new SentrySerilogOptions { - InitializeSdk = false, MinimumBreadcrumbLevel = LogEventLevel.Verbose, MinimumEventLevel = LogEventLevel.Verbose, RestrictedToMinimumLevel = LogEventLevel.Error, }; - var sink = new SentrySink(options, () => hub, null, new MockClock()); + var sink = new SentrySink(options, () => hub, new MockClock()); using var logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.Sink(sink, options.RestrictedToMinimumLevel, options.LevelSwitch) @@ -158,9 +91,8 @@ public void Sentry_WithRestrictedToMinimumLevel_ConfigureOptions_FiltersLogsBelo } [Fact] - public void Sentry_WithRestrictedToMinimumLevel_NoDsn_ParameterIsAccepted() + public void Sentry_WithRestrictedToMinimumLevel_ParameterIsAccepted() { - // Verify the no-DSN overload accepts restrictedToMinimumLevel without throwing var ex = Record.Exception(() => new LoggerConfiguration() .WriteTo.Sentry( diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs index 5a0d40fe58..b5011db15f 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs @@ -21,7 +21,7 @@ public void Emit_StructuredLogging_CapturesLog() [Theory] [InlineData(false)] [InlineData(true)] - public void Emit_StructuredLogging_UseHubOptionsOverSinkOptions(bool isEnabled) + public void Emit_StructuredLogging_RequiresHubOptions(bool isEnabled) { InMemorySentryStructuredLogger capturer = new(); _fixture.Hub.Logger.Returns(capturer); @@ -66,8 +66,8 @@ public void Emit_StructuredLogging_LogEvent(bool withActiveSpan) { InMemorySentryStructuredLogger capturer = new(); _fixture.Hub.Logger.Returns(capturer); - _fixture.Options.Environment = "test-environment"; - _fixture.Options.Release = "test-release"; + _fixture.SentryOptions.Environment = "test-environment"; + _fixture.SentryOptions.Release = "test-release"; if (withActiveSpan) { diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.cs index 274b715092..85c52b7b77 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.cs @@ -5,25 +5,30 @@ public partial class SentrySinkTests private class Fixture { public SentrySerilogOptions Options { get; set; } = new(); + public InMemoryDiagnosticLogger DiagnosticLogger { get; } = new(); + public SentryOptions SentryOptions { get; } public IHub Hub { get; set; } = Substitute.For(); public Func HubAccessor { get; set; } - public IDisposable SdkDisposeHandle { get; set; } = Substitute.For(); public Scope Scope { get; } = new(new SentryOptions()); public Fixture() { + SentryOptions = new SentryOptions + { + Debug = true, + DiagnosticLogger = DiagnosticLogger + }; Hub.IsEnabled.Returns(true); Hub.Logger.Returns(new InMemorySentryStructuredLogger()); HubAccessor = () => Hub; Hub.SubstituteConfigureScope(Scope); - SentryClientExtensions.SentryOptionsForTestingOnly = Options; + SentryClientExtensions.SentryOptionsForTestingOnly = SentryOptions; } public SentrySink GetSut() => new( Options, HubAccessor, - SdkDisposeHandle, new MockClock()); } @@ -218,27 +223,46 @@ public void Emit_Properties_AsExtra() } [Fact] - public void Close_DisposesSdk() + public void Emit_UseSerilogNotCalled_LogsWarningOnce() { var sut = _fixture.GetSut(); var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); sut.Emit(evt); + sut.Emit(evt); - _fixture.SdkDisposeHandle.DidNotReceive().Dispose(); + _fixture.DiagnosticLogger.Entries + .Where(e => e.Level == SentryLevel.Warning && e.Message.Contains("UseSerilog()")) + .Should().ContainSingle(); + } - sut.Dispose(); + [Fact] + public void Emit_UseSerilogCalled_NoWarning() + { + _fixture.SentryOptions.UseSerilog(); + var sut = _fixture.GetSut(); - _fixture.SdkDisposeHandle.Received(1).Dispose(); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); + sut.Emit(evt); + + _fixture.DiagnosticLogger.Entries + .Should().NotContain(e => e.Message.Contains("UseSerilog()")); } [Fact] - public void Close_NoDisposeHandleProvided_DoesNotThrow() + public void Emit_DisabledHub_NoWarning() { - _fixture.SdkDisposeHandle = null; + _fixture.Hub.IsEnabled.Returns(false); var sut = _fixture.GetSut(); - sut.Dispose(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); + sut.Emit(evt); + + _fixture.DiagnosticLogger.Entries + .Should().NotContain(e => e.Message.Contains("UseSerilog()")); } [Fact] diff --git a/test/Sentry.Serilog.Tests/SerilogAspNetSentrySdkTestFixture.cs b/test/Sentry.Serilog.Tests/SerilogAspNetSentrySdkTestFixture.cs index ee332aa46b..6be339c065 100644 --- a/test/Sentry.Serilog.Tests/SerilogAspNetSentrySdkTestFixture.cs +++ b/test/Sentry.Serilog.Tests/SerilogAspNetSentrySdkTestFixture.cs @@ -32,7 +32,7 @@ protected override void ConfigureBuilder(WebHostBuilder builder) builder.ConfigureLogging(loggingBuilder => { var logger = new LoggerConfiguration() - .WriteTo.Sentry(ValidDsn) + .WriteTo.Sentry() .CreateLogger(); loggingBuilder.AddSerilog(logger); }); From 5385388faab46350d3c2ce5f502ae34b3fb7b3cf Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 14 Sep 2026 01:24:47 +0000 Subject: [PATCH 02/42] Accept API verifier changes --- .../ApiApprovalTests.Run.DotNet11_0.verified.txt | 2 ++ test/Sentry.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 9e7456b450..8934fb57a7 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -59,6 +59,8 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } public NLog.Layouts.Layout? Dsn { get; set; } + [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 5f150f965e..803c8f81b3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -837,6 +837,8 @@ namespace Sentry public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableBackpressureHandling { get; set; } + [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } [System.Obsolete("Metrics are always enabled. This option is ignored and will be removed in version" + " 7.0.0. To drop metrics, use SetBeforeSendMetric and return null.")] From c72c26e6db21344fd3731a81da163ac9271622a7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 17 Sep 2026 12:42:43 +1200 Subject: [PATCH 03/42] Tweak comments in the samples --- samples/Sentry.Samples.AspNetCore.Serilog/Program.cs | 9 ++++----- samples/Sentry.Samples.Serilog/Program.cs | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs b/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs index 9fe8854732..ff974a903f 100644 --- a/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs +++ b/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs @@ -15,8 +15,7 @@ public static WebApplication BuildWebApp(string[] args) c.Enrich.FromLogContext() .MinimumLevel.Debug() .WriteTo.Console() - // Add Sentry integration with Serilog. This only configures the sink - Sentry itself is - // initialised by UseSentry below. + // Configure Serilog to send logs to Sentry. This only configures the sink - Sentry is initialised below. .WriteTo.Sentry(s => { // Sets the minimum log level required to add a log message as breadcrumb @@ -25,9 +24,9 @@ public static WebApplication BuildWebApp(string[] args) s.MinimumEventLevel = LogEventLevel.Error; })); - // Add Sentry integration - // It can be defined via configuration (including `appsettings.json`) - // or coded explicitly, in the options callback: o.Dsn = ""; o.Release = "1.0"; ... + // Add the Sentry integration. + // Most options can be defined via binding configuration (including `appsettings.json` as we do here) + // or coded explicitly, in the options callback below (as we do with the DSN and Serilog log context) builder.WebHost.UseSentry(o => { #if !SENTRY_DSN_DEFINED_IN_ENV diff --git a/samples/Sentry.Samples.Serilog/Program.cs b/samples/Sentry.Samples.Serilog/Program.cs index 26d91f9676..b4c0075c56 100644 --- a/samples/Sentry.Samples.Serilog/Program.cs +++ b/samples/Sentry.Samples.Serilog/Program.cs @@ -8,7 +8,7 @@ internal static class Program { private static void Main() { - // Initialise Sentry. The Serilog sink below doesn't do this for you. + // Initialise Sentry SDK itself using var _ = SentrySdk.Init(options => { #if !SENTRY_DSN_DEFINED_IN_ENV @@ -28,12 +28,12 @@ private static void Main() .Enrich.FromLogContext() .MinimumLevel.Debug() .WriteTo.Console() - // Other overloads exist, for example, configuring the sink with no parameters at all. + // Configure Serilog to send logs to Sentry .WriteTo.Sentry(options => { // Debug and higher are stored as breadcrumbs (default os Information) options.MinimumBreadcrumbLevel = LogEventLevel.Debug; - // Error and higher is sent as event (default is Error) + // Error and higher are sent as events (default is Error) options.MinimumEventLevel = LogEventLevel.Error; // Optional Serilog text formatter used to format LogEvent to string. If TextFormatter is set, FormatProvider is ignored. options.TextFormatter = new MessageTemplateTextFormatter("[{MyTaskId}] {Message}"); From cafeb7b674046beb10cee2fc1fe5e02758590d4d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 17 Sep 2026 13:00:55 +1200 Subject: [PATCH 04/42] feat: NLog target no longer initializes the SDK The Sentry target for NLog now only configures the target. Sentry must be initialized separately (SentrySdk.Init, UseSentry, etc). - SentryNLogOptions no longer derives from SentryOptions and only carries target settings; FlushTimeout moves onto it directly - Remove InitializeSdk, Dsn/DsnLayout, Release/ReleaseLayout, Environment/EnvironmentLayout and ShutdownTimeoutSeconds. Events take release and environment from the SDK options - Collapse the AddSentry overloads into AddSentry(optionsConfig, targetName); the dsn overloads are removed - The target no longer routes SDK diagnostics to NLog's InternalLogger Part of #5245 Co-Authored-By: Claude Opus 5 --- samples/Sentry.Samples.NLog/NLog.config | 12 +- samples/Sentry.Samples.NLog/Program.cs | 28 ++-- samples/Sentry.Samples.NLog/README.md | 22 +-- src/Sentry.NLog/ConfigurationExtensions.cs | 58 +------- src/Sentry.NLog/NLogDiagnosticLogger.cs | 58 -------- src/Sentry.NLog/SentryNLogOptions.cs | 52 +------ src/Sentry.NLog/SentryTarget.cs | 112 +------------- ...iApprovalTests.Run.DotNet10_0.verified.txt | 21 +-- ...iApprovalTests.Run.DotNet11_0.verified.txt | 21 +-- ...piApprovalTests.Run.DotNet8_0.verified.txt | 21 +-- ...piApprovalTests.Run.DotNet9_0.verified.txt | 21 +-- .../ApiApprovalTests.Run.Net4_8.verified.txt | 21 +-- .../ConfigurationExtensionsTest.cs | 10 +- ...rationTests.Simple.DotNet10_0.verified.txt | 4 +- ...grationTests.Simple.DotNet8_0.verified.txt | 4 +- ...grationTests.Simple.DotNet9_0.verified.txt | 4 +- ...ntegrationTests.Simple.Net4_8.verified.txt | 4 +- .../IntegrationTests.verify.cs | 126 ++++++++-------- .../NLogDiagnosticLoggerTest.cs | 29 ---- .../SentryTargetTests.Structured.cs | 6 +- test/Sentry.NLog.Tests/SentryTargetTests.cs | 137 ++---------------- 21 files changed, 141 insertions(+), 630 deletions(-) delete mode 100644 src/Sentry.NLog/NLogDiagnosticLogger.cs delete mode 100644 test/Sentry.NLog.Tests/NLogDiagnosticLoggerTest.cs diff --git a/samples/Sentry.Samples.NLog/NLog.config b/samples/Sentry.Samples.NLog/NLog.config index 5022aa23bb..9e512f40e1 100644 --- a/samples/Sentry.Samples.NLog/NLog.config +++ b/samples/Sentry.Samples.NLog/NLog.config @@ -10,11 +10,8 @@ - - + - - - diff --git a/samples/Sentry.Samples.NLog/Program.cs b/samples/Sentry.Samples.NLog/Program.cs index 93d1a56397..fdfd335cb6 100644 --- a/samples/Sentry.Samples.NLog/Program.cs +++ b/samples/Sentry.Samples.NLog/Program.cs @@ -12,6 +12,18 @@ public static class Program private static void Main() { + // Initialise Sentry SDK itself + using var _ = SentrySdk.Init(options => + { +#if !SENTRY_DSN_DEFINED_IN_ENV + // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = SamplesShared.Dsn; +#endif + options.AttachStacktrace = true; + options.SendDefaultPii = true; // Send Personal Identifiable information like the username of the user logged in to the device + }); + try { // You can configure your logger using a configuration file: @@ -97,28 +109,18 @@ private static void UsingNLogConfigFile() private static void UsingCodeConfiguration() { - // Other overloads exist, for example, configure the SDK with only the DSN or no parameters at all. + // Configure NLog to send logs to Sentry var config = LogManager.Configuration = new LoggingConfiguration(); _ = config .AddSentry(options => { -#if !SENTRY_DSN_DEFINED_IN_ENV - // A DSN is required. You can set here in code, in the SENTRY_DSN environment variable or in the - // NLog.config file. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - options.Dsn = SamplesShared.Dsn; -#endif options.Layout = "${message}"; options.BreadcrumbLayout = "${logger}: ${message}"; // Optionally specify a separate format for breadcrumbs options.MinimumBreadcrumbLevel = LogLevel.Debug; // Debug and higher are stored as breadcrumbs (default is Info) - options.MinimumEventLevel = LogLevel.Error; // Error and higher is sent as event (default is Error) - - options.AttachStacktrace = true; - options.SendDefaultPii = true; // Send Personal Identifiable information like the username of the user logged in to the device + options.MinimumEventLevel = LogLevel.Error; // Error and higher are sent as events (default is Error) options.IncludeEventDataOnBreadcrumbs = true; // Optionally include event properties with breadcrumbs - options.ShutdownTimeoutSeconds = 5; //Optionally specify user properties via NLog (here using MappedDiagnosticsLogicalContext as an example) options.User = new SentryNLogUser @@ -134,8 +136,6 @@ private static void UsingCodeConfiguration() }; options.AddTag("logger", "${logger}"); // Send the logger name as a tag - - // Other configuration }); config.AddTarget(new DebuggerTarget("debugger")); diff --git a/samples/Sentry.Samples.NLog/README.md b/samples/Sentry.Samples.NLog/README.md index 6cf4e027d2..a66736ebfb 100644 --- a/samples/Sentry.Samples.NLog/README.md +++ b/samples/Sentry.Samples.NLog/README.md @@ -3,19 +3,16 @@ This is a simple console application that demonstrates how you can add Sentry to your application using NLog. This project attempts to sample the integration by using code only and also via the configuration file. -In both cases **you need to add your own DSN** so you can see the events sent in your Sentry project. +In both cases the Sentry target only sends logs to Sentry. The Sentry SDK itself is initialised separately, with +`SentrySdk.Init` in `Program.cs`, so **you need to add your own DSN** there to see the events sent in your Sentry project. You can get your [Sentry DSN at sentry.io](https://sentry.io). -Make sure to add it to both `NLog.config` and `Program.cs` in this directory. ## Configuration of NLog.config The following options are available for the NLog Sentry Target: ```xml - - ``` -* **dsn** - Sentry Data Source Name Address. See also https://sentry.io -* **initializeSdk** - Whether the NLog target should initialize the Sentry SDK (Using Dsn). Default: _True_ -* **environment** - Application Environment sent to Sentry -* **release** - Application Release Version sent to Sentry * **layout** - NLog Layout for rendering SentryEvent message. Default: _${message}_ * **includeEventProperties** - Include LogEvent properties as Data on SentryEvent. Default: _True_ * **includeEventPropertiesAsTags** - Include LogEvent properties as extra Tags on SentryEvent. Default: _False_ @@ -68,10 +54,6 @@ The following options are available for the NLog Sentry Target: * **email** * **ipAddress** * **other** - Any arbitrary key-value pairs to be included as properties for a user on every event. -* **options** - * **sendDefaultPii** - Whether to include default Personal Identifiable information (UserName / IP-Address). Default: _False_ - * **isEnvironmentUser** - Lookup Environment.User if having enabled **sendDefaultPii**. Default: _True_ - * **attachStacktrace** - Whether to send the stack trace of a event captured without an exception. Default: _False_ There is filtering logic in the Sentry Target that is usually handled by NLog Logging Rules and Filters. Mostly because the same Sentry Target is writing both breadcrumbs and actual SentryEvents. diff --git a/src/Sentry.NLog/ConfigurationExtensions.cs b/src/Sentry.NLog/ConfigurationExtensions.cs index 596fb9bca0..7ac7c0f880 100644 --- a/src/Sentry.NLog/ConfigurationExtensions.cs +++ b/src/Sentry.NLog/ConfigurationExtensions.cs @@ -16,62 +16,17 @@ public static class ConfigurationExtensions /// Adds a target for Sentry to the NLog configuration. /// /// - /// If DSN is not set, the SDK will look for an environment variable called SENTRY_DSN. If nothing is - /// found, SDK is disabled. + /// This doesn't initialise Sentry. Initialise Sentry separately, using SentrySdk.Init or another Sentry + /// integration (such as ASP.NET Core or MAUI). /// /// The NLog configuration. /// An optional action for configuring the Sentry target options. - /// The configuration. - public static LoggingConfiguration AddSentry( - this LoggingConfiguration configuration, - Action? optionsConfig = null) - { - // Not to throw on code that ignores nullability warnings. - if (configuration.IsNull()) - { - return configuration!; - } - - return configuration.AddSentry(null, DefaultTargetName, optionsConfig); - } - - /// - /// Adds a target for Sentry to the NLog configuration. - /// - /// The NLog configuration. - /// - /// The sentry DSN. If DSN is not set, the SDK will look for an environment variable called SENTRY_DSN. - /// If nothing is found, SDK is disabled. - /// - /// An optional action for configuring the Sentry target options. - /// The configuration. - public static LoggingConfiguration AddSentry( - this LoggingConfiguration configuration, - string? dsn, - Action? optionsConfig = null) - { - // Not to throw on code that ignores nullability warnings. - if (configuration.IsNull()) - { - return configuration!; - } - - return configuration.AddSentry(dsn, DefaultTargetName, optionsConfig); - } - - /// - /// Adds a target for Sentry to the NLog configuration. - /// - /// The NLog configuration. - /// The sentry DSN. /// The name to give the new target. - /// An optional action for configuring the Sentry target options. /// The configuration. public static LoggingConfiguration AddSentry( this LoggingConfiguration configuration, - string? dsn, - string targetName, - Action? optionsConfig = null) + Action? optionsConfig = null, + string targetName = DefaultTargetName) { // Not to throw on code that ignores nullability warnings. if (configuration.IsNull()) @@ -95,11 +50,6 @@ public static LoggingConfiguration AddSentry( Layout = "${message}", }; - if (dsn != null && string.IsNullOrWhiteSpace(options.Dsn)) - { - options.Dsn = dsn; - } - configuration.AddTarget(targetName, target); configuration.AddRuleForAllLevels(targetName); diff --git a/src/Sentry.NLog/NLogDiagnosticLogger.cs b/src/Sentry.NLog/NLogDiagnosticLogger.cs deleted file mode 100644 index 493b8ff6a7..0000000000 --- a/src/Sentry.NLog/NLogDiagnosticLogger.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Sentry.NLog; - -internal class NLogDiagnosticLogger : IDiagnosticLogger -{ - private readonly IDiagnosticLogger? _extraLogger; - - public NLogDiagnosticLogger(IDiagnosticLogger? extraLogger = null) - { - if (!InternalLogger.LogToConsole || extraLogger is not ConsoleDiagnosticLogger) - { - _extraLogger = extraLogger; - } - } - - public bool IsEnabled(SentryLevel level) - { - if (_extraLogger?.IsEnabled(level) == true) - { - return true; - } - - return level switch - { - SentryLevel.Fatal => InternalLogger.IsFatalEnabled, - SentryLevel.Error => InternalLogger.IsErrorEnabled, - SentryLevel.Warning => InternalLogger.IsWarnEnabled, - SentryLevel.Info => InternalLogger.IsInfoEnabled, - _ => InternalLogger.IsDebugEnabled - }; - } - - public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args) - { - switch (logLevel) - { - case SentryLevel.Fatal: - InternalLogger.Fatal(exception, message, args); - break; - case SentryLevel.Error: - InternalLogger.Error(exception, message, args); - break; - case SentryLevel.Warning: - InternalLogger.Warn(exception, message, args); - break; - case SentryLevel.Info: - InternalLogger.Info(exception, message, args); - break; - default: - InternalLogger.Debug(exception, message, args); - break; - } - - if (_extraLogger?.IsEnabled(logLevel) == true) - { - _extraLogger.Log(logLevel, message, exception, args); - } - } -} diff --git a/src/Sentry.NLog/SentryNLogOptions.cs b/src/Sentry.NLog/SentryNLogOptions.cs index e273fb3ddf..dd3008349a 100644 --- a/src/Sentry.NLog/SentryNLogOptions.cs +++ b/src/Sentry.NLog/SentryNLogOptions.cs @@ -1,28 +1,19 @@ namespace Sentry.NLog; /// -/// Sentry Options for NLog logging. All properties can be configured via code or in NLog.config xml file. +/// Options for the Sentry target for NLog. All properties can be configured via code or in NLog.config xml file. /// -/// +/// +/// These options only configure the target. The Sentry SDK itself is configured and initialised separately, using +/// SentrySdk.Init or another Sentry integration (such as ASP.NET Core or MAUI). +/// [NLogConfigurationItem] -public class SentryNLogOptions : SentryOptions +public class SentryNLogOptions { - /// - public SentryNLogOptions() - { - // NLog's default flush timeout is 15 seconds, so we will do the same. - FlushTimeout = TimeSpan.FromSeconds(15); - } - /// - /// How many seconds to wait after triggering before just shutting down the - /// Sentry sdk. + /// How long to wait for Sentry to flush when NLog is flushed. Defaults to 15 seconds, the same as NLog. /// - public int ShutdownTimeoutSeconds - { - get => (int)ShutdownTimeout.TotalSeconds; - set => ShutdownTimeout = TimeSpan.FromSeconds(value); - } + public TimeSpan FlushTimeout { get; set; } = TimeSpan.FromSeconds(15); /// /// Minimum log level for events to trigger a send to Sentry. Defaults to . @@ -68,39 +59,12 @@ public int ShutdownTimeoutSeconds [NLogConfigurationIgnoreProperty] // Configure this directly on the target in XML config. public Layout? Layout { get; set; } - /// - /// Configured layout for Dsn-Address to Sentry - /// - [NLogConfigurationIgnoreProperty] // Configure this directly on the target in XML config. - public Layout? DsnLayout { get; set; } - - /// - /// Configured layout for application Release version to Sentry - /// - [NLogConfigurationIgnoreProperty] // Configure this directly on the target in XML config. - public Layout? ReleaseLayout { get; set; } - - /// - /// Configured layout for application Environment to Sentry - /// - [NLogConfigurationIgnoreProperty] // Configure this directly on the target in XML config. - public Layout? EnvironmentLayout { get; set; } - /// /// Any additional tags to apply to each logged message. /// [NLogConfigurationIgnoreProperty] // Configure this directly on the target in XML config. public IList Tags { get; } = new List(); - /// - /// Whether the NLog integration should initialize the SDK. - /// - /// - /// By default, if a DSN is provided to the NLog integration it will initialize the SDK. - /// This might be not ideal when using multiple integrations in case you want another one doing the Init. - /// - public bool InitializeSdk { get; set; } = true; - /// /// Optionally configure one or more parts of the user information to be rendered dynamically from an NLog layout /// diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index f49827f588..68a90f0ca7 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -10,7 +10,6 @@ public sealed partial class SentryTarget : TargetWithContext internal Func HubAccessor { get; } private readonly ISystemClock _clock; - private IDisposable? _sdkDisposable; internal static readonly SdkVersion NameAndVersion = typeof(SentryTarget).Assembly.GetNameAndVersion(); @@ -38,12 +37,11 @@ public SentryTarget(SentryNLogOptions options) : this( options, () => HubAdapter.Instance, - null, SystemClock.Clock) { } - internal SentryTarget(SentryNLogOptions options, Func hubAccessor, IDisposable? sdkInstance, ISystemClock clock) + internal SentryTarget(SentryNLogOptions options, Func hubAccessor, ISystemClock clock) { Options = options; HubAccessor = hubAccessor; @@ -54,15 +52,10 @@ internal SentryTarget(SentryNLogOptions options, Func hubAccessor, IDispos Layout = "${message}"; BreadcrumbCategory = Options.BreadcrumbCategoryLayout ?? "${logger}"; IncludeEventProperties = true; - - if (sdkInstance != null) - { - _sdkDisposable = sdkInstance; - } } /// - /// Options for both the and the sentry sdk itself. + /// Options for the . /// public SentryNLogOptions Options { get; } @@ -72,33 +65,6 @@ internal SentryTarget(SentryNLogOptions options, Func hubAccessor, IDispos [ArrayParameter(typeof(TargetPropertyWithContext), "tag")] public IList Tags => Options.Tags; - /// - /// Configured layout for Data Source Name of a given project in Sentry - /// - public Layout? Dsn - { - get => Options.DsnLayout; - set => Options.DsnLayout = value; - } - - /// - /// Configured layout for application Release version to Sentry - /// - public Layout? Release - { - get => Options.ReleaseLayout; - set => Options.ReleaseLayout = value; - } - - /// - /// Configured layout for application Environment to Sentry - /// - public Layout? Environment - { - get => Options.EnvironmentLayout; - set => Options.EnvironmentLayout = value; - } - /// /// An optional layout specific to breadcrumbs. If not set, uses the same layout as the standard . /// @@ -152,19 +118,6 @@ public bool EnableLogs } } - /// - /// Whether the NLog integration should initialize the SDK. - /// - /// - /// By default, if a DSN is provided to the NLog integration it will initialize the SDK. - /// This might be not ideal when using multiple integrations in case you want another one doing the Init. - /// - public bool InitializeSdk - { - get => Options.InitializeSdk; - set => Options.InitializeSdk = value; - } - /// /// Set this to to ignore log messages that don't contain an exception. /// @@ -195,17 +148,7 @@ public bool IncludeEventDataOnBreadcrumbs } /// - /// How many seconds to wait after triggering before just shutting down the - /// Sentry sdk. - /// - public int ShutdownTimeoutSeconds - { - get => Options.ShutdownTimeoutSeconds; - set => Options.ShutdownTimeoutSeconds = value; - } - - /// - /// How long to wait for the flush to finish, in seconds. Defaults to 2 seconds. + /// How long to wait for the flush to finish, in seconds. Defaults to 15 seconds. /// public int FlushTimeoutSeconds { @@ -222,13 +165,6 @@ public SentryNLogUser? User set => Options.User = value; } - /// - protected override void CloseTarget() - { - _sdkDisposable?.Dispose(); - base.CloseTarget(); - } - /// protected override void InitializeTarget() { @@ -240,40 +176,6 @@ protected override void InitializeTarget() base.InitializeTarget(); - if (InternalLogger.IsDebugEnabled || InternalLogger.IsInfoEnabled || InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled || InternalLogger.IsFatalEnabled) - { - var existingLogger = Options.DiagnosticLogger; - if (existingLogger is not NLogDiagnosticLogger) - { - Options.DiagnosticLogger = new NLogDiagnosticLogger(existingLogger); - } - Options.Debug = true; - } - - var customDsn = Dsn?.Render(LogEventInfo.CreateNullEvent()); - if (!string.IsNullOrEmpty(customDsn)) - { - Options.Dsn = customDsn; - } - - var customRelease = Release?.Render(LogEventInfo.CreateNullEvent()); - if (!string.IsNullOrEmpty(customRelease)) - { - Options.Release = customRelease; - } - - var customEnvironment = Environment?.Render(LogEventInfo.CreateNullEvent()); - if (!string.IsNullOrEmpty(customEnvironment)) - { - Options.Environment = customEnvironment; - } - - // If the sdk is not there, set it on up. - if (InitializeSdk && _sdkDisposable == null) - { - _sdkDisposable = SentrySdk.Init(Options); - } - if (!HubAccessor().IsEnabled) { InternalLogger.Info("Sentry(Name={0}): Hub not enabled", Name); @@ -315,7 +217,7 @@ protected override void Write(LogEventInfo logEvent) 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.Message}"); + HubAccessor()?.GetSentryOptions()?.DiagnosticLogger?.LogError($"Reentrant log event detected. Logging when inside the scope of another log event can cause a StackOverflowException. LogEventInfo.Message:{logEvent.Message}"); return; } @@ -327,7 +229,7 @@ protected override void Write(LogEventInfo logEvent) } catch (Exception exception) { - Options.DiagnosticLogger?.LogError(exception, "Failed to write log event"); + HubAccessor()?.GetSentryOptions()?.DiagnosticLogger?.LogError(exception, "Failed to write log event"); throw; } finally @@ -371,8 +273,6 @@ private void InnerWrite(LogEventInfo logEvent) CreateBreadcrumb(logEvent, exception, shouldIncludeProperties, hub); } - // Read the options from the Hub rather than the Target's NLog-Options. If the NLog-Target is added without a - // DSN (i.e. without initialising the SDK), then base options will only be initialised in the Hub options. var sentryOptions = hub.GetSentryOptions(); if (sentryOptions is not null) { @@ -450,8 +350,6 @@ private void CreateSentryEvent(LogEventInfo logEvent, Exception? exception, bool }, Logger = logEvent.LoggerName, Level = logEvent.Level.ToSentryLevel(), - Release = Options.Release, - Environment = Options.Environment, User = GetUser(logEvent) ?? new SentryUser(), }; diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 8934fb57a7..4e37d9de02 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -3,38 +3,29 @@ namespace NLog { public static class ConfigurationExtensions { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } namespace Sentry.NLog { [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions + public class SentryNLogOptions { public SentryNLogOptions() { } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } + public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? Layout { get; set; } public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } public NLog.LogLevel? MinimumEventLevel { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] public System.Collections.Generic.IList Tags { get; } [NLog.Config.NLogConfigurationIgnoreProperty] public Sentry.NLog.SentryNLogUser? User { get; set; } @@ -58,25 +49,19 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] public System.Collections.Generic.IList Tags { get; } public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } protected override void InitializeTarget() { } protected override void Write(NLog.LogEventInfo logEvent) { } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 8934fb57a7..4e37d9de02 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -3,38 +3,29 @@ namespace NLog { public static class ConfigurationExtensions { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } namespace Sentry.NLog { [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions + public class SentryNLogOptions { public SentryNLogOptions() { } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } + public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? Layout { get; set; } public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } public NLog.LogLevel? MinimumEventLevel { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] public System.Collections.Generic.IList Tags { get; } [NLog.Config.NLogConfigurationIgnoreProperty] public Sentry.NLog.SentryNLogUser? User { get; set; } @@ -58,25 +49,19 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] public System.Collections.Generic.IList Tags { get; } public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } protected override void InitializeTarget() { } protected override void Write(NLog.LogEventInfo logEvent) { } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 8934fb57a7..4e37d9de02 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -3,38 +3,29 @@ namespace NLog { public static class ConfigurationExtensions { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } namespace Sentry.NLog { [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions + public class SentryNLogOptions { public SentryNLogOptions() { } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } + public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? Layout { get; set; } public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } public NLog.LogLevel? MinimumEventLevel { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] public System.Collections.Generic.IList Tags { get; } [NLog.Config.NLogConfigurationIgnoreProperty] public Sentry.NLog.SentryNLogUser? User { get; set; } @@ -58,25 +49,19 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] public System.Collections.Generic.IList Tags { get; } public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } protected override void InitializeTarget() { } protected override void Write(NLog.LogEventInfo logEvent) { } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 8934fb57a7..4e37d9de02 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -3,38 +3,29 @@ namespace NLog { public static class ConfigurationExtensions { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } namespace Sentry.NLog { [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions + public class SentryNLogOptions { public SentryNLogOptions() { } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } + public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? Layout { get; set; } public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } public NLog.LogLevel? MinimumEventLevel { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] public System.Collections.Generic.IList Tags { get; } [NLog.Config.NLogConfigurationIgnoreProperty] public Sentry.NLog.SentryNLogUser? User { get; set; } @@ -58,25 +49,19 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] public System.Collections.Generic.IList Tags { get; } public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } protected override void InitializeTarget() { } protected override void Write(NLog.LogEventInfo logEvent) { } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 8934fb57a7..4e37d9de02 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -3,38 +3,29 @@ namespace NLog { public static class ConfigurationExtensions { - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } namespace Sentry.NLog { [NLog.Config.NLogConfigurationItem] - public class SentryNLogOptions : Sentry.SentryOptions + public class SentryNLogOptions { public SentryNLogOptions() { } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? DsnLayout { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? EnvironmentLayout { get; set; } + public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? Layout { get; set; } public NLog.LogLevel? MinimumBreadcrumbLevel { get; set; } public NLog.LogLevel? MinimumEventLevel { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] - public NLog.Layouts.Layout? ReleaseLayout { get; set; } - public int ShutdownTimeoutSeconds { get; set; } - [NLog.Config.NLogConfigurationIgnoreProperty] public System.Collections.Generic.IList Tags { get; } [NLog.Config.NLogConfigurationIgnoreProperty] public Sentry.NLog.SentryNLogUser? User { get; set; } @@ -58,25 +49,19 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public NLog.Layouts.Layout? Environment { get; set; } public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } - public NLog.Layouts.Layout? Release { get; set; } - public int ShutdownTimeoutSeconds { get; set; } [NLog.Config.ArrayParameter(typeof(NLog.Targets.TargetPropertyWithContext), "tag")] public System.Collections.Generic.IList Tags { get; } public Sentry.NLog.SentryNLogUser? User { get; set; } - protected override void CloseTarget() { } protected override void FlushAsync(NLog.Common.AsyncContinuation asyncContinuation) { } protected override void InitializeTarget() { } protected override void Write(NLog.LogEventInfo logEvent) { } diff --git a/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs b/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs index f46f27d593..d1f950d284 100644 --- a/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs +++ b/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs @@ -19,14 +19,10 @@ public void AddSentry_ConfigCallback_CallbackInvoked() } [Fact] - public void AddSentry_DsnAndConfigCallback_CallbackInvokedAndDsnUsed() + public void AddSentry_TargetName_TargetNamed() { - var expectedTimeout = TimeSpan.FromDays(1); - var expectedDsn = "https://a@sentry.io/1"; - var actual = new LoggingConfiguration().AddSentry(expectedDsn, o => o.FlushTimeout = expectedTimeout); - var sentryTarget = Assert.IsType(actual.AllTargets[0]); - Assert.Equal(expectedTimeout.TotalSeconds, sentryTarget.FlushTimeoutSeconds); - Assert.Equal(expectedDsn, sentryTarget.Options.Dsn); + var actual = new LoggingConfiguration().AddSentry(targetName: "custom"); + Assert.Equal("custom", actual.AllTargets[0].Name); } [Fact] diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt index 34197eb0e0..f489c6a8fc 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet10_0.verified.txt @@ -40,8 +40,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 52, - ColumnNumber: 17, + LineNumber: 57, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt index 34197eb0e0..f489c6a8fc 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet8_0.verified.txt @@ -40,8 +40,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 52, - ColumnNumber: 17, + LineNumber: 57, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt index 34197eb0e0..f489c6a8fc 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.DotNet9_0.verified.txt @@ -40,8 +40,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 52, - ColumnNumber: 17, + LineNumber: 57, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, diff --git a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt index 34197eb0e0..f489c6a8fc 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/IntegrationTests.Simple.Net4_8.verified.txt @@ -40,8 +40,8 @@ FileName: IntegrationTests.verify.cs, Function: Task IntegrationTests.Simple(), Module: null, - LineNumber: 52, - ColumnNumber: 17, + LineNumber: 57, + ColumnNumber: 21, AbsolutePath: {ProjectDirectory}IntegrationTests.verify.cs, ContextLine: null, InApp: false, diff --git a/test/Sentry.NLog.Tests/IntegrationTests.verify.cs b/test/Sentry.NLog.Tests/IntegrationTests.verify.cs index 887897a3b4..edf97f08f8 100644 --- a/test/Sentry.NLog.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.NLog.Tests/IntegrationTests.verify.cs @@ -12,52 +12,58 @@ public Task Simple() var transport = new RecordingTransport(); - var configuration = new LoggingConfiguration(); + using (SentrySdk.Init( + options => + { + options.TracesSampleRate = 1; + options.Transport = transport; + options.DiagnosticLevel = SentryLevel.Debug; + options.Dsn = ValidDsn; + options.Release = "test-release"; + })) + { + var configuration = new LoggingConfiguration(); - configuration.AddSentry( - options => - { - options.TracesSampleRate = 1; - options.Layout = "${message}"; - options.Transport = transport; - options.DiagnosticLevel = SentryLevel.Debug; - options.IncludeEventDataOnBreadcrumbs = true; - options.MinimumBreadcrumbLevel = LogLevel.Debug; - options.Dsn = ValidDsn; - options.Release = "test-release"; - options.User = new SentryNLogUser + configuration.AddSentry( + options => { - Id = "${scopeproperty:item=id}", - Username = "${scopeproperty:item=username}", - Email = "${scopeproperty:item=email}", - IpAddress = "${scopeproperty:item=ipAddress}", - Segment = "${scopeproperty:item=segment}", - Other = + options.Layout = "${message}"; + options.IncludeEventDataOnBreadcrumbs = true; + options.MinimumBreadcrumbLevel = LogLevel.Debug; + options.User = new SentryNLogUser { - new TargetPropertyWithContext("mood", "joyous") - }, - }; + Id = "${scopeproperty:item=id}", + Username = "${scopeproperty:item=username}", + Email = "${scopeproperty:item=email}", + IpAddress = "${scopeproperty:item=ipAddress}", + Segment = "${scopeproperty:item=segment}", + Other = + { + new TargetPropertyWithContext("mood", "joyous") + }, + }; - options.AddTag("logger", "${logger}"); - }); + options.AddTag("logger", "${logger}"); + }); - LogManager.Configuration = configuration; + LogManager.Configuration = configuration; - var log = LogManager.GetCurrentClassLogger(); + var log = LogManager.GetCurrentClassLogger(); - using (ScopeContext.PushProperty("id", "myId")) - { - try - { - throw new("Exception message"); - } - catch (Exception exception) + using (ScopeContext.PushProperty("id", "myId")) { - log.Error(exception, "message = {arg}", "arg value"); + try + { + throw new("Exception message"); + } + catch (Exception exception) + { + log.Error(exception, "message = {arg}", "arg value"); + } } - } - LogManager.Flush(); + LogManager.Flush(); + } return Verify(transport.Envelopes) .UniqueForRuntimeAndVersion() @@ -68,33 +74,35 @@ public Task Simple() public Task LoggingInsideTheContextOfLogging() { var transport = new RecordingTransport(); - - var configuration = new LoggingConfiguration(); - var diagnosticLogger = new InMemoryDiagnosticLogger(); - configuration.AddSentry( - options => - { - options.TracesSampleRate = 1; - options.Debug = true; - options.DiagnosticLogger = diagnosticLogger; - options.Transport = transport; - options.Dsn = ValidDsn; - options.AttachStacktrace = false; - options.Release = "test-release"; - }); - LogManager.Configuration = configuration; + using (SentrySdk.Init( + options => + { + options.TracesSampleRate = 1; + options.Debug = true; + options.DiagnosticLogger = diagnosticLogger; + options.Transport = transport; + options.Dsn = ValidDsn; + options.AttachStacktrace = false; + options.Release = "test-release"; + })) + { + var configuration = new LoggingConfiguration(); + configuration.AddSentry(); + + LogManager.Configuration = configuration; - var log = LogManager.GetCurrentClassLogger(); + var log = LogManager.GetCurrentClassLogger(); - SentrySdk.ConfigureScope( - scope => - { - scope.OnEvaluating += (_, _) => log.Error("message from OnEvaluating"); - log.Error("message"); - }); - LogManager.Flush(); + SentrySdk.ConfigureScope( + scope => + { + scope.OnEvaluating += (_, _) => log.Error("message from OnEvaluating"); + log.Error("message"); + }); + LogManager.Flush(); + } return Verify( new diff --git a/test/Sentry.NLog.Tests/NLogDiagnosticLoggerTest.cs b/test/Sentry.NLog.Tests/NLogDiagnosticLoggerTest.cs deleted file mode 100644 index d8f7e94e60..0000000000 --- a/test/Sentry.NLog.Tests/NLogDiagnosticLoggerTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Sentry.NLog.Tests; - -public class NLogDiagnosticLoggerTest -{ - [Fact] - public void NLogDiagnosticLogger_Levels() - { - var logger = new NLogDiagnosticLogger(); - try - { - InternalLogger.LogLevel = LogLevel.Debug; - foreach (SentryLevel level in Enum.GetValues(typeof(SentryLevel))) - { - var logWriter = new StringWriter(); - InternalLogger.LogWriter = logWriter; - if (logger.IsEnabled(level)) - { - logger.Log(level, level.ToString()); - } - Assert.Contains(level.ToString(), logWriter.ToString()); - } - } - finally - { - InternalLogger.LogWriter = null; - InternalLogger.LogLevel = LogLevel.Off; - } - } -} diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs b/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs index 1faf731b88..ba80c06c16 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs @@ -7,7 +7,7 @@ public partial class SentryTargetTests [Theory] [InlineData(false)] [InlineData(true)] - public void Write_StructuredLogging_UseHubOptionsOverTargetOptions(bool isEnabled) + public void Write_StructuredLogging_RequiresHubOptions(bool isEnabled) { InMemorySentryStructuredLogger capturer = new(); _fixture.Hub.Logger.Returns(capturer); @@ -55,8 +55,8 @@ public void Write_StructuredLogging_LogEvent(bool withActiveSpan) { InMemorySentryStructuredLogger capturer = new(); _fixture.Hub.Logger.Returns(capturer); - _fixture.Options.Environment = "test-environment"; - _fixture.Options.Release = "test-release"; + _fixture.SentryOptions.Environment = "test-environment"; + _fixture.SentryOptions.Release = "test-release"; if (withActiveSpan) { diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.cs b/test/Sentry.NLog.Tests/SentryTargetTests.cs index a95ebeb259..2790811f4a 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.cs @@ -8,14 +8,14 @@ public partial class SentryTargetTests private class Fixture { - public SentryNLogOptions Options { get; } = new() { Dsn = ValidDsn }; + public SentryNLogOptions Options { get; } = new(); + + public SentryOptions SentryOptions { get; } = new() { Dsn = ValidDsn }; public IHub Hub { get; } = Substitute.For(); public Func HubAccessor { get; set; } - public IDisposable SdkDisposeHandle { get; set; } = Substitute.For(); - public Scope Scope { get; } public Fixture() @@ -24,7 +24,7 @@ public Fixture() HubAccessor = () => Hub; Scope = new Scope(new SentryOptions()); Hub.SubstituteConfigureScope(Scope); - SentryClientExtensions.SentryOptionsForTestingOnly = Options; + SentryClientExtensions.SentryOptionsForTestingOnly = SentryOptions; } public Target GetTarget(bool asyncTarget = false) @@ -32,11 +32,9 @@ public Target GetTarget(bool asyncTarget = false) var target = new SentryTarget( Options, HubAccessor, - SdkDisposeHandle, new MockClock()) { Name = "sentry", - Dsn = Options.Dsn ?? Options.DsnLayout, }; if (asyncTarget) @@ -77,9 +75,9 @@ public void Can_configure_from_xml_file() - + - True + True @@ -92,14 +90,9 @@ public void Can_configure_from_xml_file() var t = logFactory.Configuration.FindTargetByName("sentry") as SentryTarget; Assert.NotNull(t); - if (t.Options.Dsn != null) - { - Assert.Equal(ValidDsn, t.Options.Dsn); - } - - Assert.Equal("test", t.Options.Environment); - Assert.Equal("1.2.3", t.Options.Release); - Assert.True(t.Options.AttachStacktrace); + Assert.Equal(LogLevel.Warn, t.Options.MinimumEventLevel); + Assert.Equal(TimeSpan.FromSeconds(5), t.Options.FlushTimeout); + Assert.True(t.Options.IncludeEventPropertiesAsTags); } [Fact] @@ -111,7 +104,7 @@ public void Can_configure_user_from_xml_file() - + @@ -126,7 +119,6 @@ public void Can_configure_user_from_xml_file() var t = logFactory.Configuration.FindTargetByName("sentry") as SentryTarget; Assert.NotNull(t); - Assert.Equal(ValidDsn, t.Options.Dsn); Assert.Equal("myUser", t.User?.Username?.ToString()); var other = t.User?.Other; @@ -137,27 +129,8 @@ public void Can_configure_user_from_xml_file() } [Fact] - public void Shutdown_DisposesSdk() - { - _fixture.Options.InitializeSdk = false; - var target = _fixture.GetTarget(); - LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(target)); - - var sut = LogManager.GetCurrentClassLogger(); - - sut.Error(DefaultMessage); - - _fixture.SdkDisposeHandle.DidNotReceive().Dispose(); - - LogManager.Shutdown(); - - _fixture.SdkDisposeHandle.Received(1).Dispose(); - } - - [Fact] - public void Shutdown_NoDisposeHandleProvided_DoesNotThrow() + public void Shutdown_DoesNotThrow() { - _fixture.Options.InitializeSdk = false; var factory = _fixture.GetLoggerFactory(); var sut = factory.GetCurrentClassLogger(); @@ -477,68 +450,6 @@ void Continuation(Exception _) await hub.Received().FlushAsync(Arg.Any()); } - [SkippableFact] - public void InitializeTarget_InitializesSdk() - { -#if SENTRY_DSN_DEFINED_IN_ENV - Skip.If(true, "This test only works when the DSN is not configured as an environment variable."); -#endif - _fixture.Options.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; - _fixture.SdkDisposeHandle = null; - _fixture.Options.InitializeSdk = true; - - var logWriter = new StringWriter(); - - try - { - InternalLogger.LogWriter = logWriter; - InternalLogger.LogLevel = LogLevel.Debug; - - _fixture.GetLoggerFactory(); - - var logOutput = logWriter.ToString(); - Assert.Contains("Init called with an empty string as the DSN. Sentry SDK will be disabled.", logOutput); - } - finally - { - InternalLogger.LogWriter = null; - InternalLogger.LogLevel = LogLevel.Off; - } - } - - [Fact] - public void Dsn_ReturnsDsnFromOptions_Null() - { - _fixture.Options.Dsn = null; - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Null(target.Dsn); - } - - [Fact] - public void Dsn_ReturnsDsnFromOptions_Instance() - { - var expectedDsn = "https://a@sentry.io/1"; - _fixture.Options.Dsn = expectedDsn; - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Equal(expectedDsn, target.Options.Dsn); - } - - [Fact] - public void Dsn_SupportsNLogLayout_Lookup() - { - var expectedDsn = "https://a@sentry.io/1"; - var target = (SentryTarget)_fixture.GetTarget(); - target.Dsn = "${var:mydsn}"; - var logFactory = new LogFactory(); - var logConfig = new LoggingConfiguration(logFactory) - { - Variables = { ["mydsn"] = expectedDsn } - }; - logConfig.AddRuleForAllLevels(target); - logFactory.Configuration = logConfig; - Assert.Equal(expectedDsn, target.Options.Dsn); - } - [Fact] public void MinimumEventLevel_SetInOptions_ReturnsValue() { @@ -662,32 +573,6 @@ public void IncludeEventDataOnBreadcrumbs_SetterReplacesOptions() Assert.False(target.IncludeEventDataOnBreadcrumbs); } - [Fact] - public void ShutdownTimeoutSeconds_ValueFromOptions() - { - const int expected = 60; - _fixture.Options.ShutdownTimeoutSeconds = expected; - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Equal(expected, target.ShutdownTimeoutSeconds); - } - - [Fact] - public void ShutdownTimeoutSeconds_Default_2Seconds() - { - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Equal(2, target.ShutdownTimeoutSeconds); - } - - [Fact] - public void ShutdownTimeoutSeconds_SetterReplacesOptions() - { - var expected = 60; - _fixture.Options.ShutdownTimeoutSeconds = int.MinValue; - var target = (SentryTarget)_fixture.GetTarget(); - target.ShutdownTimeoutSeconds = expected; - Assert.Equal(expected, target.ShutdownTimeoutSeconds); - } - [Fact] public void FlushTimeoutSeconds_ValueFromOptions() { From 19db652e6a2c7c5f0bf2370dc44cb832fac76400 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 17 Sep 2026 15:29:54 +1200 Subject: [PATCH 05/42] Tweaked wording Co-authored-by: James Crosswell --- samples/Sentry.Samples.NLog/Program.cs | 2 +- samples/Sentry.Samples.NLog/README.md | 6 +++--- src/Sentry.NLog/SentryNLogOptions.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/Sentry.Samples.NLog/Program.cs b/samples/Sentry.Samples.NLog/Program.cs index fdfd335cb6..c2561f69ab 100644 --- a/samples/Sentry.Samples.NLog/Program.cs +++ b/samples/Sentry.Samples.NLog/Program.cs @@ -12,7 +12,7 @@ public static class Program private static void Main() { - // Initialise Sentry SDK itself + // Initialise the Sentry SDK using var _ = SentrySdk.Init(options => { #if !SENTRY_DSN_DEFINED_IN_ENV diff --git a/samples/Sentry.Samples.NLog/README.md b/samples/Sentry.Samples.NLog/README.md index a66736ebfb..99ba5bad79 100644 --- a/samples/Sentry.Samples.NLog/README.md +++ b/samples/Sentry.Samples.NLog/README.md @@ -2,9 +2,9 @@ This is a simple console application that demonstrates how you can add Sentry to your application using NLog. -This project attempts to sample the integration by using code only and also via the configuration file. -In both cases the Sentry target only sends logs to Sentry. The Sentry SDK itself is initialised separately, with -`SentrySdk.Init` in `Program.cs`, so **you need to add your own DSN** there to see the events sent in your Sentry project. +This project demonstrates configuring Sentry and NLog via code and configuration. +The Sentry target configures NLog to send logs to Sentry. The Sentry SDK itself is initialised via the call to `SentrySdk.Init` +in `Program.cs`, so **you need to add your own DSN** there to see the events sent in your Sentry project. You can get your [Sentry DSN at sentry.io](https://sentry.io). diff --git a/src/Sentry.NLog/SentryNLogOptions.cs b/src/Sentry.NLog/SentryNLogOptions.cs index dd3008349a..ae63b11835 100644 --- a/src/Sentry.NLog/SentryNLogOptions.cs +++ b/src/Sentry.NLog/SentryNLogOptions.cs @@ -1,7 +1,7 @@ namespace Sentry.NLog; /// -/// Options for the Sentry target for NLog. All properties can be configured via code or in NLog.config xml file. +/// Options for the Sentry target for NLog. All properties can be configured via code or the NLog.config file. /// /// /// These options only configure the target. The Sentry SDK itself is configured and initialised separately, using From 489d7ce82bdb22c86b41567641159495d084d3bb Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 17 Sep 2026 15:32:01 +1200 Subject: [PATCH 06/42] feat: NLog target flushes using the SDK's FlushTimeout Remove SentryTarget.FlushTimeoutSeconds and SentryNLogOptions.FlushTimeout. When NLog flushes the target, the hub is now flushed with the FlushTimeout from the options used to initialize Sentry, since the target no longer owns the SDK. Part of #5245 Co-Authored-By: Claude Opus 5 --- samples/Sentry.Samples.NLog/README.md | 2 - src/Sentry.NLog/SentryNLogOptions.cs | 5 --- src/Sentry.NLog/SentryTarget.cs | 11 +----- ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 - ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet9_0.verified.txt | 2 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 - .../ConfigurationExtensionsTest.cs | 5 +-- test/Sentry.NLog.Tests/SentryTargetTests.cs | 37 +++---------------- 10 files changed, 9 insertions(+), 61 deletions(-) diff --git a/samples/Sentry.Samples.NLog/README.md b/samples/Sentry.Samples.NLog/README.md index 99ba5bad79..78c1932a77 100644 --- a/samples/Sentry.Samples.NLog/README.md +++ b/samples/Sentry.Samples.NLog/README.md @@ -22,7 +22,6 @@ The following options are available for the NLog Sentry Target: ignoreEventsWithNoException="False" includeEventDataOnBreadcrumbs="False" includeEventPropertiesAsTags="True" - flushTimeoutSeconds="15" > @@ -47,7 +46,6 @@ The following options are available for the NLog Sentry Target: * **minimumEventLevel** - Send NLog LogEvents as SentryEvent when matching severity (or worse). Default: _Error_ * **minimumBreadcrumbLevel** - Send NLog LogEvents as Breadcrumbs when matching severity (or worse). Default: _Info_ * **ignoreEventsWithNoException** - Ignore NLog LogEvents without an exception. Default: _False_ -* **flushTimeoutSeconds** - Flush timeout in seconds before aborting flush to Sentry. Default: _15_ * **user** * **id** * **username** diff --git a/src/Sentry.NLog/SentryNLogOptions.cs b/src/Sentry.NLog/SentryNLogOptions.cs index ae63b11835..99ec14a79b 100644 --- a/src/Sentry.NLog/SentryNLogOptions.cs +++ b/src/Sentry.NLog/SentryNLogOptions.cs @@ -10,11 +10,6 @@ namespace Sentry.NLog; [NLogConfigurationItem] public class SentryNLogOptions { - /// - /// How long to wait for Sentry to flush when NLog is flushed. Defaults to 15 seconds, the same as NLog. - /// - public TimeSpan FlushTimeout { get; set; } = TimeSpan.FromSeconds(15); - /// /// Minimum log level for events to trigger a send to Sentry. Defaults to . /// diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index 68a90f0ca7..b3f53a186b 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -147,15 +147,6 @@ public bool IncludeEventDataOnBreadcrumbs set => Options.IncludeEventDataOnBreadcrumbs = value; } - /// - /// How long to wait for the flush to finish, in seconds. Defaults to 15 seconds. - /// - public int FlushTimeoutSeconds - { - get => (int)Options.FlushTimeout.TotalSeconds; - set => Options.FlushTimeout = TimeSpan.FromSeconds(value); - } - /// /// Optionally configure one or more parts of the user information to be rendered dynamically from an NLog layout /// @@ -186,7 +177,7 @@ protected override void InitializeTarget() protected override void FlushAsync(AsyncContinuation asyncContinuation) { _ = HubAccessor() - .FlushAsync(Options.FlushTimeout) + .FlushAsync() .ContinueWith(t => asyncContinuation(t.Exception)); } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 4e37d9de02..331cfbd575 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -17,7 +17,6 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } @@ -52,7 +51,6 @@ namespace Sentry.NLog [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 4e37d9de02..331cfbd575 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -17,7 +17,6 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } @@ -52,7 +51,6 @@ namespace Sentry.NLog [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4e37d9de02..331cfbd575 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -17,7 +17,6 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } @@ -52,7 +51,6 @@ namespace Sentry.NLog [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 4e37d9de02..331cfbd575 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -17,7 +17,6 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } @@ -52,7 +51,6 @@ namespace Sentry.NLog [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 4e37d9de02..331cfbd575 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -17,7 +17,6 @@ namespace Sentry.NLog public NLog.Layouts.Layout? BreadcrumbCategoryLayout { get; set; } [NLog.Config.NLogConfigurationIgnoreProperty] public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - public System.TimeSpan FlushTimeout { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } @@ -52,7 +51,6 @@ namespace Sentry.NLog [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } - public int FlushTimeoutSeconds { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } diff --git a/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs b/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs index d1f950d284..4a1efcb3a9 100644 --- a/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs +++ b/test/Sentry.NLog.Tests/ConfigurationExtensionsTest.cs @@ -12,10 +12,9 @@ public void AddSentry_Parameterless_DefaultTargetName() [Fact] public void AddSentry_ConfigCallback_CallbackInvoked() { - var expected = TimeSpan.FromDays(1); - var actual = new LoggingConfiguration().AddSentry(o => o.FlushTimeout = expected); + var actual = new LoggingConfiguration().AddSentry(o => o.MinimumEventLevel = LogLevel.Warn); var sentryTarget = Assert.IsType(actual.AllTargets[0]); - Assert.Equal(expected.TotalSeconds, sentryTarget.FlushTimeoutSeconds); + Assert.Equal(LogLevel.Warn.ToString(), sentryTarget.MinimumEventLevel); } [Fact] diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.cs b/test/Sentry.NLog.Tests/SentryTargetTests.cs index 2790811f4a..b04eb5d284 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.cs @@ -75,7 +75,7 @@ public void Can_configure_from_xml_file() - + True @@ -91,7 +91,7 @@ public void Can_configure_from_xml_file() var t = logFactory.Configuration.FindTargetByName("sentry") as SentryTarget; Assert.NotNull(t); Assert.Equal(LogLevel.Warn, t.Options.MinimumEventLevel); - Assert.Equal(TimeSpan.FromSeconds(5), t.Options.FlushTimeout); + Assert.True(t.Options.IgnoreEventsWithNoException); Assert.True(t.Options.IncludeEventPropertiesAsTags); } @@ -413,11 +413,12 @@ public void Log_WithCustomBreadcrumbCategory_RendersCorrectly() } [Fact] - public async Task LogManager_WhenFlushCalled_CallsSentryFlushAsync() + public async Task LogManager_WhenFlushCalled_FlushesHubWithSdkFlushTimeout() { var timeout = TimeSpan.FromSeconds(2); + var sdkFlushTimeout = TimeSpan.FromSeconds(3); - _fixture.Options.FlushTimeout = timeout; + _fixture.SentryOptions.FlushTimeout = sdkFlushTimeout; var factory = _fixture.GetLoggerFactory(asyncTarget: true); // Verify that it's asynchronous @@ -447,7 +448,7 @@ void Continuation(Exception _) Assert.True(tcs.Task.IsCompleted); testDisposable.Received().Dispose(); - await hub.Received().FlushAsync(Arg.Any()); + await hub.Received().FlushAsync(sdkFlushTimeout); } [Fact] @@ -573,25 +574,6 @@ public void IncludeEventDataOnBreadcrumbs_SetterReplacesOptions() Assert.False(target.IncludeEventDataOnBreadcrumbs); } - [Fact] - public void FlushTimeoutSeconds_ValueFromOptions() - { - var expected = 10; - _fixture.Options.FlushTimeout = TimeSpan.FromSeconds(expected); - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Equal(expected, target.FlushTimeoutSeconds); - } - - [Fact] - public void FlushTimeoutSeconds_SetterReplacesOptions() - { - var expected = 100; - _fixture.Options.FlushTimeout = TimeSpan.FromSeconds(expected); - var target = (SentryTarget)_fixture.GetTarget(); - target.FlushTimeoutSeconds = expected; - Assert.Equal(expected, target.FlushTimeoutSeconds); - } - [Fact] public void IgnoreEventsWithNoException_SetterReplacesOptions() { @@ -601,13 +583,6 @@ public void IgnoreEventsWithNoException_SetterReplacesOptions() Assert.True(target.IgnoreEventsWithNoException); } - [Fact] - public void FlushTimeoutSeconds_Default_15Seconds() - { - var target = (SentryTarget)_fixture.GetTarget(); - Assert.Equal(15, target.FlushTimeoutSeconds); - } - [Fact] public void BreadcrumbLayout_Null_FallsBackToLayout() { From 6438db5cb05366d1d97d041e7235ed4aa4a69816 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 17 Sep 2026 16:36:22 +1200 Subject: [PATCH 07/42] feat: log4net appender no longer initializes the SDK The Sentry appender for log4net now only sends log events to Sentry. Sentry must be initialized separately (SentrySdk.Init, UseSentry, etc). - Remove SentryAppender.Dsn and the lazy SDK initialization on first append - Remove SentryAppender.Environment; events take the environment from the options used to initialize Sentry - Remove the OnClose override, which only disposed the SDK the appender had initialized Part of #5245 Co-Authored-By: Claude Opus 5 --- samples/Sentry.Samples.Log4Net/Program.cs | 11 ++ samples/Sentry.Samples.Log4Net/README.md | 6 +- samples/Sentry.Samples.Log4Net/app.config | 7 +- .../SentryAppender.Structured.cs | 8 +- src/Sentry.Log4Net/SentryAppender.cs | 52 +------- ...iApprovalTests.Run.DotNet10_0.verified.txt | 3 - ...iApprovalTests.Run.DotNet11_0.verified.txt | 3 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 3 - .../IntegrationTests.verify.cs | 5 +- .../SentryAppenderTests.Structured.cs | 16 --- .../SentryAppenderTests.cs | 120 +----------------- 11 files changed, 30 insertions(+), 204 deletions(-) diff --git a/samples/Sentry.Samples.Log4Net/Program.cs b/samples/Sentry.Samples.Log4Net/Program.cs index f35396bf34..5bc40b5fbb 100644 --- a/samples/Sentry.Samples.Log4Net/Program.cs +++ b/samples/Sentry.Samples.Log4Net/Program.cs @@ -10,6 +10,17 @@ internal class Program private static void Main() { + // Initialise the Sentry SDK. The SentryAppender configured in app.config only sends log events to Sentry. + using var _ = SentrySdk.Init(options => + { +#if !SENTRY_DSN_DEFINED_IN_ENV + // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = SamplesShared.Dsn; +#endif + options.Environment = "dev"; + }); + // Set the user running the process the current principal // Appender was configured to send the user with the event AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); diff --git a/samples/Sentry.Samples.Log4Net/README.md b/samples/Sentry.Samples.Log4Net/README.md index 58e51407d1..4905969628 100644 --- a/samples/Sentry.Samples.Log4Net/README.md +++ b/samples/Sentry.Samples.Log4Net/README.md @@ -2,8 +2,8 @@ This is a simple console application that demonstrates how you can add Sentry to your applications using log4net. -`Program.cs` doesn't have any reference to Sentry. In fact, it doesn't even include a `using Sentry;` directive. -The integration is simply done by adding the [Sentry.Log4Net](https://www.nuget.org/packages/Sentry.Log4Net) package to you project and configuring the appender via xml. +The Sentry SDK is initialised via the call to `SentrySdk.Init` in `Program.cs`. Log events are sent to Sentry by adding the +[Sentry.Log4Net](https://www.nuget.org/packages/Sentry.Log4Net) package to your project and configuring the appender via xml. It's also possible to use the SDK directly on your app as the documentation describes. If you for example call `SentrySdk.AddBreadcrumb("crumb")`, all log4net events will include that breadcrumb. @@ -12,7 +12,7 @@ If you for example call `SentrySdk.AddBreadcrumb("crumb")`, all log4net events w ## Running this sample -> First modify the `app.config` and add **your own DSN**. You can get one at [sentry.io](sentry.io). This will make sure you can see the result of running this app by looking at what this creates in Sentry. +> First modify `Program.cs` and add **your own DSN**. You can get one at [sentry.io](sentry.io). This will make sure you can see the result of running this app by looking at what this creates in Sentry. Now you're ready to run the code. diff --git a/samples/Sentry.Samples.Log4Net/app.config b/samples/Sentry.Samples.Log4Net/app.config index 613a0bed0e..b61cde00af 100644 --- a/samples/Sentry.Samples.Log4Net/app.config +++ b/samples/Sentry.Samples.Log4Net/app.config @@ -10,14 +10,9 @@ - - - - - + - diff --git a/src/Sentry.Log4Net/SentryAppender.Structured.cs b/src/Sentry.Log4Net/SentryAppender.Structured.cs index f0230dafc5..feb810ed0f 100644 --- a/src/Sentry.Log4Net/SentryAppender.Structured.cs +++ b/src/Sentry.Log4Net/SentryAppender.Structured.cs @@ -2,7 +2,7 @@ namespace Sentry.Log4Net; public partial class SentryAppender { - private static void CaptureStructuredLog(IHub hub, SentryOptions options, LoggingEvent loggingEvent, string? environment, bool sendIdentity) + private static void CaptureStructuredLog(IHub hub, SentryOptions options, LoggingEvent loggingEvent, bool sendIdentity) { if (loggingEvent.ToSentryLogLevel() is not { } level) { @@ -20,12 +20,6 @@ private static void CaptureStructuredLog(IHub hub, SentryOptions options, Loggin log.SetDefaultAttributes(options, scope, Sdk); log.SetOrigin("auto.log.log4net"); - // Honor the appender-level settings, overriding the scope/options defaults, to match the SentryEvent path. - if (!string.IsNullOrWhiteSpace(environment)) - { - log.SetAttribute("sentry.environment", environment!); - } - if (sendIdentity && !string.IsNullOrEmpty(loggingEvent.Identity)) { log.SetAttribute("user.id", loggingEvent.Identity); diff --git a/src/Sentry.Log4Net/SentryAppender.cs b/src/Sentry.Log4Net/SentryAppender.cs index 9fa218fed4..e4295705c9 100644 --- a/src/Sentry.Log4Net/SentryAppender.cs +++ b/src/Sentry.Log4Net/SentryAppender.cs @@ -7,11 +7,6 @@ namespace Sentry.Log4Net; /// public partial class SentryAppender : AppenderSkeleton { - private readonly Func _initAction; - private volatile IDisposable? _sdkHandle; - - private readonly Lock _initSync = new(); - internal static readonly SdkVersion NameAndVersion = typeof(SentryAppender).Assembly.GetNameAndVersion(); @@ -25,18 +20,10 @@ internal static readonly SdkVersion NameAndVersion private readonly IHub _hub; - /// - /// Sentry DSN. - /// - public string? Dsn { get; set; } /// /// Whether to send the Identity or not. /// public bool SendIdentity { get; set; } - /// - /// Environment to send in the event. - /// - public string? Environment { get; set; } /// /// Lowest level required for a log message to become an event. @@ -53,15 +40,12 @@ internal static readonly SdkVersion NameAndVersion /// /// Creates a new instance of the . /// - public SentryAppender() : this(SentrySdk.Init, HubAdapter.Instance) + public SentryAppender() : this(HubAdapter.Instance) { } - internal SentryAppender( - Func initAction, - IHub hubGetter) + internal SentryAppender(IHub hub) { - _initAction = initAction; - _hub = hubGetter; + _hub = hub; } /// @@ -76,18 +60,9 @@ protected override void Append(LoggingEvent loggingEvent) return; } - if (!_hub.IsEnabled && _sdkHandle == null) + if (!_hub.IsEnabled) { - if (Dsn == null) - { - return; - } - - lock (_initSync) - { - // ReSharper disable once NonAtomicCompoundOperator Double init guarded by the lock - _sdkHandle ??= _initAction(Dsn); - } + return; } CaptureStructuredLog(loggingEvent); @@ -113,7 +88,7 @@ private void CaptureStructuredLog(LoggingEvent loggingEvent) try { - CaptureStructuredLog(_hub, options, loggingEvent, Environment, SendIdentity); + CaptureStructuredLog(_hub, options, loggingEvent, SendIdentity); } catch (Exception ex) { @@ -155,11 +130,6 @@ private void CreateSentryEvent(LoggingEvent loggingEvent, Exception? exception) }; } - if (!string.IsNullOrWhiteSpace(Environment)) - { - evt.Environment = Environment; - } - _hub.CaptureEvent(evt); } @@ -237,14 +207,4 @@ private void AddBreadcrumbFromLoggingEvent(LoggingEvent loggingEvent) yield return new KeyValuePair("log4net-level", loggingEvent.Level.Name); } } - - /// - /// Disposes the SDK if initialized. - /// - protected override void OnClose() - { - base.OnClose(); - - _sdkHandle?.Dispose(); - } } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 117659776a..a82332f24d 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,11 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } - public string? Dsn { get; set; } - public string? Environment { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } - protected override void OnClose() { } } } \ No newline at end of file diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 117659776a..a82332f24d 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,11 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } - public string? Dsn { get; set; } - public string? Environment { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } - protected override void OnClose() { } } } \ No newline at end of file diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 117659776a..a82332f24d 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,11 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } - public string? Dsn { get; set; } - public string? Environment { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } - protected override void OnClose() { } } } \ No newline at end of file diff --git a/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs b/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs index 6ba7959933..9675389741 100644 --- a/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.Log4Net.Tests/IntegrationTests.verify.cs @@ -104,12 +104,9 @@ private static Hierarchy SetupLogging(IHub hub) tracer.ActivateOptions(); hierarchy.Root.AddAppender(tracer); - var appender = new SentryAppender( - _ => Substitute.For(), - hub) + var appender = new SentryAppender(hub) { Layout = layout, - Dsn = ValidDsn, SendIdentity = true }; appender.ActivateOptions(); diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs b/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs index abbc4f9a2a..55211bcb32 100644 --- a/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs +++ b/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs @@ -209,22 +209,6 @@ public void DoAppend_StructuredLoggingWithoutException_LeavesBreadcrumb() _ = _fixture.Hub.Received(0).CaptureEvent(Arg.Any()); } - [Fact] - public void DoAppend_StructuredLogging_ConfiguredEnvironment_OverridesOptions() - { - InMemorySentryStructuredLogger capturer = new(); - _fixture.Hub.Logger.Returns(capturer); - _fixture.Options.Environment = "options-environment"; - - var sut = _fixture.GetSut(); - sut.Environment = "appender-environment"; - - sut.DoAppend(CreateLoggingEvent(Level.Info, "Message")); - - var log = capturer.Logs.Should().ContainSingle().Which; - log.Attributes.ShouldContain("sentry.environment", "appender-environment"); - } - [Fact] public void DoAppend_StructuredLogging_SendIdentity_SetsUser() { diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs index cfcad5c7c9..d6050033f1 100644 --- a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs +++ b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs @@ -4,36 +4,21 @@ public partial class SentryAppenderTests : IDisposable { private class Fixture { - public bool InitInvoked { get; set; } - public string DsnReceivedOnInit { get; set; } - public IDisposable SdkDisposeHandle { get; set; } = Substitute.For(); - public Func InitAction { get; set; } public IHub Hub { get; set; } = Substitute.For(); - public Func HubAccessor { get; set; } public Scope Scope { get; } = new(new SentryOptions()); - public string Dsn { get; set; } = "dsn"; public SentryOptions Options { get; } = new(); public Fixture() { - HubAccessor = () => Hub; + Hub.IsEnabled.Returns(true); Hub.SubstituteConfigureScope(Scope); - InitAction = s => - { - DsnReceivedOnInit = s; - InitInvoked = true; - return SdkDisposeHandle; - }; } public SentryAppender GetSut() { SentryClientExtensions.SentryOptionsForTestingOnly = Options; - var sut = new SentryAppender(InitAction, Hub) - { - Dsn = Dsn - }; + var sut = new SentryAppender(Hub); sut.ActivateOptions(); return sut; } @@ -185,66 +170,19 @@ public void Append_ConfiguredSendUser_UserInEvent() } [Fact] - public void Append_NoDsn_InitNotCalled() + public void Append_DisabledHub_HubNotCalled() { - _fixture.Dsn = null; - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - sut.DoAppend(evt); - - Assert.False(_fixture.InitInvoked); - } - - [Fact] - public void Append_WithEnabledHub_InitNotCalled() - { - _ = _fixture.Hub.IsEnabled.Returns(true); - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - sut.DoAppend(evt); - - Assert.False(_fixture.InitInvoked); - } - - [Fact] - public void Append_WithDsn_InitCalled() - { - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - sut.DoAppend(evt); - - Assert.True(_fixture.InitInvoked); - Assert.Same(_fixture.Dsn, _fixture.DsnReceivedOnInit); - } - - [Fact] - public void Append_NoDsn_HubNotCalled() - { - _fixture.Dsn = null; - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - sut.DoAppend(evt); - - Assert.False(_fixture.InitInvoked); - _ = _fixture.Hub.DidNotReceiveWithAnyArgs().CaptureEvent(null); - } - - [Fact] - public void Append_NoDsnAndDisabledHub_HubNotCalled() - { - _fixture.Dsn = null; _ = _fixture.Hub.IsEnabled.Returns(false); + InMemorySentryStructuredLogger capturer = new(); + _fixture.Hub.Logger.Returns(capturer); var sut = _fixture.GetSut(); var evt = new LoggingEvent(new LoggingEventData()); sut.DoAppend(evt); - Assert.False(_fixture.InitInvoked); _ = _fixture.Hub.DidNotReceiveWithAnyArgs().CaptureEvent(null); + capturer.Logs.Should().BeEmpty(); + _fixture.Scope.Breadcrumbs.Should().BeEmpty(); } [Fact] @@ -284,7 +222,6 @@ public void Append_ThreadContext_AsExtra() ThreadContext.Properties[id] = expected; var sut = _fixture.GetSut(); - sut.Dsn = "dsn"; var evt = new LoggingEvent(new LoggingEventData()); sut.DoAppend(evt); @@ -293,32 +230,6 @@ public void Append_ThreadContext_AsExtra() .CaptureEvent(Arg.Is(e => e.Extra[id] == expected)); } - [Fact] - public void Append_ByDefault_DoesNotSetEnvironment() - { - var sut = _fixture.GetSut(); - var evt = new LoggingEvent(new LoggingEventData()); - - sut.DoAppend(evt); - - _ = _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Environment == null)); - } - - [Fact] - public void Append_ConfiguredEnvironment() - { - const string expected = "dev"; - var sut = _fixture.GetSut(); - sut.Environment = expected; - var evt = new LoggingEvent(new LoggingEventData()); - - sut.DoAppend(evt); - - _ = _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Environment == expected)); - } - [Fact] public void MinimumEventLevel_DefaultsToNull() { @@ -388,21 +299,4 @@ public void DoAppend_AboveMinimumEventLevel_AddsEvent() _ = _fixture.Hub.Received(1) .CaptureEvent(Arg.Is(e => e.Message.Message == expectedMessage)); } - - [Fact] - public void Close_DisposesSdk() - { - const string expectedDsn = "dsn"; - var sut = _fixture.GetSut(); - sut.Dsn = expectedDsn; - - var evt = new LoggingEvent(new LoggingEventData()); - sut.DoAppend(evt); - - _fixture.SdkDisposeHandle.DidNotReceive().Dispose(); - - sut.Close(); - - _fixture.SdkDisposeHandle.Received(1).Dispose(); - } } From d2e72811a9251cbba1258710a27671889f565ceb Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 18 Sep 2026 11:45:37 +1200 Subject: [PATCH 08/42] feat: Microsoft.Extensions.Logging integration no longer initializes the SDK Completes the logging-integration part of #5245. The MEL integration now only wires up the logger providers; Sentry has to be initialized separately. Unlike Serilog, NLog and log4net, SentryLoggingOptions keeps deriving from SentryOptions, because SentryAspNetCoreOptions, SentryMauiOptions and SentryBlazorOptions derive from it and those integrations do initialize the SDK. InitializeSdk therefore stays as internal plumbing, now defaulting to false and opted into by the framework integrations that own it. Co-Authored-By: Claude Opus 5 --- samples/Sentry.Samples.GenericHost/Program.cs | 17 +++--- .../appsettings.json | 4 +- samples/Sentry.Samples.ME.Logging/Program.cs | 44 +++++++-------- .../Program.cs | 22 ++++---- .../WebAssemblyHostBuilderExtensions.cs | 5 ++ .../SentryAspNetCoreOptions.cs | 2 + .../BindableSentryLoggingOptions.cs | 2 - .../LoggingBuilderExtensions.cs | 8 --- .../SentryLoggerFactoryExtensions.cs | 33 ++---------- .../SentryLoggingOptions.cs | 10 ++-- .../Internal/SentryMauiOptionsSetup.cs | 3 -- .../allsettings.json | 1 - .../IntegrationMockedBackgroundWorker.cs | 1 - test/Sentry.AspNetCore.Tests/allsettings.json | 1 - ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 - ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 - ...piApprovalTests.Run.DotNet9_0.verified.txt | 2 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 - .../ConfigurationOptionsTests.cs | 10 +--- .../LoggingBuilderExtensionsTests.cs | 10 +--- .../LoggingTests.cs | 40 +++++++++----- .../SentryLoggerFactoryExtensionsTests.cs | 53 +++---------------- .../SentryLoggingOptionsSetupTests.cs | 5 +- .../appsettings.json | 3 +- 25 files changed, 102 insertions(+), 182 deletions(-) diff --git a/samples/Sentry.Samples.GenericHost/Program.cs b/samples/Sentry.Samples.GenericHost/Program.cs index 5bad8f3641..ec0d0ab221 100644 --- a/samples/Sentry.Samples.GenericHost/Program.cs +++ b/samples/Sentry.Samples.GenericHost/Program.cs @@ -7,14 +7,19 @@ builder.Logging.AddConfiguration(builder.Configuration); +// Initialise the Sentry SDK. The logging integration added below only forwards log messages to Sentry. +using var sentry = SentrySdk.Init(options => +{ #if !SENTRY_DSN_DEFINED_IN_ENV -// A DSN is required. You can set it here in code, via the SENTRY_DSN environment variable or in your -// appsettings.json file. -// See https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/#configure -builder.Logging.AddSentry(SamplesShared.Dsn); -#else -builder.Logging.AddSentry(); + // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = SamplesShared.Dsn; #endif + // Send user name and machine name + options.SendDefaultPii = true; +}); + +builder.Logging.AddSentry(); builder.Services.AddHostedService(); diff --git a/samples/Sentry.Samples.GenericHost/appsettings.json b/samples/Sentry.Samples.GenericHost/appsettings.json index 9d20069922..5d9d65349a 100644 --- a/samples/Sentry.Samples.GenericHost/appsettings.json +++ b/samples/Sentry.Samples.GenericHost/appsettings.json @@ -10,9 +10,7 @@ } }, "Sentry": { - //"Dsn": "TODO: Configure your DSN here and uncomment this line", "MinimumBreadcrumbLevel": "Debug", - "MinimumEventLevel": "Warning", - "SendDefaultPii": true // Send user name and machine name + "MinimumEventLevel": "Warning" } } diff --git a/samples/Sentry.Samples.ME.Logging/Program.cs b/samples/Sentry.Samples.ME.Logging/Program.cs index fb2e8aa63d..e027cd7330 100644 --- a/samples/Sentry.Samples.ME.Logging/Program.cs +++ b/samples/Sentry.Samples.ME.Logging/Program.cs @@ -1,35 +1,39 @@ using Microsoft.Extensions.Logging; using Sentry.Extensions.Logging; -using var loggerFactory = LoggerFactory.Create(builder => +// Initialise the Sentry SDK. The logging integration added below only forwards log messages to Sentry. +using var sentry = SentrySdk.Init(options => { - builder.AddConsole(); - builder.AddSentry(options => - { #if !SENTRY_DSN_DEFINED_IN_ENV - // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - options.Dsn = SamplesShared.Dsn; + // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + options.Dsn = SamplesShared.Dsn; #endif - // Set to true to SDK debugging to see the internal messages through the logging library. - options.Debug = false; - // Configure the level of Sentry internal logging - options.DiagnosticLevel = SentryLevel.Debug; + // Set to true to SDK debugging to see the internal messages through the logging library. + options.Debug = false; + // Configure the level of Sentry internal logging + options.DiagnosticLevel = SentryLevel.Debug; - options.MaxBreadcrumbs = 150; // Increasing from default 100 - options.Release = "e386dfd"; // If not set here, SDK looks for it on main assembly's AssemblyInformationalVersion and AssemblyVersion + options.MaxBreadcrumbs = 150; // Increasing from default 100 + options.Release = "e386dfd"; // If not set here, SDK looks for it on main assembly's AssemblyInformationalVersion and AssemblyVersion + + options.SetBeforeSendLog(static log => + { + log.SetAttribute("attribute-key", "attribute-value"); + return log; + }); +}); +using var loggerFactory = LoggerFactory.Create(builder => +{ + builder.AddConsole(); + builder.AddSentry(options => + { // Optionally configure options: The default values are: options.MinimumBreadcrumbLevel = LogLevel.Information; // It requires at least this level to store breadcrumb options.MinimumEventLevel = LogLevel.Error; // This level or above will result in event sent to Sentry - options.SetBeforeSendLog(static log => - { - log.SetAttribute("attribute-key", "attribute-value"); - return log; - }); - // Don't keep as a breadcrumb or send events for messages of level less than Critical with exception of type DivideByZeroException options.AddLogEntryFilter((_, level, _, exception) => level < LogLevel.Critical && exception is DivideByZeroException); @@ -86,8 +90,6 @@ Dependency.Work("8 - This unhandled exception is captured and includes Scope (A, B) and crumbs: (2, 4, 5) and event (3) "); } -// Disposing the LoggerFactory will close the SDK since it was initialized through -// the integration while calling .Init() internal static class Dependency { diff --git a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs index 93c91d900f..9342669372 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs +++ b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs @@ -14,6 +14,16 @@ var dsn = SamplesShared.Dsn; #endif +// Initialise the Sentry SDK. The logging integration added below only forwards log messages to Sentry. +using var sentry = SentrySdk.Init(options => +{ + options.Dsn = dsn; + options.TracesSampleRate = 1.0; + options.UseOtlp(); // <-- Configure Sentry to use open telemetry + options.DisableSentryHttpMessageHandler = true; // So Sentry doesn't also create spans for outbound HTTP requests + options.Debug = true; +}); + var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => @@ -25,17 +35,7 @@ .AddHttpClientInstrumentation(); // From OpenTelemetry.Instrumentation.Http... adds automatic tracing for outgoing HTTP requests }); }) - .ConfigureLogging(logging => - { - logging.AddSentry(options => - { - options.Dsn = dsn; - options.TracesSampleRate = 1.0; - options.UseOtlp(); // <-- Configure Sentry to use open telemetry - options.DisableSentryHttpMessageHandler = true; // So Sentry doesn't also create spans for outbound HTTP requests - options.Debug = true; - }); - }) + .ConfigureLogging(logging => logging.AddSentry()) .Build(); await host.RunAsync(); diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs index 25c3070e68..be31fc993a 100644 --- a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs +++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs @@ -46,5 +46,10 @@ public static WebAssemblyHostBuilder UseSentry(this WebAssemblyHostBuilder build /// public class SentryBlazorOptions : SentryLoggingOptions { + /// + /// Creates a new instance of . + /// + public SentryBlazorOptions() => InitializeSdk = true; + // Awesome Blazor specific options go here } diff --git a/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs b/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs index f25ebc11ba..92ddc9a39d 100644 --- a/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs +++ b/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs @@ -108,6 +108,8 @@ public SentryAspNetCoreOptions() { // Don't report Environment.UserName as the user. IsEnvironmentUser = false; + + InitializeSdk = true; } internal void SetEnvironment(IWebHostEnvironment hostingEnvironment) diff --git a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs index 299e61a21c..a976890ef2 100644 --- a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs @@ -7,13 +7,11 @@ internal class BindableSentryLoggingOptions : BindableSentryOptions { public LogLevel? MinimumBreadcrumbLevel { get; set; } public LogLevel? MinimumEventLevel { get; set; } - public bool? InitializeSdk { get; set; } public void ApplyTo(SentryLoggingOptions options) { base.ApplyTo(options); options.MinimumBreadcrumbLevel = MinimumBreadcrumbLevel ?? options.MinimumBreadcrumbLevel; options.MinimumEventLevel = MinimumEventLevel ?? options.MinimumEventLevel; - options.InitializeSdk = InitializeSdk ?? options.InitializeSdk; } } diff --git a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs index 7731288078..f4993ecf9f 100644 --- a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs +++ b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs @@ -21,14 +21,6 @@ public static class LoggingBuilderExtensions public static ILoggingBuilder AddSentry(this ILoggingBuilder builder) => builder.AddSentry((Action?)null); - /// - /// Adds the Sentry logging integration. - /// - /// The builder. - /// The DSN. - public static ILoggingBuilder AddSentry(this ILoggingBuilder builder, string dsn) - => builder.AddSentry(o => o.Dsn = dsn); - /// /// Adds the Sentry logging integration. /// diff --git a/src/Sentry.Extensions.Logging/SentryLoggerFactoryExtensions.cs b/src/Sentry.Extensions.Logging/SentryLoggerFactoryExtensions.cs index c2f2fc1e9e..f9fd8fae8d 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggerFactoryExtensions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggerFactoryExtensions.cs @@ -17,8 +17,8 @@ public static class SentryLoggerFactoryExtensions /// Adds the Sentry logging integration. /// /// - /// This method does not need to be called when calling `UseSentry` with ASP.NET Core - /// since that integrates with the logging framework automatically. + /// This method does not initialize Sentry. Initialize it separately, with + /// or a framework integration such as UseSentry. /// /// The factory. /// The options configuration. @@ -30,34 +30,7 @@ public static ILoggerFactory AddSentry( optionsConfiguration?.Invoke(options); - if (options.DiagnosticLogger == null) - { - var logger = factory.CreateLogger(); - options.DiagnosticLogger = new MelDiagnosticLogger(logger, options.DiagnosticLevel); - } - - IHub hub; - if (options.InitializeSdk) - { - if (SentrySdk.IsEnabled && options.Dsn is null) - { - options.LogWarning("Not calling Init from {0} because SDK is already enabled and no DSN was provided to the integration", nameof(SentryLoggerFactoryExtensions)); - hub = HubAdapter.Instance; - } - else - { - options.LogDebug("Initializing from {0} and swapping current Hub.", nameof(SentryLoggerFactoryExtensions)); - hub = SentrySdk.InitHub(options); - SentrySdk.UseHub(hub); - } - } - else - { - // Access to whatever the SentrySdk points to (disabled or initialized via SentrySdk.Init) - hub = HubAdapter.Instance; - } - - factory.AddProvider(new SentryLoggerProvider(hub, SystemClock.Clock, options)); + factory.AddProvider(new SentryLoggerProvider(HubAdapter.Instance, SystemClock.Clock, options)); return factory; } } diff --git a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs index 3014f7cd0e..a4e9a2075c 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs @@ -30,17 +30,17 @@ public class SentryLoggingOptions : SentryOptions /// public LogLevel MinimumEventLevel { get; set; } = LogLevel.Error; - /// - /// Whether to initialize this SDK through this integration - /// - public bool InitializeSdk { get; set; } = true; - /// /// Add a callback to configure the scope upon SDK initialization /// /// The function to invoke when initializing the SDK public void ConfigureScope(Action action) => ConfigureScopeCallbacks = ConfigureScopeCallbacks.Concat(new[] { action }).ToArray(); + /// + /// Whether the integration owning these options initializes the SDK + /// + internal bool InitializeSdk { get; set; } + /// /// Log entry filters /// diff --git a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs index 7436d0720e..f759e7861d 100644 --- a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs +++ b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs @@ -33,9 +33,6 @@ public void Configure(SentryMauiOptions options) // NOTE: Anything set here will overwrite options set by the user. // For option defaults that can be changed, use the constructor in SentryMauiOptions instead. - // We'll initialize the SDK in SentryMauiInitializer - options.InitializeSdk = false; - // Global Mode makes sense for client apps options.IsGlobalModeEnabled = true; diff --git a/test/Sentry.AspNetCore.Grpc.Tests/allsettings.json b/test/Sentry.AspNetCore.Grpc.Tests/allsettings.json index 4be2835072..880d7ce1e5 100644 --- a/test/Sentry.AspNetCore.Grpc.Tests/allsettings.json +++ b/test/Sentry.AspNetCore.Grpc.Tests/allsettings.json @@ -6,7 +6,6 @@ "IncludeActivityData": true, "MinimumBreadcrumbLevel": "Error", "MinimumEventLevel": "Critical", - "InitializeSdk": "false", "MaxBreadcrumbs": "999", "SampleRate": "1", "Release": "7f5d9a1", diff --git a/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs b/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs index 4869455bf4..8ef5288078 100644 --- a/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs +++ b/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs @@ -262,7 +262,6 @@ public void AllSettingsViaJson() Assert.True(options.IncludeActivityData); Assert.Equal(LogLevel.Error, options.MinimumBreadcrumbLevel); Assert.Equal(LogLevel.Critical, options.MinimumEventLevel); - Assert.False(options.InitializeSdk); Assert.Equal(999, options.MaxBreadcrumbs); Assert.Equal(1, options.SampleRate); Assert.Equal("7f5d9a1", options.Release); diff --git a/test/Sentry.AspNetCore.Tests/allsettings.json b/test/Sentry.AspNetCore.Tests/allsettings.json index 427091798c..6edd4090f7 100644 --- a/test/Sentry.AspNetCore.Tests/allsettings.json +++ b/test/Sentry.AspNetCore.Tests/allsettings.json @@ -6,7 +6,6 @@ "IncludeActivityData": true, "MinimumBreadcrumbLevel": "Error", "MinimumEventLevel": "Critical", - "InitializeSdk": "false", "MaxBreadcrumbs": "999", "SampleRate": 1.0, "Release": "7f5d9a1", diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 8650a342f4..2d06a96f0f 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,7 +4,6 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -31,7 +30,6 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions : Sentry.SentryOptions { public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 8650a342f4..2d06a96f0f 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,7 +4,6 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -31,7 +30,6 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions : Sentry.SentryOptions { public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 8650a342f4..2d06a96f0f 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -4,7 +4,6 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -31,7 +30,6 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions : Sentry.SentryOptions { public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 8650a342f4..2d06a96f0f 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -4,7 +4,6 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -31,7 +30,6 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions : Sentry.SentryOptions { public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 8650a342f4..2d06a96f0f 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,7 +4,6 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } - public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -31,7 +30,6 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions : Sentry.SentryOptions { public SentryLoggingOptions() { } - public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } diff --git a/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs b/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs index 2c66765548..969ffc4d73 100644 --- a/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs @@ -49,22 +49,14 @@ public void SentryLoggingOptions_ValuesFromAppSettings() using (new AssertionScope()) { - sentryLoggingOptions.InitializeSdk.Should().BeFalse(); sentryLoggingOptions.MinimumBreadcrumbLevel.Should().Be(LogLevel.Warning); sentryLoggingOptions.MinimumEventLevel.Should().Be(LogLevel.Critical); } } [Fact] - public void SentryOptions_InitializeTrue_ValuesAppliedFromLoggingOptions() + public void SentryOptions_ValuesFromAppSettings() { - var dict = new Dictionary - { - {"Sentry:InitializeSdk", "true"}, - }; - - _ = _fixture.Builder.AddInMemoryCollection(dict); - var provider = _fixture.GetSut(); var sentryLoggingOptions = provider.GetRequiredService>().Value; diff --git a/test/Sentry.Extensions.Logging.Tests/LoggingBuilderExtensionsTests.cs b/test/Sentry.Extensions.Logging.Tests/LoggingBuilderExtensionsTests.cs index 8f9546b8d8..25166aad29 100644 --- a/test/Sentry.Extensions.Logging.Tests/LoggingBuilderExtensionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/LoggingBuilderExtensionsTests.cs @@ -11,10 +11,7 @@ public void AddSentry_LoggingBuilder_AddLoggerProviders() { // Arrange var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging((ILoggingBuilder builder) => builder.AddSentry(options => - { - options.InitializeSdk = false; - })); + serviceCollection.AddLogging((ILoggingBuilder builder) => builder.AddSentry()); using var serviceProvider = serviceCollection.BuildServiceProvider(); using var loggerFactory = serviceProvider.GetRequiredService(); @@ -32,10 +29,7 @@ public void AddSentry_LoggingBuilder_AddLoggerFilterRules() { // Arrange var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging((ILoggingBuilder builder) => builder.AddSentry(options => - { - options.InitializeSdk = false; - })); + serviceCollection.AddLogging((ILoggingBuilder builder) => builder.AddSentry()); using var serviceProvider = serviceCollection.BuildServiceProvider(); using var loggerFactory = serviceProvider.GetRequiredService(); diff --git a/test/Sentry.Extensions.Logging.Tests/LoggingTests.cs b/test/Sentry.Extensions.Logging.Tests/LoggingTests.cs index 4e4dd049da..7bf40d2d33 100644 --- a/test/Sentry.Extensions.Logging.Tests/LoggingTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/LoggingTests.cs @@ -24,14 +24,18 @@ public void Log_CapturesEvent(LogLevel logLevel) // Arrange var worker = Substitute.For(); + using var sentry = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = worker; + o.InitNativeSdks = false; + }); + var serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => builder.AddSentry(o => { - o.Dsn = ValidDsn; o.MinimumBreadcrumbLevel = LogLevel.None; o.MinimumEventLevel = logLevel; - o.BackgroundWorker = worker; - o.InitNativeSdks = false; })); serviceCollection.Configure(options => options.AddFilter(CategoryName, LogLevel.None)); using var serviceProvider = serviceCollection.BuildServiceProvider(); @@ -70,14 +74,18 @@ public void Log_AddsBreadcrumb(LogLevel logLevel) // Arrange var worker = Substitute.For(); + using var sentry = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = worker; + o.InitNativeSdks = false; + }); + var serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => builder.AddSentry(o => { - o.Dsn = ValidDsn; o.MinimumBreadcrumbLevel = logLevel; o.MinimumEventLevel = LogLevel.None; - o.BackgroundWorker = worker; - o.InitNativeSdks = false; })); serviceCollection.Configure(options => options.AddFilter(CategoryName, LogLevel.None)); using var serviceProvider = serviceCollection.BuildServiceProvider(); @@ -121,14 +129,18 @@ public void Log_CapturesStructuredLog(LogLevel logLevel) // Arrange var worker = Substitute.For(); + using var sentry = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = worker; + o.InitNativeSdks = false; + }); + var serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => builder.AddSentry(o => { - o.Dsn = ValidDsn; o.MinimumBreadcrumbLevel = LogLevel.None; o.MinimumEventLevel = LogLevel.None; - o.BackgroundWorker = worker; - o.InitNativeSdks = false; })); serviceCollection.Configure(options => options.AddFilter(CategoryName, logLevel)); using var serviceProvider = serviceCollection.BuildServiceProvider(); @@ -164,14 +176,18 @@ public void Log_EventsAndBreadcrumbsIgnoreConfiguration_StructuredLogsRespectCon var envelopes = new List(2); worker.EnqueueEnvelope(Arg.Do(envelope => envelopes.Add(envelope))); + using var sentry = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = worker; + o.InitNativeSdks = false; + }); + var serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => builder.AddSentry(o => { - o.Dsn = ValidDsn; o.MinimumBreadcrumbLevel = LogLevel.Information; o.MinimumEventLevel = LogLevel.Warning; - o.BackgroundWorker = worker; - o.InitNativeSdks = false; })); serviceCollection.Configure(options => options.AddFilter(CategoryName, LogLevel.Error)); using var serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs index 8937cf76e3..057c0b88d7 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs @@ -16,11 +16,7 @@ public void AddSentry_ConfigureScope_HubEnabledTrue_InvokesCallback() .Do(info => info.Arg>()(scope)); _ = SentrySdk.UseHub(hub); - _ = sut.AddSentry(o => - { - o.InitializeSdk = false; // use the mock above - o.ConfigureScope(s => s.Level = expected); - }); + _ = sut.AddSentry(o => o.ConfigureScope(s => s.Level = expected)); Assert.Equal(expected, scope.Level); } @@ -37,57 +33,24 @@ public void AddSentry_ConfigureScope_HubEnabledFalse_DoesNotInvokesCallback() .Do(info => info.Arg>()(scope)); _ = SentrySdk.UseHub(hub); - _ = sut.AddSentry(o => - { - o.InitializeSdk = false; // use the mock above - o.ConfigureScope(s => s.Level = expected); - }); + _ = sut.AddSentry(o => o.ConfigureScope(s => s.Level = expected)); Assert.NotEqual(expected, scope.Level); } [Fact] - public void AddSentry_InitializeSdkFalse_HubAdapter() + public void AddSentry_WithDsn_DoesNotInitializeSdk() { var sut = Substitute.For(); + var hub = Substitute.For(); + _ = hub.IsEnabled.Returns(false); + _ = SentrySdk.UseHub(hub); - _ = sut.AddSentry(o => o.InitializeSdk = false); + _ = sut.AddSentry(o => o.Dsn = ValidDsn); sut.Received(1) .AddProvider(Arg.Is(p => p.Hub == HubAdapter.Instance)); - } - - [Fact] - public void AddSentry_NoDiagnosticSet_MelSet() - { - SentryLoggingOptions options = null; - var sut = Substitute.For(); - _ = sut.AddSentry(o => - { - o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; - o.Debug = true; - options = o; - }); - - _ = Assert.IsType(options.DiagnosticLogger); - } - - [Fact] - public void AddSentry_DiagnosticSet_NoOverriden() - { - SentryLoggingOptions options = null; - var sut = Substitute.For(); - var diagnosticLogger = Substitute.For(); - _ = sut.AddSentry(o => - { - o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; - o.Debug = true; - Assert.Null(o.DiagnosticLogger); - o.DiagnosticLogger = diagnosticLogger; - options = o; - }); - - Assert.Same(diagnosticLogger, options.DiagnosticLogger); + Assert.False(SentrySdk.IsEnabled); } [Fact] diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs index 778215de16..876f387b18 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs @@ -54,8 +54,7 @@ public void Configure_BindsConfigurationToOptions() JsonPreserveReferences = true, MinimumBreadcrumbLevel = LogLevel.Debug, - MinimumEventLevel = LogLevel.Error, - InitializeSdk = true + MinimumEventLevel = LogLevel.Error }; var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary @@ -105,7 +104,6 @@ public void Configure_BindsConfigurationToOptions() ["JsonPreserveReferences"] = expected.JsonPreserveReferences.ToString(), ["MinimumBreadcrumbLevel"] = expected.MinimumBreadcrumbLevel.ToString(), ["MinimumEventLevel"] = expected.MinimumEventLevel.ToString(), - ["InitializeSdk"] = expected.InitializeSdk.ToString(), }) .Build(); @@ -162,7 +160,6 @@ public void Configure_BindsConfigurationToOptions() actual.MinimumBreadcrumbLevel.Should().Be(expected.MinimumBreadcrumbLevel); actual.MinimumEventLevel.Should().Be(expected.MinimumEventLevel); - actual.InitializeSdk.Should().Be(expected.InitializeSdk); } } } diff --git a/test/Sentry.Extensions.Logging.Tests/appsettings.json b/test/Sentry.Extensions.Logging.Tests/appsettings.json index dc995ef4f2..91330bfa64 100644 --- a/test/Sentry.Extensions.Logging.Tests/appsettings.json +++ b/test/Sentry.Extensions.Logging.Tests/appsettings.json @@ -4,7 +4,6 @@ "MaxBreadcrumbs": 150, "Release": "e386dfd", "MinimumBreadcrumbLevel": "Warning", - "MinimumEventLevel": "Critical", - "InitializeSdk": false + "MinimumEventLevel": "Critical" } } From d318a862527a8ecb26f2a3e20589fdb49c171677 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 18 Sep 2026 14:24:41 +1200 Subject: [PATCH 09/42] feat: SentryHostOptions replaces SentryLoggingOptions as the base for framework options SentryLoggingOptions no longer derives from SentryOptions, matching the Serilog and NLog options: it carries only the log levels and entry filters. SentryAspNetCoreOptions, SentryMauiOptions and SentryBlazorOptions now derive from a new abstract SentryHostOptions, which keeps MinimumEventLevel, MinimumBreadcrumbLevel, ConfigureScope and AddLogEntryFilter by passing them through to an inner SentryLoggingOptions, so existing UseSentry callbacks and configuration keys keep working. InitializeSdk is removed. Integrations that initialise through DI call AddSentry; MAUI, which initialises in SentryMauiInitializer, uses an internal non-initialising overload. ConfigureScope callbacks are applied right after the SDK is initialised instead of when the MEL logger provider is built. Also fixes Blazor WebAssembly's logger ignoring the logging settings from UseSentry (it was built from a separate, default IOptions), and structured logs from plain MEL taking default attributes from options the SDK was not initialised with. Co-Authored-By: Claude Opus 5 --- .../SentryStructuredLoggerBenchmarks.cs | 4 +- samples/Sentry.Samples.ME.Logging/Program.cs | 4 +- .../WebAssemblyHostBuilderExtensions.cs | 35 +++-- .../BindableSentryAspNetCoreOptions.cs | 2 +- .../SentryAspNetCoreLoggerProvider.cs | 4 +- .../SentryAspNetCoreOptions.cs | 4 +- ...entryAspNetCoreStructuredLoggerProvider.cs | 9 +- .../BindableSentryHostOptions.cs | 17 +++ .../BindableSentryLoggingOptions.cs | 4 +- .../ServiceCollectionExtensions.cs | 35 +++-- .../LoggingBuilderExtensions.cs | 14 +- .../SentryHostOptions.cs | 45 ++++++ .../SentryHostOptionsSetup.cs | 28 ++++ .../SentryLoggerProvider.cs | 6 - .../SentryLoggingOptions.cs | 23 +-- .../SentryLoggingOptionsExtensions.cs | 10 ++ .../SentryStructuredLogger.cs | 10 +- .../SentryStructuredLoggerProvider.cs | 16 +-- src/Sentry.Maui/BindableSentryMauiOptions.cs | 2 +- .../Internal/SentryMauiInitializer.cs | 2 + .../Internal/SentryMauiLoggerProvider.cs | 4 +- .../SentryMauiStructuredLoggerProvider.cs | 9 +- .../SentryMauiAppBuilderExtensions.cs | 2 +- src/Sentry.Maui/SentryMauiOptions.cs | 2 +- .../WebAssemblyHostBuilderExtensionsTests.cs | 44 ++++++ ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 +- ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 2 +- ...reSentryWebHostBuilder.IntegrationTests.cs | 9 -- .../IntegrationMockedBackgroundWorker.cs | 4 +- .../MiddlewareLoggerIntegration.cs | 6 +- .../SentryAspNetCoreOptionsSetupTests.cs | 6 +- ...AspNetCoreStructuredLoggerProviderTests.cs | 13 +- .../SentryWebHostBuilderExtensionsTests.cs | 6 +- .../SqlListenerTests.verify.cs | 6 +- ...iApprovalTests.Run.DotNet10_0.verified.txt | 14 +- ...iApprovalTests.Run.DotNet11_0.verified.txt | 14 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 14 +- ...piApprovalTests.Run.DotNet9_0.verified.txt | 14 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 14 +- .../ConfigurationOptionsTests.cs | 37 +---- .../SentryLoggerFactoryExtensionsTests.cs | 50 +------ .../SentryLoggingOptionsExtensionsTests.cs | 52 ++++--- .../SentryLoggingOptionsSetupTests.cs | 136 +----------------- .../SentryStructuredLoggerProviderTests.cs | 15 +- .../SentryStructuredLoggerTests.cs | 13 +- .../ServiceCollectionExtensionsTests.cs | 51 +++++++ .../appsettings.json | 3 - .../SentryStartupTests.cs | 4 +- ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 +- ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 +- ...SentryMauiStructuredLoggerProviderTests.cs | 13 +- .../SentryMauiAppBuilderExtensionsTests.cs | 36 +++-- test/Sentry.Tests/SentryOptionsTests.cs | 29 ++++ 55 files changed, 487 insertions(+), 419 deletions(-) create mode 100644 src/Sentry.Extensions.Logging/BindableSentryHostOptions.cs create mode 100644 src/Sentry.Extensions.Logging/SentryHostOptions.cs create mode 100644 src/Sentry.Extensions.Logging/SentryHostOptionsSetup.cs create mode 100644 test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/WebAssemblyHostBuilderExtensionsTests.cs create mode 100644 test/Sentry.Extensions.Logging.Tests/ServiceCollectionExtensionsTests.cs diff --git a/benchmarks/Sentry.Extensions.Logging.Benchmarks/SentryStructuredLoggerBenchmarks.cs b/benchmarks/Sentry.Extensions.Logging.Benchmarks/SentryStructuredLoggerBenchmarks.cs index d7608082df..2ea4c5b9e4 100644 --- a/benchmarks/Sentry.Extensions.Logging.Benchmarks/SentryStructuredLoggerBenchmarks.cs +++ b/benchmarks/Sentry.Extensions.Logging.Benchmarks/SentryStructuredLoggerBenchmarks.cs @@ -16,7 +16,7 @@ public class SentryStructuredLoggerBenchmarks [GlobalSetup] public void Setup() { - SentryLoggingOptions options = new() + SentryOptions options = new() { Dsn = DsnSamples.ValidDsn, }; @@ -34,7 +34,7 @@ public void Setup() }; _hub = new Hub(options, DisabledHub.Instance); - _logger = new SentryStructuredLogger("CategoryName", options, _hub, clock, sdk); + _logger = new SentryStructuredLogger("CategoryName", _hub, clock, sdk); _logRecord = new LogRecord(LogLevel.Information, new EventId(2025, "EventName"), new InvalidOperationException("exception-message"), "Number={Number}, Text={Text}", 2018, "message"); } diff --git a/samples/Sentry.Samples.ME.Logging/Program.cs b/samples/Sentry.Samples.ME.Logging/Program.cs index e027cd7330..a80e29cd67 100644 --- a/samples/Sentry.Samples.ME.Logging/Program.cs +++ b/samples/Sentry.Samples.ME.Logging/Program.cs @@ -25,6 +25,8 @@ }); }); +SentrySdk.ConfigureScope(s => s.SetTag("RootScope", "sent with all events")); + using var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); @@ -36,8 +38,6 @@ // Don't keep as a breadcrumb or send events for messages of level less than Critical with exception of type DivideByZeroException options.AddLogEntryFilter((_, level, _, exception) => level < LogLevel.Critical && exception is DivideByZeroException); - - options.ConfigureScope(s => s.SetTag("RootScope", "sent with all events")); }); // Don't send logs for messages of level less than Warning for category Program builder.AddFilter(typeof(Program).FullName, LogLevel.Warning); diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs index be31fc993a..9c1410e981 100644 --- a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs +++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs @@ -1,9 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Configuration; using Microsoft.Extensions.Options; using Sentry; using Sentry.AspNetCore.Blazor.WebAssembly.Internal; using Sentry.Extensions.Logging; +using Sentry.Extensions.Logging.Extensions.DependencyInjection; +using Sentry.Infrastructure; using Sentry.Internal; // ReSharper disable once CheckNamespace - Discoverability @@ -22,7 +25,15 @@ public static class WebAssemblyHostBuilderExtensions /// public static WebAssemblyHostBuilder UseSentry(this WebAssemblyHostBuilder builder, Action configureOptions) { - builder.Logging.AddSentry(blazorOptions => + builder.Logging.AddSentryBlazor(configureOptions); + return builder; + } + + internal static ILoggingBuilder AddSentryBlazor(this ILoggingBuilder logging, Action configureOptions) + { + logging.AddConfiguration(); + + logging.Services.Configure(blazorOptions => { configureOptions(blazorOptions); @@ -35,21 +46,27 @@ public static WebAssemblyHostBuilder UseSentry(this WebAssemblyHostBuilder build blazorOptions.AddTransactionProcessor(new TraceIgnoreStatusCodeTransactionProcessor(blazorOptions)); }); - builder.Services.AddSingleton, BlazorWasmOptionsSetup>(); + logging.Services.AddSingleton, SentryHostOptionsSetup>(); + logging.Services.AddSingleton, BlazorWasmOptionsSetup>(); - return builder; + logging.Services.AddSingleton(c => new SentryLoggerProvider( + c.GetRequiredService(), + SystemClock.Clock, + c.GetRequiredService>().Value.Logging)); + logging.Services.AddSingleton(c => new SentryStructuredLoggerProvider(c.GetRequiredService())); + logging.Services.AddSentry(); + + logging.AddFilter(_ => true); + logging.AddFilter("Sentry.ISentryClient", LogLevel.None); + + return logging; } } /// /// Sentry Blazor Options /// -public class SentryBlazorOptions : SentryLoggingOptions +public class SentryBlazorOptions : SentryHostOptions { - /// - /// Creates a new instance of . - /// - public SentryBlazorOptions() => InitializeSdk = true; - // Awesome Blazor specific options go here } diff --git a/src/Sentry.AspNetCore/BindableSentryAspNetCoreOptions.cs b/src/Sentry.AspNetCore/BindableSentryAspNetCoreOptions.cs index e98a485d9f..c0e48fdc5a 100644 --- a/src/Sentry.AspNetCore/BindableSentryAspNetCoreOptions.cs +++ b/src/Sentry.AspNetCore/BindableSentryAspNetCoreOptions.cs @@ -8,7 +8,7 @@ namespace Sentry.AspNetCore; /// -internal class BindableSentryAspNetCoreOptions : BindableSentryLoggingOptions +internal class BindableSentryAspNetCoreOptions : BindableSentryHostOptions { public bool? IncludeActivityData { get; set; } public RequestSize? MaxRequestBodySize { get; set; } diff --git a/src/Sentry.AspNetCore/SentryAspNetCoreLoggerProvider.cs b/src/Sentry.AspNetCore/SentryAspNetCoreLoggerProvider.cs index 5ad52c8e9b..dd5955c425 100644 --- a/src/Sentry.AspNetCore/SentryAspNetCoreLoggerProvider.cs +++ b/src/Sentry.AspNetCore/SentryAspNetCoreLoggerProvider.cs @@ -15,12 +15,12 @@ internal sealed class SentryAspNetCoreLoggerProvider : SentryLoggerProvider /// Creates a new instance of /// public SentryAspNetCoreLoggerProvider(IOptions options, IHub hub) - : base(options, hub) + : base(hub, SystemClock.Clock, options.Value.Logging) { } internal SentryAspNetCoreLoggerProvider(SentryAspNetCoreOptions options, IHub hub, ISystemClock clock) - : base(hub, clock, options) + : base(hub, clock, options.Logging) { } } diff --git a/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs b/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs index 92ddc9a39d..e2bd238f9f 100644 --- a/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs +++ b/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs @@ -11,7 +11,7 @@ namespace Sentry.AspNetCore; /// An options class for the ASP.NET Core Sentry integration /// /// -public class SentryAspNetCoreOptions : SentryLoggingOptions +public class SentryAspNetCoreOptions : SentryHostOptions { /// /// Gets or sets a value indicating whether [include System.Diagnostic.Activity data] to events. @@ -108,8 +108,6 @@ public SentryAspNetCoreOptions() { // Don't report Environment.UserName as the user. IsEnvironmentUser = false; - - InitializeSdk = true; } internal void SetEnvironment(IWebHostEnvironment hostingEnvironment) diff --git a/src/Sentry.AspNetCore/SentryAspNetCoreStructuredLoggerProvider.cs b/src/Sentry.AspNetCore/SentryAspNetCoreStructuredLoggerProvider.cs index 42f7cd8cc3..efd242270f 100644 --- a/src/Sentry.AspNetCore/SentryAspNetCoreStructuredLoggerProvider.cs +++ b/src/Sentry.AspNetCore/SentryAspNetCoreStructuredLoggerProvider.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Sentry.Extensions.Logging; using Sentry.Infrastructure; @@ -11,13 +10,13 @@ namespace Sentry.AspNetCore; [ProviderAlias("Sentry")] internal sealed class SentryAspNetCoreStructuredLoggerProvider : SentryStructuredLoggerProvider { - public SentryAspNetCoreStructuredLoggerProvider(IOptions options, IHub hub) - : this(options.Value, hub, SystemClock.Clock, CreateSdkVersion()) + public SentryAspNetCoreStructuredLoggerProvider(IHub hub) + : this(hub, SystemClock.Clock, CreateSdkVersion()) { } - internal SentryAspNetCoreStructuredLoggerProvider(SentryAspNetCoreOptions options, IHub hub, ISystemClock clock, SdkVersion sdk) - : base(options, hub, clock, sdk) + internal SentryAspNetCoreStructuredLoggerProvider(IHub hub, ISystemClock clock, SdkVersion sdk) + : base(hub, clock, sdk) { } diff --git a/src/Sentry.Extensions.Logging/BindableSentryHostOptions.cs b/src/Sentry.Extensions.Logging/BindableSentryHostOptions.cs new file mode 100644 index 0000000000..c0c3482476 --- /dev/null +++ b/src/Sentry.Extensions.Logging/BindableSentryHostOptions.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Logging; + +namespace Sentry.Extensions.Logging; + +/// +internal class BindableSentryHostOptions : BindableSentryOptions +{ + public LogLevel? MinimumBreadcrumbLevel { get; set; } + public LogLevel? MinimumEventLevel { get; set; } + + public void ApplyTo(SentryHostOptions options) + { + base.ApplyTo(options); + options.MinimumBreadcrumbLevel = MinimumBreadcrumbLevel ?? options.MinimumBreadcrumbLevel; + options.MinimumEventLevel = MinimumEventLevel ?? options.MinimumEventLevel; + } +} diff --git a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs index a976890ef2..e7ed493305 100644 --- a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs @@ -2,15 +2,13 @@ namespace Sentry.Extensions.Logging; -/// -internal class BindableSentryLoggingOptions : BindableSentryOptions +internal class BindableSentryLoggingOptions { public LogLevel? MinimumBreadcrumbLevel { get; set; } public LogLevel? MinimumEventLevel { get; set; } public void ApplyTo(SentryLoggingOptions options) { - base.ApplyTo(options); options.MinimumBreadcrumbLevel = MinimumBreadcrumbLevel ?? options.MinimumBreadcrumbLevel; options.MinimumEventLevel = MinimumEventLevel ?? options.MinimumEventLevel; } diff --git a/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs index 2c19f126a0..e12fc8f020 100644 --- a/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Sentry.Extensions.Logging/Extensions/DependencyInjection/ServiceCollectionExtensions.cs @@ -13,30 +13,41 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection; public static class ServiceCollectionExtensions { /// - /// Adds Sentry's services to the + /// Adds Sentry's services to the , initializing Sentry with + /// when the hub is first resolved. /// /// The services. public static IServiceCollection AddSentry(this IServiceCollection services) - where TOptions : SentryLoggingOptions, new() + where TOptions : SentryHostOptions, new() + => services.AddSentry(initializeSdk: true); + + internal static IServiceCollection AddSentry(this IServiceCollection services, bool initializeSdk) + where TOptions : SentryHostOptions, new() { services.TryAddSingleton( c => c.GetRequiredService>().Value); - services.TryAddTransient(c => c.GetRequiredService()); - services.TryAddTransient(c => c.GetRequiredService>()()); - - services.TryAddSingleton>(c => + if (initializeSdk) { - var options = c.GetRequiredService>().Value; - - if (options.InitializeSdk) + services.TryAddSingleton>(c => { + var options = c.GetRequiredService>().Value; var hub = SentrySdk.InitHub(options); SentrySdk.UseHub(hub); - } + options.ApplyConfigureScopeCallbacks(hub); + + return () => HubAdapter.Instance; + }); + } - return () => HubAdapter.Instance; - }); + return services.AddSentryHub(); + } + + internal static IServiceCollection AddSentryHub(this IServiceCollection services) + { + services.TryAddTransient(c => c.GetRequiredService()); + services.TryAddTransient(c => c.GetRequiredService>()()); + services.TryAddSingleton>(_ => () => HubAdapter.Instance); // Custom handler for HttpClientFactory. // Must be singleton: https://github.com/getsentry/sentry-dotnet/issues/785 diff --git a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs index f4993ecf9f..cf7f7264a8 100644 --- a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs +++ b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs @@ -27,12 +27,6 @@ public static ILoggingBuilder AddSentry(this ILoggingBuilder builder) /// The builder. /// The options configuration. public static ILoggingBuilder AddSentry(this ILoggingBuilder builder, Action? optionsConfiguration) - => builder.AddSentry(optionsConfiguration); - - internal static ILoggingBuilder AddSentry( - this ILoggingBuilder builder, - Action? optionsConfiguration) - where TOptions : SentryLoggingOptions, new() { builder.AddConfiguration(); @@ -41,14 +35,14 @@ internal static ILoggingBuilder AddSentry( builder.Services.Configure(optionsConfiguration); } - builder.Services.AddSingleton, SentryLoggingOptionsSetup>(); + builder.Services.AddSingleton, SentryLoggingOptionsSetup>(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSentry(); + builder.Services.AddSentryHub(); // All logs should flow to the SentryLogger, regardless of level. - // Filtering of events is handled in SentryLogger, using SentryOptions.MinimumEventLevel - // Filtering of breadcrumbs is handled in SentryLogger, using SentryOptions.MinimumBreadcrumbLevel + // Filtering of events is handled in SentryLogger, using SentryLoggingOptions.MinimumEventLevel + // Filtering of breadcrumbs is handled in SentryLogger, using SentryLoggingOptions.MinimumBreadcrumbLevel builder.AddFilter(_ => true); // Logs from the SentryLogger should not flow to the SentryStructuredLogger as this may cause recursive invocations. diff --git a/src/Sentry.Extensions.Logging/SentryHostOptions.cs b/src/Sentry.Extensions.Logging/SentryHostOptions.cs new file mode 100644 index 0000000000..b6b86266e4 --- /dev/null +++ b/src/Sentry.Extensions.Logging/SentryHostOptions.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.Logging; + +namespace Sentry.Extensions.Logging; + +/// +/// Options for integrations that initialize Sentry and also send log entries to it, such as ASP.NET Core and MAUI. +/// +/// +public abstract class SentryHostOptions : SentryOptions +{ + internal SentryLoggingOptions Logging { get; } = new(); + + /// + public LogLevel MinimumBreadcrumbLevel + { + get => Logging.MinimumBreadcrumbLevel; + set => Logging.MinimumBreadcrumbLevel = value; + } + + /// + public LogLevel MinimumEventLevel + { + get => Logging.MinimumEventLevel; + set => Logging.MinimumEventLevel = value; + } + + /// + /// Add a callback to configure the scope upon SDK initialization + /// + /// The function to invoke when initializing the SDK + public void ConfigureScope(Action action) => ConfigureScopeCallbacks = ConfigureScopeCallbacks.Concat(new[] { action }).ToArray(); + + /// + /// List of callbacks to be invoked when initializing the SDK + /// + internal Action[] ConfigureScopeCallbacks { get; set; } = Array.Empty>(); + + internal void ApplyConfigureScopeCallbacks(IHub hub) + { + foreach (var callback in ConfigureScopeCallbacks) + { + hub.ConfigureScope(callback); + } + } +} diff --git a/src/Sentry.Extensions.Logging/SentryHostOptionsSetup.cs b/src/Sentry.Extensions.Logging/SentryHostOptionsSetup.cs new file mode 100644 index 0000000000..b08091ae3e --- /dev/null +++ b/src/Sentry.Extensions.Logging/SentryHostOptionsSetup.cs @@ -0,0 +1,28 @@ +#if NET6_0_OR_GREATER +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Configuration; +using Microsoft.Extensions.Options; + +namespace Sentry.Extensions.Logging; + +internal sealed class SentryHostOptionsSetup : IConfigureOptions + where TOptions : SentryHostOptions +{ + private readonly IConfiguration _config; + + public SentryHostOptionsSetup(ILoggerProviderConfiguration config) + { + ArgumentNullException.ThrowIfNull(config); + _config = config.Configuration; + } + + public void Configure(TOptions options) + { + ArgumentNullException.ThrowIfNull(options); + + var bindable = new BindableSentryHostOptions(); + _config.Bind(bindable); + bindable.ApplyTo(options); + } +} +#endif diff --git a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs index 034e7b4694..ddb50d931b 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs @@ -61,12 +61,6 @@ internal SentryLoggerProvider( } } }); - - // Add scope configuration to hub from options - foreach (var callback in options.ConfigureScopeCallbacks) - { - hub.ConfigureScope(callback); - } } } diff --git a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs index a4e9a2075c..cd934d6f68 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs @@ -5,8 +5,11 @@ namespace Sentry.Extensions.Logging; /// /// Sentry logging integration options /// -/// -public class SentryLoggingOptions : SentryOptions +/// +/// These only configure which log entries are sent to Sentry. Sentry itself is initialized separately, with +/// or a framework integration such as UseSentry. +/// +public class SentryLoggingOptions { /// /// Gets or sets the minimum breadcrumb level. @@ -30,24 +33,8 @@ public class SentryLoggingOptions : SentryOptions /// public LogLevel MinimumEventLevel { get; set; } = LogLevel.Error; - /// - /// Add a callback to configure the scope upon SDK initialization - /// - /// The function to invoke when initializing the SDK - public void ConfigureScope(Action action) => ConfigureScopeCallbacks = ConfigureScopeCallbacks.Concat(new[] { action }).ToArray(); - - /// - /// Whether the integration owning these options initializes the SDK - /// - internal bool InitializeSdk { get; set; } - /// /// Log entry filters /// internal ILogEntryFilter[] Filters { get; set; } = Array.Empty(); - - /// - /// List of callbacks to be invoked when initializing the SDK - /// - internal Action[] ConfigureScopeCallbacks { get; set; } = Array.Empty>(); } diff --git a/src/Sentry.Extensions.Logging/SentryLoggingOptionsExtensions.cs b/src/Sentry.Extensions.Logging/SentryLoggingOptionsExtensions.cs index a536de7cb8..a89390b4c7 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggingOptionsExtensions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggingOptionsExtensions.cs @@ -35,4 +35,14 @@ public static void AddLogEntryFilter( this SentryLoggingOptions options, Func filter) => options.AddLogEntryFilter(new DelegateLogEntryFilter(filter)); + + /// + public static void AddLogEntryFilter(this SentryHostOptions options, ILogEntryFilter filter) + => options.Logging.AddLogEntryFilter(filter); + + /// + public static void AddLogEntryFilter( + this SentryHostOptions options, + Func filter) + => options.Logging.AddLogEntryFilter(filter); } diff --git a/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs b/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs index 2b7c2c1aed..6eae15193f 100644 --- a/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs +++ b/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs @@ -7,15 +7,13 @@ namespace Sentry.Extensions.Logging; internal sealed class SentryStructuredLogger : ILogger { private readonly string? _categoryName; - private readonly SentryLoggingOptions _options; private readonly IHub _hub; private readonly ISystemClock _clock; private readonly SdkVersion _sdk; - internal SentryStructuredLogger(string categoryName, SentryLoggingOptions options, IHub hub, ISystemClock clock, SdkVersion sdk) + internal SentryStructuredLogger(string categoryName, IHub hub, ISystemClock clock, SdkVersion sdk) { _categoryName = categoryName; - _options = options; _clock = clock; _hub = hub; _sdk = sdk; @@ -34,7 +32,7 @@ public bool IsEnabled(LogLevel logLevel) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if (!IsEnabled(logLevel)) + if (!IsEnabled(logLevel) || _hub.GetSentryOptions() is not { } options) { return; } @@ -52,7 +50,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except } catch (FormatException e) { - _options.DiagnosticLogger?.LogError(e, "Template string does not match the provided argument. The Log will be dropped."); + options.DiagnosticLogger?.LogError(e, "Template string does not match the provided argument. The Log will be dropped."); return; } @@ -87,7 +85,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except }; var scope = _hub.GetScope(); - log.SetDefaultAttributes(_options, scope, _sdk); + log.SetDefaultAttributes(options, scope, _sdk); log.SetOrigin("auto.log.extensions_logging"); if (_categoryName is not null) diff --git a/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs b/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs index bf74a36da0..b55c95e15f 100644 --- a/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs +++ b/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Sentry.Infrastructure; namespace Sentry.Extensions.Logging; @@ -10,24 +9,17 @@ namespace Sentry.Extensions.Logging; [ProviderAlias("Sentry")] internal class SentryStructuredLoggerProvider : ILoggerProvider { - private readonly SentryLoggingOptions _options; private readonly IHub _hub; private readonly ISystemClock _clock; private readonly SdkVersion _sdk; - public SentryStructuredLoggerProvider(IOptions options, IHub hub) - : this(options.Value, hub, SystemClock.Clock, CreateSdkVersion()) + public SentryStructuredLoggerProvider(IHub hub) + : this(hub, SystemClock.Clock, CreateSdkVersion()) { } - internal SentryStructuredLoggerProvider(IHub hub, ISystemClock clock, SentryLoggingOptions options) - : this(options, hub, clock, CreateSdkVersion()) + internal SentryStructuredLoggerProvider(IHub hub, ISystemClock clock, SdkVersion sdk) { - } - - internal SentryStructuredLoggerProvider(SentryLoggingOptions options, IHub hub, ISystemClock clock, SdkVersion sdk) - { - _options = options; _hub = hub; _clock = clock; _sdk = sdk; @@ -35,7 +27,7 @@ internal SentryStructuredLoggerProvider(SentryLoggingOptions options, IHub hub, public ILogger CreateLogger(string categoryName) { - return new SentryStructuredLogger(categoryName, _options, _hub, _clock, _sdk); + return new SentryStructuredLogger(categoryName, _hub, _clock, _sdk); } public void Dispose() diff --git a/src/Sentry.Maui/BindableSentryMauiOptions.cs b/src/Sentry.Maui/BindableSentryMauiOptions.cs index f4f98e1ad0..3a79156eec 100644 --- a/src/Sentry.Maui/BindableSentryMauiOptions.cs +++ b/src/Sentry.Maui/BindableSentryMauiOptions.cs @@ -3,7 +3,7 @@ namespace Sentry.Maui; /// -internal class BindableSentryMauiOptions : BindableSentryLoggingOptions +internal class BindableSentryMauiOptions : BindableSentryHostOptions { public bool? IncludeTextInBreadcrumbs { get; set; } public bool? IncludeTitleInBreadcrumbs { get; set; } diff --git a/src/Sentry.Maui/Internal/SentryMauiInitializer.cs b/src/Sentry.Maui/Internal/SentryMauiInitializer.cs index 3a59dc4c81..209f3d6948 100644 --- a/src/Sentry.Maui/Internal/SentryMauiInitializer.cs +++ b/src/Sentry.Maui/Internal/SentryMauiInitializer.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Options; +using Sentry.Extensibility; namespace Sentry.Maui.Internal; @@ -14,6 +15,7 @@ public void Initialize(IServiceProvider services) // Initialize the Sentry SDK. var disposable = SentrySdk.Init(options); + options.ApplyConfigureScopeCallbacks(HubAdapter.Instance); // Register the return value from initializing the SDK with the disposer. // This will ensure that it gets disposed when the service provider is disposed. diff --git a/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs b/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs index dddd59bae2..98f0764438 100644 --- a/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs +++ b/src/Sentry.Maui/Internal/SentryMauiLoggerProvider.cs @@ -12,12 +12,12 @@ namespace Sentry.Maui.Internal; internal sealed class SentryMauiLoggerProvider : SentryLoggerProvider { public SentryMauiLoggerProvider(IOptions options, IHub hub) - : base(options, hub) + : base(hub, SystemClock.Clock, options.Value.Logging) { } internal SentryMauiLoggerProvider(SentryMauiOptions options, IHub hub, ISystemClock clock) - : base(hub, clock, options) + : base(hub, clock, options.Logging) { } } diff --git a/src/Sentry.Maui/Internal/SentryMauiStructuredLoggerProvider.cs b/src/Sentry.Maui/Internal/SentryMauiStructuredLoggerProvider.cs index a72ffafc46..79087cddd5 100644 --- a/src/Sentry.Maui/Internal/SentryMauiStructuredLoggerProvider.cs +++ b/src/Sentry.Maui/Internal/SentryMauiStructuredLoggerProvider.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Sentry.Extensions.Logging; using Sentry.Infrastructure; @@ -11,13 +10,13 @@ namespace Sentry.Maui.Internal; [ProviderAlias("Sentry")] internal sealed class SentryMauiStructuredLoggerProvider : SentryStructuredLoggerProvider { - public SentryMauiStructuredLoggerProvider(IOptions options, IHub hub) - : this(options.Value, hub, SystemClock.Clock, CreateSdkVersion()) + public SentryMauiStructuredLoggerProvider(IHub hub) + : this(hub, SystemClock.Clock, CreateSdkVersion()) { } - internal SentryMauiStructuredLoggerProvider(SentryMauiOptions options, IHub hub, ISystemClock clock, SdkVersion sdk) - : base(options, hub, clock, sdk) + internal SentryMauiStructuredLoggerProvider(IHub hub, ISystemClock clock, SdkVersion sdk) + : base(hub, clock, sdk) { } diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index 579c2b63e6..647ae61756 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -78,7 +78,7 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, // This is ultimately the class that enables all the MauiElementEventBinders above services.TryAddSingleton(); - services.AddSentry(); + services.AddSentry(initializeSdk: false); builder.RegisterMauiEventsBinder(); diff --git a/src/Sentry.Maui/SentryMauiOptions.cs b/src/Sentry.Maui/SentryMauiOptions.cs index 17038bf747..5c436afab0 100644 --- a/src/Sentry.Maui/SentryMauiOptions.cs +++ b/src/Sentry.Maui/SentryMauiOptions.cs @@ -7,7 +7,7 @@ namespace Sentry.Maui; /// /// Sentry MAUI integration options /// -public class SentryMauiOptions : SentryLoggingOptions +public class SentryMauiOptions : SentryHostOptions { /// /// Creates a new instance of . diff --git a/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/WebAssemblyHostBuilderExtensionsTests.cs b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/WebAssemblyHostBuilderExtensionsTests.cs new file mode 100644 index 0000000000..6568883fd8 --- /dev/null +++ b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/WebAssemblyHostBuilderExtensionsTests.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Sentry.AspNetCore.Blazor.WebAssembly.Tests; + +public class WebAssemblyHostBuilderExtensionsTests : IDisposable +{ + private readonly List _events = new(); + + public void Dispose() => SentrySdk.Close(); + + private ServiceProvider GetSut(Action configureOptions) + { + var services = new ServiceCollection(); + services.AddSingleton(new FakeNavigationManager()); + services.AddLogging(logging => logging.AddSentryBlazor(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = Substitute.For(); + o.AutoSessionTracking = false; + o.SetBeforeSend((e, _) => + { + _events.Add(e); + return null; + }); + configureOptions(o); + })); + return services.BuildServiceProvider(); + } + + [Fact] + public void UseSentry_MinimumEventLevel_AppliesToLogger() + { + using var provider = GetSut(o => o.MinimumEventLevel = LogLevel.Critical); + var logger = provider.GetRequiredService().CreateLogger("test_category"); + + logger.LogError("below the configured level"); + logger.LogCritical("at the configured level"); + + _events.Should().ContainSingle().Which.Message!.Message.Should().Be("at the configured level"); + } +} diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index f6ceb2a3c3..463241570b 100644 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -39,7 +39,7 @@ namespace Sentry.AspNetCore public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryAspNetCoreOptions() { } public bool AdjustStandardEnvironmentNameCasing { get; set; } diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index f6ceb2a3c3..463241570b 100644 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -39,7 +39,7 @@ namespace Sentry.AspNetCore public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryAspNetCoreOptions() { } public bool AdjustStandardEnvironmentNameCasing { get; set; } diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index f6ceb2a3c3..463241570b 100644 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -39,7 +39,7 @@ namespace Sentry.AspNetCore public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryAspNetCoreOptions() { } public bool AdjustStandardEnvironmentNameCasing { get; set; } diff --git a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index f6ceb2a3c3..463241570b 100644 --- a/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.AspNetCore.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -39,7 +39,7 @@ namespace Sentry.AspNetCore public static string? TryGetHttpPath(this Sentry.TransactionSamplingContext samplingContext) { } public static string? TryGetHttpRoute(this Sentry.TransactionSamplingContext samplingContext) { } } - public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryAspNetCoreOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryAspNetCoreOptions() { } public bool AdjustStandardEnvironmentNameCasing { get; set; } diff --git a/test/Sentry.AspNetCore.Tests/AspNetCoreSentryWebHostBuilder.IntegrationTests.cs b/test/Sentry.AspNetCore.Tests/AspNetCoreSentryWebHostBuilder.IntegrationTests.cs index 455bf8c0a2..3c38c2471c 100644 --- a/test/Sentry.AspNetCore.Tests/AspNetCoreSentryWebHostBuilder.IntegrationTests.cs +++ b/test/Sentry.AspNetCore.Tests/AspNetCoreSentryWebHostBuilder.IntegrationTests.cs @@ -46,13 +46,4 @@ public void UseSentry_DisableDsnString_DisabledSdk() Assert.False(SentrySdk.IsEnabled); } - - [Fact] - public void UseSentry_OptionsNotInitializeSdk_DisabledSdk() - { - _ = _webHostBuilder.UseSentry(o => o.InitializeSdk = false) - .Build(); - - Assert.False(SentrySdk.IsEnabled); - } } diff --git a/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs b/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs index 8ef5288078..12abc4aca3 100644 --- a/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs +++ b/test/Sentry.AspNetCore.Tests/IntegrationMockedBackgroundWorker.cs @@ -37,7 +37,7 @@ public IntegrationMockedBackgroundWorker(ITestOutputHelper output) [Fact] public async Task DisabledSdk_UnhandledException_NoEventCaptured() { - Configure = o => o.InitializeSdk = false; + Configure = o => o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; Build(); _ = await HttpClient.GetAsync("/throw"); @@ -49,7 +49,7 @@ public async Task DisabledSdk_UnhandledException_NoEventCaptured() [Fact] public void DisabledSdk_WithLogger_NoEventCaptured() { - Configure = o => o.InitializeSdk = false; + Configure = o => o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; Build(); var logger = ServiceProvider.GetRequiredService>(); diff --git a/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs b/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs index d869be9ff9..fc9d1df61e 100644 --- a/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs +++ b/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs @@ -35,11 +35,7 @@ private class Fixture : IDisposable public Fixture() { HubAccessor = () => Hub; - var loggingOptions = new SentryLoggingOptions - { - InitializeSdk = false, - }; - loggingOptions.InitializeSdk = false; + var loggingOptions = new SentryLoggingOptions(); Client.When(client => client.CaptureEvent(Arg.Any(), Arg.Any(), Arg.Any())) .Do(callback => callback.Arg().Evaluate()); diff --git a/test/Sentry.AspNetCore.Tests/SentryAspNetCoreOptionsSetupTests.cs b/test/Sentry.AspNetCore.Tests/SentryAspNetCoreOptionsSetupTests.cs index 916b2a615d..3aa2722d81 100644 --- a/test/Sentry.AspNetCore.Tests/SentryAspNetCoreOptionsSetupTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryAspNetCoreOptionsSetupTests.cs @@ -49,7 +49,7 @@ public void Filters_KestrelApplicationEvent_NoException_Filtered() sut.Configure(_target); //Assert - Assert.Contains(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, null)); + Assert.Contains(_target.Logging.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, null)); } [Fact] @@ -62,7 +62,7 @@ public void Filters_KestrelApplicationEvent_WithException_Filtered() sut.Configure(_target); // Assert - Assert.Contains(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, new Exception())); + Assert.Contains(_target.Logging.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, new Exception())); } [Fact] @@ -75,7 +75,7 @@ public void Filters_KestrelEventId1_WithException_NotFiltered() sut.Configure(_target); // Assert - Assert.DoesNotContain(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Trace, 1, null)); + Assert.DoesNotContain(_target.Logging.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Trace, 1, null)); } [Theory] diff --git a/test/Sentry.AspNetCore.Tests/SentryAspNetCoreStructuredLoggerProviderTests.cs b/test/Sentry.AspNetCore.Tests/SentryAspNetCoreStructuredLoggerProviderTests.cs index 18d3092333..5ce57761c3 100644 --- a/test/Sentry.AspNetCore.Tests/SentryAspNetCoreStructuredLoggerProviderTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryAspNetCoreStructuredLoggerProviderTests.cs @@ -6,21 +6,18 @@ namespace Sentry.AspNetCore.Tests; -public class SentryAspNetCoreStructuredLoggerProviderTests +public class SentryAspNetCoreStructuredLoggerProviderTests : IDisposable { private class Fixture { - public IOptions Options { get; } public IHub Hub { get; } public MockClock Clock { get; } public SdkVersion Sdk { get; } public Fixture() { - var loggingOptions = new SentryAspNetCoreOptions(); - - Options = Microsoft.Extensions.Options.Options.Create(loggingOptions); Hub = Substitute.For(); + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions(); Clock = new MockClock(); Sdk = new SdkVersion { @@ -33,12 +30,14 @@ public Fixture() public SentryAspNetCoreStructuredLoggerProvider GetSut() { - return new SentryAspNetCoreStructuredLoggerProvider(Options.Value, Hub, Clock, Sdk); + return new SentryAspNetCoreStructuredLoggerProvider(Hub, Clock, Sdk); } } private readonly Fixture _fixture = new(); + public void Dispose() => SentryClientExtensions.SentryOptionsForTestingOnly = null; + [Fact] public void Type_CustomAttributes_HasProviderAliasAttribute() { @@ -55,7 +54,6 @@ public void Ctor_DependencyInjection_CanCreate() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); @@ -84,7 +82,6 @@ public void CreateLogger_DependencyInjection_CanLog() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); diff --git a/test/Sentry.AspNetCore.Tests/SentryWebHostBuilderExtensionsTests.cs b/test/Sentry.AspNetCore.Tests/SentryWebHostBuilderExtensionsTests.cs index 6ae6378710..a9c8d9bd29 100644 --- a/test/Sentry.AspNetCore.Tests/SentryWebHostBuilderExtensionsTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryWebHostBuilderExtensionsTests.cs @@ -61,7 +61,7 @@ public void UseSentry_DisableDsnString_ServicesRegistered(Action assert) { - _ = WebHostBuilder.UseSentry(o => o.InitializeSdk = false); + _ = WebHostBuilder.UseSentry(o => o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue); assert(Services); } @@ -88,7 +88,7 @@ public void UseSentry_Logging_AddLoggerProviders() #endif WebHostBuilder.UseSentry((SentryAspNetCoreOptions options) => { - options.InitializeSdk = false; + options.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; }); using var serviceProvider = Services.BuildServiceProvider(); @@ -104,7 +104,7 @@ public void UseSentry_Logging_AddLoggerFilterRules() { WebHostBuilder.UseSentry((SentryAspNetCoreOptions options) => { - options.InitializeSdk = false; + options.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; }); using var serviceProvider = Services.BuildServiceProvider(); diff --git a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs index b9f4250999..20fa73ed45 100644 --- a/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs +++ b/test/Sentry.DiagnosticSource.IntegrationTests/SqlListenerTests.verify.cs @@ -62,7 +62,7 @@ public async Task LoggingAsync() Skip.If(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); var transport = new RecordingTransport(); - void ApplyOptions(SentryLoggingOptions sentryOptions) + void ApplyOptions(SentryOptions sentryOptions) { sentryOptions.AttachStacktrace = false; sentryOptions.TracesSampleRate = 1; @@ -72,7 +72,7 @@ void ApplyOptions(SentryLoggingOptions sentryOptions) sentryOptions.Debug = true; } - var options = new SentryLoggingOptions(); + var options = new SentryOptions(); ApplyOptions(options); await using var database = await _fixture.SqlInstance.Build(); @@ -87,7 +87,7 @@ void ApplyOptions(SentryLoggingOptions sentryOptions) await dbContext.SaveChangesAsync(); } - var loggerFactory = LoggerFactory.Create(_ => _.AddSentry(ApplyOptions)); + var loggerFactory = LoggerFactory.Create(_ => _.AddSentry()); using (var hub = new Hub(options)) { var transaction = hub.StartTransaction("my transaction", "my operation"); diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 2d06a96f0f..26b02c5b85 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -27,15 +27,23 @@ namespace Sentry.Extensions.Logging public bool IsEnabled(Sentry.SentryLevel level) { } public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } } - public class SentryLoggingOptions : Sentry.SentryOptions + public abstract class SentryHostOptions : Sentry.SentryOptions { - public SentryLoggingOptions() { } + protected SentryHostOptions() { } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } } + public class SentryLoggingOptions + { + public SentryLoggingOptions() { } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + } public static class SentryLoggingOptionsExtensions { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, System.Func filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } } @@ -45,6 +53,6 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection public static class ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + where TOptions : Sentry.Extensions.Logging.SentryHostOptions, new () { } } } \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 2d06a96f0f..26b02c5b85 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -27,15 +27,23 @@ namespace Sentry.Extensions.Logging public bool IsEnabled(Sentry.SentryLevel level) { } public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } } - public class SentryLoggingOptions : Sentry.SentryOptions + public abstract class SentryHostOptions : Sentry.SentryOptions { - public SentryLoggingOptions() { } + protected SentryHostOptions() { } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } } + public class SentryLoggingOptions + { + public SentryLoggingOptions() { } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + } public static class SentryLoggingOptionsExtensions { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, System.Func filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } } @@ -45,6 +53,6 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection public static class ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + where TOptions : Sentry.Extensions.Logging.SentryHostOptions, new () { } } } \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 2d06a96f0f..26b02c5b85 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -27,15 +27,23 @@ namespace Sentry.Extensions.Logging public bool IsEnabled(Sentry.SentryLevel level) { } public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } } - public class SentryLoggingOptions : Sentry.SentryOptions + public abstract class SentryHostOptions : Sentry.SentryOptions { - public SentryLoggingOptions() { } + protected SentryHostOptions() { } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } } + public class SentryLoggingOptions + { + public SentryLoggingOptions() { } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + } public static class SentryLoggingOptionsExtensions { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, System.Func filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } } @@ -45,6 +53,6 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection public static class ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + where TOptions : Sentry.Extensions.Logging.SentryHostOptions, new () { } } } \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 2d06a96f0f..26b02c5b85 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -27,15 +27,23 @@ namespace Sentry.Extensions.Logging public bool IsEnabled(Sentry.SentryLevel level) { } public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } } - public class SentryLoggingOptions : Sentry.SentryOptions + public abstract class SentryHostOptions : Sentry.SentryOptions { - public SentryLoggingOptions() { } + protected SentryHostOptions() { } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } } + public class SentryLoggingOptions + { + public SentryLoggingOptions() { } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + } public static class SentryLoggingOptionsExtensions { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, System.Func filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } } @@ -45,6 +53,6 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection public static class ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + where TOptions : Sentry.Extensions.Logging.SentryHostOptions, new () { } } } \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 2d06a96f0f..26b02c5b85 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -27,15 +27,23 @@ namespace Sentry.Extensions.Logging public bool IsEnabled(Sentry.SentryLevel level) { } public void Log(Sentry.SentryLevel logLevel, string message, System.Exception? exception = null, params object?[] args) { } } - public class SentryLoggingOptions : Sentry.SentryOptions + public abstract class SentryHostOptions : Sentry.SentryOptions { - public SentryLoggingOptions() { } + protected SentryHostOptions() { } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } public void ConfigureScope(System.Action action) { } } + public class SentryLoggingOptions + { + public SentryLoggingOptions() { } + public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } + public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } + } public static class SentryLoggingOptionsExtensions { + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } + public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryHostOptions options, System.Func filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, Sentry.Extensions.Logging.ILogEntryFilter filter) { } public static void AddLogEntryFilter(this Sentry.Extensions.Logging.SentryLoggingOptions options, System.Func filter) { } } @@ -45,6 +53,6 @@ namespace Sentry.Extensions.Logging.Extensions.DependencyInjection public static class ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSentry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) - where TOptions : Sentry.Extensions.Logging.SentryLoggingOptions, new () { } + where TOptions : Sentry.Extensions.Logging.SentryHostOptions, new () { } } } \ No newline at end of file diff --git a/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs b/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs index 969ffc4d73..4a6fcc815e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/ConfigurationOptionsTests.cs @@ -29,12 +29,7 @@ public IServiceProvider GetSut() { var configuration = Builder.Build(); var services = new ServiceCollection(); - _ = services.AddLogging(builder => builder.AddConfiguration(configuration).AddSentry(o => - { - o.BackgroundWorker = Substitute.For(); - o.InitNativeSdks = false; - o.AutoSessionTracking = false; - })); + _ = services.AddLogging(builder => builder.AddConfiguration(configuration).AddSentry()); return services.BuildServiceProvider(); } } @@ -54,36 +49,6 @@ public void SentryLoggingOptions_ValuesFromAppSettings() } } - [Fact] - public void SentryOptions_ValuesFromAppSettings() - { - var provider = _fixture.GetSut(); - var sentryLoggingOptions = provider.GetRequiredService>().Value; - - Assert.Equal(150, sentryLoggingOptions.MaxBreadcrumbs); - Assert.Equal("e386dfd", sentryLoggingOptions.Release); - Assert.Equal(ValidDsn, sentryLoggingOptions.Dsn); - } - - [Fact] - public void SentryOptions_DefaultTags_ValuesApplied() - { - const string expectedKey = "expected_key"; - const string expectedValue = "expected value"; - var dict = new Dictionary - { - {"Sentry:DefaultTags:" + expectedKey, expectedValue}, - }; - - _ = _fixture.Builder.AddInMemoryCollection(dict); - - var provider = _fixture.GetSut(); - var sentryLoggingOptions = provider.GetRequiredService>().Value; - - sentryLoggingOptions.DefaultTags.Should().ContainKey(expectedKey); - sentryLoggingOptions.DefaultTags[expectedKey].Should().Be(expectedValue); - } - [Fact] public void SentryLoggerProvider_ResolvedFromILoggerProvider() { diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs index 057c0b88d7..09191fd0e3 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggerFactoryExtensionsTests.cs @@ -5,52 +5,14 @@ namespace Sentry.Extensions.Logging.Tests; public class SentryLoggerFactoryExtensionsTests { [Fact] - public void AddSentry_ConfigureScope_HubEnabledTrue_InvokesCallback() + public void AddSentry_ProviderUsesHubAdapter() { - const SentryLevel expected = SentryLevel.Debug; var sut = Substitute.For(); - var hub = Substitute.For(); - _ = hub.IsEnabled.Returns(true); - var scope = new Scope(new SentryOptions()); - hub.When(w => w.ConfigureScope(Arg.Any>())) - .Do(info => info.Arg>()(scope)); - _ = SentrySdk.UseHub(hub); - _ = sut.AddSentry(o => o.ConfigureScope(s => s.Level = expected)); - - Assert.Equal(expected, scope.Level); - } - - [Fact] - public void AddSentry_ConfigureScope_HubEnabledFalse_DoesNotInvokesCallback() - { - const SentryLevel expected = SentryLevel.Debug; - var sut = Substitute.For(); - var hub = Substitute.For(); - _ = hub.IsEnabled.Returns(false); - var scope = new Scope(new SentryOptions()); - hub.When(w => w.ConfigureScope(Arg.Any>())) - .Do(info => info.Arg>()(scope)); - _ = SentrySdk.UseHub(hub); - - _ = sut.AddSentry(o => o.ConfigureScope(s => s.Level = expected)); - - Assert.NotEqual(expected, scope.Level); - } - - [Fact] - public void AddSentry_WithDsn_DoesNotInitializeSdk() - { - var sut = Substitute.For(); - var hub = Substitute.For(); - _ = hub.IsEnabled.Returns(false); - _ = SentrySdk.UseHub(hub); - - _ = sut.AddSentry(o => o.Dsn = ValidDsn); + _ = sut.AddSentry(); sut.Received(1) .AddProvider(Arg.Is(p => p.Hub == HubAdapter.Instance)); - Assert.False(SentrySdk.IsEnabled); } [Fact] @@ -60,7 +22,7 @@ public void AddSentry_WithOptionsCallback_CallbackInvoked() var expected = Substitute.For(); _ = expected.AddSentry(o => { - o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; + o.MinimumEventLevel = LogLevel.Critical; callbackInvoked = true; }); @@ -81,7 +43,7 @@ public void AddSentry_NoOptionsDelegate_ProviderAdded() public void AddSentry_ReturnsSameFactory() { var expected = Substitute.For(); - var actual = expected.AddSentry(o => o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue); + var actual = expected.AddSentry(o => o.MinimumEventLevel = LogLevel.Critical); Assert.Same(expected, actual); } @@ -90,7 +52,7 @@ public void AddSentry_ReturnsSameFactory() public void AddSentry_ConfigureOptionsOverload_ReturnsSameFactory() { var expected = Substitute.For(); - var actual = expected.AddSentry(o => o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue); + var actual = expected.AddSentry(o => o.MinimumEventLevel = LogLevel.Critical); Assert.Same(expected, actual); } @@ -103,7 +65,7 @@ public void AddSentry_ConfigureOptionsOverload_InvokesCallback() var invoked = false; _ = expected.AddSentry(o => { - o.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; + o.MinimumEventLevel = LogLevel.Critical; Assert.NotNull(o); invoked = true; }); diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsExtensionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsExtensionsTests.cs index 9671860170..d4cb540b6b 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsExtensionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsExtensionsTests.cs @@ -1,33 +1,53 @@ +using Microsoft.Extensions.Logging; + namespace Sentry.Extensions.Logging.Tests; public class SentryLoggingOptionsExtensionsTests { - private readonly SentryLoggingOptions _sut = new(); + private class TestHostOptions : SentryHostOptions; + + [Fact] + public void AddLogEntryFilter_LoggingOptions_AddsFilter() + { + var sut = new SentryLoggingOptions(); + var filter = Substitute.For(); + + sut.AddLogEntryFilter(filter); + + sut.Filters.Should().ContainSingle().Which.Should().BeSameAs(filter); + } [Fact] - public void ApplyDefaultTags_TagInEvent_DoesNotOverrideTag() + public void AddLogEntryFilter_HostOptions_AddsFilterToLoggingOptions() { - const string key = "key"; - const string expected = "event tag value"; - var target = new SentryEvent(); - target.SetTag(key, expected); - _sut.DefaultTags[key] = "default value"; + var sut = new TestHostOptions(); + var filter = Substitute.For(); - _sut.ApplyDefaultTags(target); + sut.AddLogEntryFilter(filter); - Assert.Equal(expected, target.Tags[key]); + sut.Logging.Filters.Should().ContainSingle().Which.Should().BeSameAs(filter); } [Fact] - public void ApplyDefaultTags_TagNotInEvent_AppliesTag() + public void AddLogEntryFilter_HostOptionsDelegate_AddsFilterToLoggingOptions() { - const string key = "key"; - const string expected = "default tag value"; - var target = new SentryEvent(); - _sut.DefaultTags[key] = expected; + var sut = new TestHostOptions(); + + sut.AddLogEntryFilter((_, _, _, _) => true); + + sut.Logging.Filters.Should().ContainSingle().Which.Should().BeOfType(); + } - _sut.ApplyDefaultTags(target); + [Fact] + public void MinimumLevels_HostOptions_PassThroughToLoggingOptions() + { + var sut = new TestHostOptions + { + MinimumBreadcrumbLevel = LogLevel.Debug, + MinimumEventLevel = LogLevel.Critical, + }; - Assert.Equal(expected, target.Tags[key]); + sut.Logging.MinimumBreadcrumbLevel.Should().Be(LogLevel.Debug); + sut.Logging.MinimumEventLevel.Should().Be(LogLevel.Critical); } } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs index 876f387b18..1c7df8cf07 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsSetupTests.cs @@ -10,100 +10,11 @@ public class SentryLoggingOptionsSetupTests public void Configure_BindsConfigurationToOptions() { // Arrange - var expected = new SentryLoggingOptions - { - IsGlobalModeEnabled = true, - EnableScopeSync = true, - TagFilters = new List { "tag1", "tag2" }, - SendDefaultPii = true, - IsEnvironmentUser = true, - ServerName = "FakeServerName", - AttachStacktrace = true, - MaxBreadcrumbs = 7, - SampleRate = 0.7f, - Release = "FakeRelease", - Distribution = "FakeDistribution", - Environment = "Test", - Dsn = "https://d4d82fc1c2c4032a83f3a29aa3a3aff@fake-sentry.io:65535/2147483647", - MaxQueueItems = 8, - MaxCacheItems = 9, - ShutdownTimeout = TimeSpan.FromSeconds(13), - FlushTimeout = TimeSpan.FromSeconds(17), - DecompressionMethods = DecompressionMethods.GZip | DecompressionMethods.Deflate, - RequestBodyCompressionLevel = CompressionLevel.Fastest, - RequestBodyCompressionBuffered = true, - SendClientReports = true, - Debug = true, - DiagnosticLevel = SentryLevel.Warning, - ReportAssembliesMode = ReportAssembliesMode.InformationalVersion, - DeduplicateMode = DeduplicateMode.AggregateException, - CacheDirectoryPath = "~/test", - CaptureFailedRequests = true, - // FailedRequestStatusCodes = IList, - FailedRequestTargets = ["target1", "target2"], - InitCacheFlushTimeout = TimeSpan.FromSeconds(27), - // DefaultTags = Dictionary, - TracesSampleRate = 0.8f, - TracePropagationTargets = new List { "target3", "target4" }, - StackTraceMode = StackTraceMode.Enhanced, - MaxAttachmentSize = 21478, - DetectStartupTime = StartupTimeDetectionMode.Fast, - AutoSessionTrackingInterval = TimeSpan.FromHours(3), - AutoSessionTracking = true, - UseAsyncFileIO = true, - JsonPreserveReferences = true, - - MinimumBreadcrumbLevel = LogLevel.Debug, - MinimumEventLevel = LogLevel.Error - }; var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { - ["IsGlobalModeEnabled"] = expected.IsGlobalModeEnabled.ToString(), - ["EnableScopeSync"] = expected.EnableScopeSync.ToString(), - ["TagFilters:0"] = expected.TagFilters.First().ToString(), - ["TagFilters:1"] = expected.TagFilters.Last().ToString(), - ["SendDefaultPii"] = expected.SendDefaultPii.ToString(), - ["IsEnvironmentUser"] = expected.IsEnvironmentUser.ToString(), - ["ServerName"] = expected.ServerName, - ["AttachStacktrace"] = expected.AttachStacktrace.ToString(), - ["MaxBreadcrumbs"] = expected.MaxBreadcrumbs.ToString(), - ["SampleRate"] = expected.SampleRate.Value.ToString(CultureInfo.InvariantCulture), - ["Release"] = expected.Release, - ["Distribution"] = expected.Distribution, - ["Environment"] = expected.Environment, - ["Dsn"] = expected.Dsn, - ["MaxQueueItems"] = expected.MaxQueueItems.ToString(), - ["MaxCacheItems"] = expected.MaxCacheItems.ToString(), - ["ShutdownTimeout"] = expected.ShutdownTimeout.ToString(), - ["FlushTimeout"] = expected.FlushTimeout.ToString(), - ["DecompressionMethods"] = expected.DecompressionMethods.ToString(), - ["RequestBodyCompressionLevel"] = expected.RequestBodyCompressionLevel.ToString(), - ["RequestBodyCompressionBuffered"] = expected.RequestBodyCompressionBuffered.ToString(), - ["SendClientReports"] = expected.SendClientReports.ToString(), - ["Debug"] = expected.Debug.ToString(), - ["DiagnosticLevel"] = expected.DiagnosticLevel.ToString(), - ["ReportAssembliesMode"] = expected.ReportAssembliesMode.ToString(), - ["DeduplicateMode"] = expected.DeduplicateMode.ToString(), - ["CacheDirectoryPath"] = expected.CacheDirectoryPath.ToString(), - ["CaptureFailedRequests"] = expected.CaptureFailedRequests.ToString(), - ["FailedRequestStatusCodes"] = expected.FailedRequestStatusCodes.ToString(), - ["FailedRequestTargets:0"] = expected.FailedRequestTargets.First().ToString(), - ["FailedRequestTargets:1"] = expected.FailedRequestTargets.Last().ToString(), - ["InitCacheFlushTimeout"] = expected.InitCacheFlushTimeout.ToString(), - ["DefaultTags"] = expected.DefaultTags.ToString(), - ["TracesSampleRate"] = expected.TracesSampleRate.Value.ToString(CultureInfo.InvariantCulture), - ["TracePropagationTargets:0"] = expected.TracePropagationTargets.First().ToString(), - ["TracePropagationTargets:1"] = expected.TracePropagationTargets.Last().ToString(), - ["StackTraceMode"] = expected.StackTraceMode.ToString(), - ["MaxAttachmentSize"] = expected.MaxAttachmentSize.ToString(), - ["DetectStartupTime"] = expected.DetectStartupTime.ToString(), - ["AutoSessionTrackingInterval"] = expected.AutoSessionTrackingInterval.ToString(), - ["AutoSessionTracking"] = expected.AutoSessionTracking.ToString(), - ["UseAsyncFileIO"] = expected.UseAsyncFileIO.ToString(), - ["JsonPreserveReferences"] = expected.JsonPreserveReferences.ToString(), - ["MinimumBreadcrumbLevel"] = expected.MinimumBreadcrumbLevel.ToString(), - ["MinimumEventLevel"] = expected.MinimumEventLevel.ToString(), + ["MinimumBreadcrumbLevel"] = nameof(LogLevel.Debug), + ["MinimumEventLevel"] = nameof(LogLevel.Critical), }) .Build(); @@ -119,47 +30,8 @@ public void Configure_BindsConfigurationToOptions() // Assert using (new AssertionScope()) { - actual.IsGlobalModeEnabled.Should().Be(expected.IsGlobalModeEnabled); - actual.EnableScopeSync.Should().Be(expected.EnableScopeSync); - actual.TagFilters.Should().BeEquivalentTo(expected.TagFilters); - actual.SendDefaultPii.Should().Be(expected.SendDefaultPii); - actual.IsEnvironmentUser.Should().Be(expected.IsEnvironmentUser); - actual.ServerName.Should().Be(expected.ServerName); - actual.AttachStacktrace.Should().Be(expected.AttachStacktrace); - actual.MaxBreadcrumbs.Should().Be(expected.MaxBreadcrumbs); - actual.SampleRate.Should().Be(expected.SampleRate); - actual.Release.Should().Be(expected.Release); - actual.Distribution.Should().Be(expected.Distribution); - actual.Environment.Should().Be(expected.Environment); - actual.Dsn.Should().Be(expected.Dsn); - actual.MaxQueueItems.Should().Be(expected.MaxQueueItems); - actual.MaxCacheItems.Should().Be(expected.MaxCacheItems); - actual.ShutdownTimeout.Should().Be(expected.ShutdownTimeout); - actual.FlushTimeout.Should().Be(expected.FlushTimeout); - actual.DecompressionMethods.Should().Be(expected.DecompressionMethods); - actual.RequestBodyCompressionLevel.Should().Be(expected.RequestBodyCompressionLevel); - actual.RequestBodyCompressionBuffered.Should().Be(expected.RequestBodyCompressionBuffered); - actual.SendClientReports.Should().Be(expected.SendClientReports); - actual.Debug.Should().Be(expected.Debug); - actual.DiagnosticLevel.Should().Be(expected.DiagnosticLevel); - actual.ReportAssembliesMode.Should().Be(expected.ReportAssembliesMode); - actual.DeduplicateMode.Should().Be(expected.DeduplicateMode); - actual.CacheDirectoryPath.Should().Be(expected.CacheDirectoryPath); - actual.CaptureFailedRequests.Should().Be(expected.CaptureFailedRequests); - actual.FailedRequestTargets.Should().BeEquivalentTo(expected.FailedRequestTargets); - actual.InitCacheFlushTimeout.Should().Be(expected.InitCacheFlushTimeout); - actual.TracesSampleRate.Should().Be(expected.TracesSampleRate); - actual.TracePropagationTargets.Should().BeEquivalentTo(expected.TracePropagationTargets); - actual.StackTraceMode.Should().Be(expected.StackTraceMode); - actual.MaxAttachmentSize.Should().Be(expected.MaxAttachmentSize); - actual.DetectStartupTime.Should().Be(expected.DetectStartupTime); - actual.AutoSessionTrackingInterval.Should().Be(expected.AutoSessionTrackingInterval); - actual.AutoSessionTracking.Should().Be(expected.AutoSessionTracking); - actual.UseAsyncFileIO.Should().Be(expected.UseAsyncFileIO); - actual.JsonPreserveReferences.Should().Be(expected.JsonPreserveReferences); - - actual.MinimumBreadcrumbLevel.Should().Be(expected.MinimumBreadcrumbLevel); - actual.MinimumEventLevel.Should().Be(expected.MinimumEventLevel); + actual.MinimumBreadcrumbLevel.Should().Be(LogLevel.Debug); + actual.MinimumEventLevel.Should().Be(LogLevel.Critical); } } } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs index c8d797244e..fd9b7c88d1 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs @@ -6,22 +6,19 @@ namespace Sentry.Extensions.Logging.Tests; -public class SentryStructuredLoggerProviderTests +public class SentryStructuredLoggerProviderTests : IDisposable { private class Fixture { - public IOptions Options { get; } public IHub Hub { get; } public MockClock Clock { get; } public SdkVersion Sdk { get; } public Fixture() { - var loggingOptions = new SentryLoggingOptions(); - - Options = Microsoft.Extensions.Options.Options.Create(loggingOptions); Hub = Substitute.For(); - Hub.SubstituteConfigureScope(new Scope(loggingOptions)); + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions(); + Hub.SubstituteConfigureScope(new Scope(new SentryOptions())); Clock = new MockClock(); Sdk = new SdkVersion { @@ -34,12 +31,14 @@ public Fixture() public SentryStructuredLoggerProvider GetSut() { - return new SentryStructuredLoggerProvider(Options.Value, Hub, Clock, Sdk); + return new SentryStructuredLoggerProvider(Hub, Clock, Sdk); } } private readonly Fixture _fixture = new(); + public void Dispose() => SentryClientExtensions.SentryOptionsForTestingOnly = null; + [Fact] public void Type_CustomAttributes_HasProviderAliasAttribute() { @@ -56,7 +55,6 @@ public void Ctor_DependencyInjection_CanCreate() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); @@ -85,7 +83,6 @@ public void CreateLogger_DependencyInjection_CanLog() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); diff --git a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerTests.cs index 0b11148cb8..c6a84e8ad6 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerTests.cs @@ -10,7 +10,7 @@ public class SentryStructuredLoggerTests : IDisposable private class Fixture { public string CategoryName { get; internal set; } - public IOptions Options { get; } + public SentryOptions Options { get; } public IHub Hub { get; } public MockClock Clock { get; } public SdkVersion Sdk { get; } @@ -20,18 +20,18 @@ private class Fixture public Fixture() { - var loggingOptions = new SentryLoggingOptions + Options = new SentryOptions { Debug = true, DiagnosticLogger = DiagnosticLogger, Environment = "my-environment", Release = "my-release", }; + SentryClientExtensions.SentryOptionsForTestingOnly = Options; CategoryName = nameof(CategoryName); - Options = Microsoft.Extensions.Options.Options.Create(loggingOptions); Hub = Substitute.For(); - Hub.SubstituteConfigureScope(new Scope(loggingOptions)); + Hub.SubstituteConfigureScope(new Scope(Options)); Clock = new MockClock(new DateTimeOffset(2025, 04, 22, 14, 51, 00, 789, TimeSpan.FromHours(2))); Sdk = new SdkVersion { @@ -58,7 +58,7 @@ public void WithActiveSpan(SentryId traceId, SpanId spanId) public SentryStructuredLogger GetSut() { - return new SentryStructuredLogger(CategoryName, Options.Value, Hub, Clock, Sdk); + return new SentryStructuredLogger(CategoryName, Hub, Clock, Sdk); } } @@ -66,6 +66,7 @@ public SentryStructuredLogger GetSut() public void Dispose() { + SentryClientExtensions.SentryOptionsForTestingOnly = null; _fixture.CapturedLogs.Should().BeEmpty(); _fixture.DiagnosticLogger.Entries.Should().BeEmpty(); } @@ -129,7 +130,7 @@ public void Log_LogLevelNone_DoesNotCaptureLog() [Fact] public void Log_WithoutActiveSpan_CaptureLog() { - var scope = new Scope(_fixture.Options.Value); + var scope = new Scope(_fixture.Options); _fixture.Hub.GetSpan().Returns((ISpan?)null); _fixture.Hub.SubstituteConfigureScope(scope); var logger = _fixture.GetSut(); diff --git a/test/Sentry.Extensions.Logging.Tests/ServiceCollectionExtensionsTests.cs b/test/Sentry.Extensions.Logging.Tests/ServiceCollectionExtensionsTests.cs new file mode 100644 index 0000000000..5b80c1d115 --- /dev/null +++ b/test/Sentry.Extensions.Logging.Tests/ServiceCollectionExtensionsTests.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.DependencyInjection; +using Sentry.Extensions.Logging.Extensions.DependencyInjection; + +namespace Sentry.Extensions.Logging.Tests; + +public class ServiceCollectionExtensionsTests : IDisposable +{ + private class TestHostOptions : SentryHostOptions; + + public void Dispose() => SentrySdk.Close(); + + private static ServiceProvider GetSut(bool initializeSdk, Action configureScope) + { + var services = new ServiceCollection(); + services.Configure(o => + { + o.Dsn = ValidDsn; + o.BackgroundWorker = Substitute.For(); + o.AutoSessionTracking = false; + o.InitNativeSdks = false; + o.ConfigureScope(configureScope); + }); + services.AddSentry(initializeSdk); + return services.BuildServiceProvider(); + } + + [Fact] + public void AddSentry_HubResolved_InitializesSdkAndAppliesConfigureScope() + { + var configured = false; + using var provider = GetSut(initializeSdk: true, _ => configured = true); + + _ = provider.GetRequiredService(); + + SentrySdk.IsEnabled.Should().BeTrue(); + configured.Should().BeTrue(); + } + + [Fact] + public void AddSentry_WithoutInitializeSdk_HubResolved_LeavesSdkAlone() + { + SentrySdk.UseHub(DisabledHub.Instance); + var configured = false; + using var provider = GetSut(initializeSdk: false, _ => configured = true); + + _ = provider.GetRequiredService(); + + SentrySdk.IsEnabled.Should().BeFalse(); + configured.Should().BeFalse(); + } +} diff --git a/test/Sentry.Extensions.Logging.Tests/appsettings.json b/test/Sentry.Extensions.Logging.Tests/appsettings.json index 91330bfa64..42dd09734e 100644 --- a/test/Sentry.Extensions.Logging.Tests/appsettings.json +++ b/test/Sentry.Extensions.Logging.Tests/appsettings.json @@ -1,8 +1,5 @@ { "Sentry": { - "Dsn": "https://d4d82fc1c2c4032a83f3a29aa3a3aff@fake-sentry.io:65535/2147483647", - "MaxBreadcrumbs": 150, - "Release": "e386dfd", "MinimumBreadcrumbLevel": "Warning", "MinimumEventLevel": "Critical" } diff --git a/test/Sentry.Google.Cloud.Functions.Tests/SentryStartupTests.cs b/test/Sentry.Google.Cloud.Functions.Tests/SentryStartupTests.cs index 39c14fa641..0ddd1bc042 100644 --- a/test/Sentry.Google.Cloud.Functions.Tests/SentryStartupTests.cs +++ b/test/Sentry.Google.Cloud.Functions.Tests/SentryStartupTests.cs @@ -127,7 +127,7 @@ public void ConfigureLogging_Logging_AddLoggerProviders() { LoggingBuilder.Services.Configure(options => { - options.InitializeSdk = false; + options.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; }); var sut = new SentryStartup(); @@ -146,7 +146,7 @@ public void ConfigureLogging_Logging_AddLoggerFilterRules() { LoggingBuilder.Services.Configure(options => { - options.InitializeSdk = false; + options.Dsn = Sentry.SentryConstants.DisableSdkDsnValue; }); var sut = new SentryStartup(); diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index db0645d89c..bac8f36955 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -34,7 +34,7 @@ namespace Sentry.Maui void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb); void UnBind(Microsoft.Maui.Controls.VisualElement element); } - public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryMauiOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryMauiOptions() { } public bool AttachScreenshot { get; set; } diff --git a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index db0645d89c..bac8f36955 100644 --- a/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Maui.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -34,7 +34,7 @@ namespace Sentry.Maui void Bind(Microsoft.Maui.Controls.VisualElement element, System.Action addBreadcrumb); void UnBind(Microsoft.Maui.Controls.VisualElement element); } - public class SentryMauiOptions : Sentry.Extensions.Logging.SentryLoggingOptions + public class SentryMauiOptions : Sentry.Extensions.Logging.SentryHostOptions { public SentryMauiOptions() { } public bool AttachScreenshot { get; set; } diff --git a/test/Sentry.Maui.Tests/Internal/SentryMauiStructuredLoggerProviderTests.cs b/test/Sentry.Maui.Tests/Internal/SentryMauiStructuredLoggerProviderTests.cs index a4ee5bb016..7d54cf85e8 100644 --- a/test/Sentry.Maui.Tests/Internal/SentryMauiStructuredLoggerProviderTests.cs +++ b/test/Sentry.Maui.Tests/Internal/SentryMauiStructuredLoggerProviderTests.cs @@ -6,21 +6,18 @@ namespace Sentry.Maui.Tests.Internal; -public class SentryMauiStructuredLoggerProviderTests +public class SentryMauiStructuredLoggerProviderTests : IDisposable { private class Fixture { - public IOptions Options { get; } public IHub Hub { get; } public MockClock Clock { get; } public SdkVersion Sdk { get; } public Fixture() { - var loggingOptions = new SentryMauiOptions(); - - Options = Microsoft.Extensions.Options.Options.Create(loggingOptions); Hub = Substitute.For(); + SentryClientExtensions.SentryOptionsForTestingOnly = new SentryOptions(); Clock = new MockClock(); Sdk = new SdkVersion { @@ -33,12 +30,14 @@ public Fixture() public SentryMauiStructuredLoggerProvider GetSut() { - return new SentryMauiStructuredLoggerProvider(Options.Value, Hub, Clock, Sdk); + return new SentryMauiStructuredLoggerProvider(Hub, Clock, Sdk); } } private readonly Fixture _fixture = new(); + public void Dispose() => SentryClientExtensions.SentryOptionsForTestingOnly = null; + [Fact] public void Type_CustomAttributes_HasProviderAliasAttribute() { @@ -55,7 +54,6 @@ public void Ctor_DependencyInjection_CanCreate() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); @@ -84,7 +82,6 @@ public void CreateLogger_DependencyInjection_CanLog() using var services = new ServiceCollection() .AddLogging() .AddSingleton() - .AddSingleton(_fixture.Options) .AddSingleton(_fixture.Hub) .BuildServiceProvider(); diff --git a/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs b/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs index e512cbd5ea..afbe518260 100644 --- a/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiAppBuilderExtensionsTests.cs @@ -190,6 +190,32 @@ public void UseSentry_SetsMauiSdkNameAndVersion() Assert.Equal(MauiConstants.SdkVersion, @event.Sdk.Version); } + [Fact] + public void UseSentry_ConfigureScope_AppliedWhenSdkInitialized() + { + // Arrange + SentryEvent @event = null; + var builder = _fixture.Builder + .UseSentry(options => + { + options.ConfigureScope(scope => scope.SetTag("configured", "at-init")); + options.SetBeforeSend((e, _) => + { + @event = e; + return null; + }); + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + client.CaptureMessage("test"); + + // Assert + Assert.NotNull(@event); + Assert.Equal("at-init", @event.Tags["configured"]); + } + [Fact] public void UseSentry_EnablesHub() { @@ -338,10 +364,7 @@ public void UseSentry_Logging_AddLoggerProviders() var builder = _fixture.Builder; // Act - builder.UseSentry((SentryMauiOptions options) => - { - options.InitializeSdk = false; - }); + builder.UseSentry(); using var serviceProvider = builder.Services.BuildServiceProvider(); var providers = serviceProvider.GetRequiredService>().ToArray(); @@ -359,10 +382,7 @@ public void UseSentry_Logging_AddLoggerFilterRules() var builder = _fixture.Builder; // Act - builder.UseSentry((SentryMauiOptions options) => - { - options.InitializeSdk = false; - }); + builder.UseSentry(); using var serviceProvider = builder.Services.BuildServiceProvider(); var loggerFilterOptions = serviceProvider.GetRequiredService>().Value; diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index b5e91926c6..2c126ad63f 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -703,4 +703,33 @@ public void CachesInstallationId() installationId2.Should().Be(installationId1); logger.Received(0).Log(SentryLevel.Debug, "Resolved installation ID '{0}'.", null, Arg.Any()); } + + [Fact] + public void ApplyDefaultTags_TagInEvent_DoesNotOverrideTag() + { + const string key = "key"; + const string expected = "event tag value"; + var sut = new SentryOptions(); + var target = new SentryEvent(); + target.SetTag(key, expected); + sut.DefaultTags[key] = "default value"; + + sut.ApplyDefaultTags(target); + + Assert.Equal(expected, target.Tags[key]); + } + + [Fact] + public void ApplyDefaultTags_TagNotInEvent_AppliesTag() + { + const string key = "key"; + const string expected = "default tag value"; + var sut = new SentryOptions(); + var target = new SentryEvent(); + sut.DefaultTags[key] = expected; + + sut.ApplyDefaultTags(target); + + Assert.Equal(expected, target.Tags[key]); + } } From 0aada8780c3b9c23503ce54dbcb88a0525850aec Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 09:30:15 +1200 Subject: [PATCH 10/42] docs: drop unused Sentry settings from the Serilog sample appsettings The sample sets the DSN in code via UseSentry, so the commented-out Dsn entry is misleading. EnableTracing is declared on BindableSentryOptions but never applied, so setting it has no effect. Part of #5245 Co-Authored-By: Claude Opus 5 --- samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json b/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json index 4c79a92f2b..df11df3e18 100644 --- a/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json +++ b/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json @@ -2,10 +2,6 @@ // Example configuration via JSON. Only a subset of the settings below: // All Sentry settings can also be configured via code or environment variables: "Sentry": { - // The DSN can also be set via environment variable - //"Dsn": "TODO: Configure your DSN here and uncomment this line", - // Enable Sentry tracing features - "EnableTracing": true, // Opt-in for payload submission "MaxRequestBodySize": "Always", // Sends Cookies, User Id when one is logged on and user IP address to sentry. It's turned off by default. From 5acfa5e8fda2ea571fee09d86af19fc3037a1785 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 10:31:18 +1200 Subject: [PATCH 11/42] fix(serilog): make the UseSerilog warning check atomic Emit can run concurrently, so the check-then-set on the warned flag could let more than one thread log the warning. Part of #5245 Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentrySink.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index bfd406045b..118abe64b1 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -27,7 +27,7 @@ internal static readonly SdkVersion NameAndVersion private readonly Func _hubAccessor; private readonly ISystemClock _clock; - private volatile bool _checkedUseSerilog; + private int _checkedUseSerilog; public SentrySink(SentrySerilogOptions options) : this( @@ -160,12 +160,11 @@ private void InnerEmit(LogEvent logEvent) private void WarnIfUseSerilogNotCalled(SentryOptions options) { - if (_checkedUseSerilog) + if (Interlocked.Exchange(ref _checkedUseSerilog, 1) != 0) { return; } - _checkedUseSerilog = true; if (!options.HasSerilogScopeEventProcessor()) { options.LogWarning( From 5686ebadbf8aa810367ac764afae4141d666cf2c Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 17:08:11 +1200 Subject: [PATCH 12/42] refactor: the logging integration no longer stamps the SDK name or disposes the hub Sdk.Name and Sdk.Version should identify the integration that initialised the hub, which after this PR can no longer be a logging integration. The logging integration identifies itself through the log origin (auto.log.*) instead. See https://github.com/getsentry/sentry-dotnet/issues/5497. With the SDK name gone, and ConfigureScope callbacks now applied at init, the scope the provider pushed has nothing left to hold, so it goes too. Disposing the hub goes as well: whoever initialises the hub owns it, and the provider is now always handed HubAdapter, which is not IDisposable. The one fixture that handed it a real Hub now disposes the Hub it created. Co-Authored-By: Claude Opus 5 --- .../LoggingBuilderExtensions.cs | 2 +- .../SentryLoggerProvider.cs | 29 ------------ .../MiddlewareLoggerIntegration.cs | 2 +- .../SentryLoggerProviderTests.cs | 45 ------------------- 4 files changed, 2 insertions(+), 76 deletions(-) diff --git a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs index cf7f7264a8..bd704c323b 100644 --- a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs +++ b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs @@ -19,7 +19,7 @@ public static class LoggingBuilderExtensions /// /// The builder. public static ILoggingBuilder AddSentry(this ILoggingBuilder builder) - => builder.AddSentry((Action?)null); + => builder.AddSentry(null); /// /// Adds the Sentry logging integration. diff --git a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs index ddb50d931b..ef7a29c825 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs @@ -13,16 +13,12 @@ internal class SentryLoggerProvider : ILoggerProvider { private readonly ISystemClock _clock; private readonly SentryLoggingOptions _options; - private readonly IDisposable? _scope; - private readonly IDisposable? _disposableHub; internal IHub Hub { get; } internal static readonly SdkVersion NameAndVersion = typeof(SentryLogger).Assembly.GetNameAndVersion(); - private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name; - /// /// Creates a new instance of . /// @@ -39,29 +35,9 @@ internal SentryLoggerProvider( ISystemClock clock, SentryLoggingOptions options) { - _disposableHub = hub as IDisposable; - Hub = hub; _clock = clock; _options = options; - - if (hub.IsEnabled) - { - _scope = hub.PushScope(); - hub.ConfigureScope(static s => - { - if (s.Sdk is { } sdk) - { - sdk.Name = Constants.SdkName; - sdk.Version = NameAndVersion.Version; - - if (NameAndVersion.Version is { } version) - { - sdk.AddPackage(ProtocolPackageName, version); - } - } - }); - } } /// @@ -71,12 +47,7 @@ internal SentryLoggerProvider( /// A logger. public ILogger CreateLogger(string categoryName) => new SentryLogger(categoryName, _options, _clock, Hub); - /// - /// Dispose. - /// public void Dispose() { - _scope?.Dispose(); - _disposableHub?.Dispose(); } } diff --git a/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs b/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs index fc9d1df61e..f379adfe22 100644 --- a/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs +++ b/test/Sentry.AspNetCore.Tests/MiddlewareLoggerIntegration.cs @@ -44,7 +44,7 @@ public Fixture() hub.BindClient(Client); Hub = hub; var provider = new SentryLoggerProvider(hub, new MockClock(), loggingOptions); - _disposable = provider; + _disposable = hub; SentryLogger = provider.CreateLogger(nameof(SentryLogger)); _ = HttpContext.Features.Returns(FeatureCollection); } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs index db4bb02e73..76770b3f7c 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs @@ -43,54 +43,9 @@ public void CreateLogger_Category_AsProvided() Assert.Equal(expectedCategory, actual.CategoryName); } - [Fact] - public void Ctor_DisabledHub_DoesNotCreatesScope() - { - _ = _fixture.Hub.IsEnabled.Returns(false); - _ = _fixture.GetSut(); - _ = _fixture.Hub.DidNotReceive().PushScope(); - } - - [Fact] - public void Ctor_EnabledHub_CreatesScope() - { - _ = _fixture.Hub.IsEnabled.Returns(true); - _ = _fixture.GetSut(); - _ = _fixture.Hub.Received(1).PushScope(); - } - - [Fact] - public void Dispose_DisposesNewScope() - { - _ = _fixture.Hub.IsEnabled.Returns(true); - var disposable = Substitute.For(); - _ = _fixture.Hub.PushScope().Returns(disposable); - - var sut = _fixture.GetSut(); - - sut.Dispose(); - - disposable.Received(1).Dispose(); - } - [Fact] public void NameAndVersion_Name_NotNull() => Assert.NotNull(SentryLoggerProvider.NameAndVersion.Name); [Fact] public void NameAndVersion_Version_NotNull() => Assert.NotNull(SentryLoggerProvider.NameAndVersion.Version); - - [Fact] - public void Ctor_ScopeSdk_ContainNameAndVersion() - { - _ = _fixture.Hub.IsEnabled.Returns(true); - var scope = new Scope(new SentryOptions()); - - _fixture.Hub.When(w => w.ConfigureScope(Arg.Any>())) - .Do(info => info.Arg>()(scope)); - - _ = _fixture.GetSut(); - - Assert.Equal(Constants.SdkName, scope.Sdk.Name); - Assert.Equal(SentryLoggerProvider.NameAndVersion.Version, scope.Sdk.Version); - } } From 3307c301f80b1ebde8aef6abf507faf2aabd158e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 18:07:14 +1200 Subject: [PATCH 13/42] refactor: the Serilog sink no longer sets the SDK name Sdk.Name should identify the integration that initialised the hub, which after this change can no longer be a logging integration. The sink identifies itself through the log origin (auto.log.serilog) instead. See https://github.com/getsentry/sentry-dotnet/issues/5497. Events are no longer stamped with sentry.dotnet.serilog, and structured logs no longer carry it as sentry.sdk.name; both now report the SDK that initialised Sentry. Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentrySink.Structured.cs | 2 +- src/Sentry.Serilog/SentrySink.cs | 27 ------------- .../SentrySinkTests.Structured.cs | 4 +- test/Sentry.Serilog.Tests/SentrySinkTests.cs | 38 ------------------- 4 files changed, 3 insertions(+), 68 deletions(-) diff --git a/src/Sentry.Serilog/SentrySink.Structured.cs b/src/Sentry.Serilog/SentrySink.Structured.cs index 29e534bdfb..6958f8d1b3 100644 --- a/src/Sentry.Serilog/SentrySink.Structured.cs +++ b/src/Sentry.Serilog/SentrySink.Structured.cs @@ -18,7 +18,7 @@ private static void CaptureStructuredLog(IHub hub, SentryOptions options, LogEve }; var scope = hub.GetScope(); - log.SetDefaultAttributes(options, scope, Sdk); + log.SetDefaultAttributes(options, scope); log.SetOrigin("auto.log.serilog"); foreach (var attribute in attributes) diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 118abe64b1..21e7b75c1f 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -8,22 +8,6 @@ internal sealed partial class SentrySink : ILogEventSink { private readonly SentrySerilogOptions _options; - internal static readonly SdkVersion NameAndVersion - = typeof(SentrySink).Assembly.GetNameAndVersion(); - - private static readonly SdkVersion Sdk = new() - { - Name = SdkName, - Version = NameAndVersion.Version, - }; - - /// - /// Serilog SDK name. - /// - public const string SdkName = "sentry.dotnet.serilog"; - - private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name; - private readonly Func _hubAccessor; private readonly ISystemClock _clock; @@ -107,17 +91,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); diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs index b5011db15f..36e19b13fd 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.Structured.cs @@ -115,9 +115,9 @@ public void Emit_StructuredLogging_LogEvent(bool withActiveSpan) log.TryGetAttribute("sentry.origin", out object? origin).Should().BeTrue(); origin.Should().Be("auto.log.serilog"); log.TryGetAttribute("sentry.sdk.name", out object? sdkName).Should().BeTrue(); - sdkName.Should().Be(SentrySink.SdkName); + sdkName.Should().Be(SdkVersion.Instance.Name); log.TryGetAttribute("sentry.sdk.version", out object? sdkVersion).Should().BeTrue(); - sdkVersion.Should().Be(SentrySink.NameAndVersion.Version); + sdkVersion.Should().Be(SdkVersion.Instance.Version); log.TryGetAttribute("property.Scalar-Property", out object? scalar).Should().BeTrue(); scalar.Should().Be(42); diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.cs index 85c52b7b77..d0af65f399 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.cs @@ -82,44 +82,6 @@ public void EmitEvent_WithoutException_LeavesBreadcrumb() _fixture.Scope.Breadcrumbs.Should().NotBeEmpty(); } - [Fact] - public void Emit_SerilogSdk_Name() - { - var sut = _fixture.GetSut(); - - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, - Enumerable.Empty()); - - sut.Emit(evt); - - var expected = typeof(SentrySink).Assembly.GetNameAndVersion(); - _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Sdk.Name == SentrySink.SdkName - && e.Sdk.Version == expected.Version)); - } - - [Fact] - public void Emit_SerilogSdk_Packages() - { - var sut = _fixture.GetSut(); - - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, - Enumerable.Empty()); - - SentryEvent actual = null; - _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())) - .Do(c => actual = c.Arg()); - - sut.Emit(evt); - - var expected = typeof(SentrySink).Assembly.GetNameAndVersion(); - - Assert.NotNull(actual); - var package = Assert.Single(actual.Sdk.Packages); - Assert.Equal("nuget:" + expected.Name, package!.Name); - Assert.Equal(expected.Version, package.Version); - } - internal class EventLogLevelsData : IEnumerable { public IEnumerator GetEnumerator() From 62dad84782683707838edff62bfad2d9e38cf01b Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 18:08:59 +1200 Subject: [PATCH 14/42] refactor: the NLog target no longer sets the SDK name Sdk.Name should identify the integration that initialised the hub, which after this change can no longer be a logging integration. The target identifies itself through the log origin (auto.log.nlog) instead. See https://github.com/getsentry/sentry-dotnet/issues/5497. Events are no longer stamped with sentry.dotnet.nlog, and structured logs no longer carry it as sentry.sdk.name; both now report the SDK that initialised Sentry. With no remaining callers, Constants is deleted. Co-Authored-By: Claude Opus 5 --- src/Sentry.NLog/Constants.cs | 12 ------- src/Sentry.NLog/SentryTarget.Structured.cs | 2 +- src/Sentry.NLog/SentryTarget.cs | 21 ------------ .../SentryTargetTests.Structured.cs | 4 +-- test/Sentry.NLog.Tests/SentryTargetTests.cs | 34 ------------------- 5 files changed, 3 insertions(+), 70 deletions(-) delete mode 100644 src/Sentry.NLog/Constants.cs diff --git a/src/Sentry.NLog/Constants.cs b/src/Sentry.NLog/Constants.cs deleted file mode 100644 index 65df808c29..0000000000 --- a/src/Sentry.NLog/Constants.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Sentry.NLog; - -/// -/// NLog Constant Values. -/// -internal static class Constants -{ - /// - /// NLog SDK name - /// - public const string SdkName = "sentry.dotnet.nlog"; -} diff --git a/src/Sentry.NLog/SentryTarget.Structured.cs b/src/Sentry.NLog/SentryTarget.Structured.cs index 66de2c3b4d..d8f7c96e98 100644 --- a/src/Sentry.NLog/SentryTarget.Structured.cs +++ b/src/Sentry.NLog/SentryTarget.Structured.cs @@ -15,7 +15,7 @@ private static void CaptureStructuredLog(IHub hub, SentryOptions options, LogEve var log = SentryLog.Create(hub, timestamp, level, logEvent.FormattedMessage, logEvent.Message, parameters); var scope = hub.GetScope(); - log.SetDefaultAttributes(options, scope, Sdk); + log.SetDefaultAttributes(options, scope); log.SetOrigin("auto.log.nlog"); if (logEvent.LoggerName is not null) diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index b3f53a186b..31c95fe85e 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -11,18 +11,8 @@ public sealed partial class SentryTarget : TargetWithContext private readonly ISystemClock _clock; - internal static readonly SdkVersion NameAndVersion = typeof(SentryTarget).Assembly.GetNameAndVersion(); - - private static readonly SdkVersion Sdk = new() - { - Name = Constants.SdkName, - Version = NameAndVersion.Version, - }; - internal static readonly string AdditionalGroupingKeyProperty = "AdditionalGroupingKey"; - private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name; - /// /// Creates a new instance of . /// @@ -344,17 +334,6 @@ private void CreateSentryEvent(LogEventInfo logEvent, Exception? exception, bool User = GetUser(logEvent) ?? new SentryUser(), }; - if (evt.Sdk is { } sdk) - { - sdk.Name = Constants.SdkName; - sdk.Version = NameAndVersion.Version; - - if (NameAndVersion.Version is { } version) - { - sdk.AddPackage(ProtocolPackageName, version); - } - } - if (Tags.Count > 0 || IncludeEventPropertiesAsTags && logEvent.HasProperties) { evt.SetTags(GetTagsFromLogEvent(logEvent)); diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs b/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs index ba80c06c16..1318e01ef7 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.Structured.cs @@ -98,8 +98,8 @@ public void Write_StructuredLogging_LogEvent(bool withActiveSpan) log.Attributes.ShouldContain("sentry.environment", "test-environment"); log.Attributes.ShouldContain("sentry.release", "test-release"); log.Attributes.ShouldContain("sentry.origin", "auto.log.nlog"); - log.Attributes.ShouldContain("sentry.sdk.name", Constants.SdkName); - log.Attributes.ShouldContain("sentry.sdk.version", SentryTarget.NameAndVersion.Version); + log.Attributes.ShouldContain("sentry.sdk.name", SdkVersion.Instance.Name!); + log.Attributes.ShouldContain("sentry.sdk.version", SdkVersion.Instance.Version!); log.Attributes.ShouldContain("category.name", "sentry"); log.Attributes.ShouldContain("property.Text-Property-Key", "Text-Property-Value"); diff --git a/test/Sentry.NLog.Tests/SentryTargetTests.cs b/test/Sentry.NLog.Tests/SentryTargetTests.cs index b04eb5d284..d0a475b453 100644 --- a/test/Sentry.NLog.Tests/SentryTargetTests.cs +++ b/test/Sentry.NLog.Tests/SentryTargetTests.cs @@ -201,40 +201,6 @@ public void Log_WithOnlyException_GeneratesBreadcrumbFromException() Assert.Equal(b.Data["exception_message"], expectedException.Message); } - [Fact] - public void Log_NLogSdk_Name() - { - _fixture.Options.MinimumEventLevel = LogLevel.Info; - var logger = _fixture.GetLogger(); - - var expected = typeof(SentryTarget).Assembly.GetNameAndVersion(); - logger.Info(DefaultMessage); - - _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Sdk.Name == Constants.SdkName - && e.Sdk.Version == expected.Version)); - } - - [Fact] - public void Log_NLogSdk_Packages() - { - _fixture.Options.MinimumEventLevel = LogLevel.Info; - var logger = _fixture.GetLogger(); - - SentryEvent actual = null; - _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())) - .Do(c => actual = c.Arg()); - - logger.Info(DefaultMessage); - - var expected = typeof(SentryTarget).Assembly.GetNameAndVersion(); - - Assert.NotNull(actual); - var package = Assert.Single(actual.Sdk.Packages); - Assert.Equal("nuget:" + expected.Name, package.Name); - Assert.Equal(expected.Version, package.Version); - } - [Theory] [ClassData(typeof(LogLevelData))] public void Log_LoggerLevel_Set(LogLevel nlogLevel, SentryLevel? sentryLevel) From 5e04b92807871028f08120fab378242f1bdaf92a Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 18:10:32 +1200 Subject: [PATCH 15/42] refactor: the log4net appender no longer sets the SDK name Sdk.Name should identify the integration that initialised the hub, which after this change can no longer be a logging integration. The appender identifies itself through the log origin (auto.log.log4net) instead. See https://github.com/getsentry/sentry-dotnet/issues/5497. Events are no longer stamped with sentry.dotnet.log4net, and structured logs no longer carry it as sentry.sdk.name; both now report the SDK that initialised Sentry. Co-Authored-By: Claude Opus 5 --- .../SentryAppender.Structured.cs | 2 +- src/Sentry.Log4Net/SentryAppender.cs | 28 --------------- .../SentryAppenderTests.Structured.cs | 4 +-- .../SentryAppenderTests.cs | 36 ------------------- 4 files changed, 3 insertions(+), 67 deletions(-) diff --git a/src/Sentry.Log4Net/SentryAppender.Structured.cs b/src/Sentry.Log4Net/SentryAppender.Structured.cs index feb810ed0f..64e9a26122 100644 --- a/src/Sentry.Log4Net/SentryAppender.Structured.cs +++ b/src/Sentry.Log4Net/SentryAppender.Structured.cs @@ -17,7 +17,7 @@ private static void CaptureStructuredLog(IHub hub, SentryOptions options, Loggin var log = SentryLog.Create(hub, timestamp, level, message, template, parameters); var scope = hub.GetScope(); - log.SetDefaultAttributes(options, scope, Sdk); + log.SetDefaultAttributes(options, scope); log.SetOrigin("auto.log.log4net"); if (sendIdentity && !string.IsNullOrEmpty(loggingEvent.Identity)) diff --git a/src/Sentry.Log4Net/SentryAppender.cs b/src/Sentry.Log4Net/SentryAppender.cs index e4295705c9..dd3072b4fe 100644 --- a/src/Sentry.Log4Net/SentryAppender.cs +++ b/src/Sentry.Log4Net/SentryAppender.cs @@ -7,17 +7,6 @@ namespace Sentry.Log4Net; /// public partial class SentryAppender : AppenderSkeleton { - internal static readonly SdkVersion NameAndVersion - = typeof(SentryAppender).Assembly.GetNameAndVersion(); - - private static readonly SdkVersion Sdk = new() - { - Name = SdkName, - Version = NameAndVersion.Version, - }; - - private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name; - private readonly IHub _hub; /// @@ -31,12 +20,6 @@ internal static readonly SdkVersion NameAndVersion /// public Level? MinimumEventLevel { get; set; } - /// - /// log4net SDK name. - /// - /// - internal const string SdkName = "sentry.dotnet.log4net"; - /// /// Creates a new instance of the . /// @@ -104,17 +87,6 @@ private void CreateSentryEvent(LoggingEvent loggingEvent, Exception? exception) Level = loggingEvent.ToSentryLevel() }; - if (evt.Sdk is { } sdk) - { - sdk.Name = SdkName; - sdk.Version = NameAndVersion.Version; - - if (NameAndVersion.Version is { } version) - { - sdk.AddPackage(ProtocolPackageName, version); - } - } - if (!string.IsNullOrWhiteSpace(loggingEvent.RenderedMessage)) { evt.Message = loggingEvent.RenderedMessage; diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs b/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs index 55211bcb32..31c3ea0441 100644 --- a/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs +++ b/test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs @@ -99,8 +99,8 @@ public void DoAppend_StructuredLogging_LogEvent(bool withActiveSpan) log.Attributes.ShouldContain("sentry.environment", "test-environment"); log.Attributes.ShouldContain("sentry.release", "test-release"); log.Attributes.ShouldContain("sentry.origin", "auto.log.log4net"); - log.Attributes.ShouldContain("sentry.sdk.name", SentryAppender.SdkName); - log.Attributes.ShouldContain("sentry.sdk.version", SentryAppender.NameAndVersion.Version); + log.Attributes.ShouldContain("sentry.sdk.name", SdkVersion.Instance.Name!); + log.Attributes.ShouldContain("sentry.sdk.version", SdkVersion.Instance.Version!); log.Attributes.ShouldContain("category.name", "TestLogger"); log.Attributes.ShouldContain("property.Text-Property", "4"); diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs index d6050033f1..6ce734c111 100644 --- a/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs +++ b/test/Sentry.Log4Net.Tests/SentryAppenderTests.cs @@ -48,42 +48,6 @@ public void Append_WithException_CreatesEventWithException() .CaptureEvent(Arg.Is(e => e.Exception == expected)); } - [Fact] - public void Append_Log4NetSdk_Name() - { - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - - sut.DoAppend(evt); - - var expected = typeof(SentryAppender).Assembly.GetNameAndVersion(); - _ = _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Sdk.Name == SentryAppender.SdkName - && e.Sdk.Version == expected.Version)); - } - - [Fact] - public void Append_Log4NetSdk_Packages() - { - var sut = _fixture.GetSut(); - - var evt = new LoggingEvent(new LoggingEventData()); - - SentryEvent actual = null; - _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())) - .Do(c => actual = c.Arg()); - - sut.DoAppend(evt); - - var expected = typeof(SentryAppender).Assembly.GetNameAndVersion(); - - Assert.NotNull(actual); - var package = Assert.Single(actual.Sdk.Packages); - Assert.Equal("nuget:" + expected.Name, package.Name); - Assert.Equal(expected.Version, package.Version); - } - [Fact] public void Append_LoggerNameAndLevel_Set() { From f6c72aefa3e975c07c89c59ce56f6f7de52c325d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 18:14:07 +1200 Subject: [PATCH 16/42] refactor: structured logs from Microsoft.Extensions.Logging no longer set the SDK name Completes the change across the four logging integrations: the SDK name on a log should identify the integration that initialised the hub, and the logging integration identifies itself through the origin (auto.log.extensions_logging). See https://github.com/getsentry/sentry-dotnet/issues/5497. The ASP.NET Core and MAUI structured logger providers keep passing their own SDK version: those integrations do initialise the SDK, so the name is theirs to set. With no remaining callers, Constants and SentryLoggerProvider.NameAndVersion are deleted. Co-Authored-By: Claude Opus 5 --- src/Sentry.Extensions.Logging/Constants.cs | 7 ------- .../SentryLoggerProvider.cs | 4 ---- .../SentryStructuredLogger.cs | 4 ++-- .../SentryStructuredLoggerProvider.cs | 15 +++------------ .../SentryLoggerProviderTests.cs | 6 ------ .../SentryStructuredLoggerProviderTests.cs | 4 ++-- 6 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 src/Sentry.Extensions.Logging/Constants.cs diff --git a/src/Sentry.Extensions.Logging/Constants.cs b/src/Sentry.Extensions.Logging/Constants.cs deleted file mode 100644 index 9c409f46b0..0000000000 --- a/src/Sentry.Extensions.Logging/Constants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Sentry.Extensions.Logging; - -internal static class Constants -{ - // See: https://github.com/getsentry/sentry-release-registry - public const string SdkName = "sentry.dotnet.extensions.logging"; -} diff --git a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs index ef7a29c825..e3c6b94fe8 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggerProvider.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Sentry.Infrastructure; -using Sentry.Reflection; namespace Sentry.Extensions.Logging; @@ -16,9 +15,6 @@ internal class SentryLoggerProvider : ILoggerProvider internal IHub Hub { get; } - internal static readonly SdkVersion NameAndVersion - = typeof(SentryLogger).Assembly.GetNameAndVersion(); - /// /// Creates a new instance of . /// diff --git a/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs b/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs index 6eae15193f..907670a332 100644 --- a/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs +++ b/src/Sentry.Extensions.Logging/SentryStructuredLogger.cs @@ -9,9 +9,9 @@ internal sealed class SentryStructuredLogger : ILogger private readonly string? _categoryName; private readonly IHub _hub; private readonly ISystemClock _clock; - private readonly SdkVersion _sdk; + private readonly SdkVersion? _sdk; - internal SentryStructuredLogger(string categoryName, IHub hub, ISystemClock clock, SdkVersion sdk) + internal SentryStructuredLogger(string categoryName, IHub hub, ISystemClock clock, SdkVersion? sdk) { _categoryName = categoryName; _clock = clock; diff --git a/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs b/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs index b55c95e15f..4345f16710 100644 --- a/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs +++ b/src/Sentry.Extensions.Logging/SentryStructuredLoggerProvider.cs @@ -11,14 +11,14 @@ internal class SentryStructuredLoggerProvider : ILoggerProvider { private readonly IHub _hub; private readonly ISystemClock _clock; - private readonly SdkVersion _sdk; + private readonly SdkVersion? _sdk; public SentryStructuredLoggerProvider(IHub hub) - : this(hub, SystemClock.Clock, CreateSdkVersion()) + : this(hub, SystemClock.Clock, sdk: null) { } - internal SentryStructuredLoggerProvider(IHub hub, ISystemClock clock, SdkVersion sdk) + internal SentryStructuredLoggerProvider(IHub hub, ISystemClock clock, SdkVersion? sdk) { _hub = hub; _clock = clock; @@ -33,13 +33,4 @@ public ILogger CreateLogger(string categoryName) public void Dispose() { } - - private static SdkVersion CreateSdkVersion() - { - return new SdkVersion - { - Name = Constants.SdkName, - Version = SentryLoggerProvider.NameAndVersion.Version, - }; - } } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs index 76770b3f7c..99122fe37c 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggerProviderTests.cs @@ -42,10 +42,4 @@ public void CreateLogger_Category_AsProvided() Assert.Equal(expectedCategory, actual.CategoryName); } - - [Fact] - public void NameAndVersion_Name_NotNull() => Assert.NotNull(SentryLoggerProvider.NameAndVersion.Name); - - [Fact] - public void NameAndVersion_Version_NotNull() => Assert.NotNull(SentryLoggerProvider.NameAndVersion.Version); } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs index fd9b7c88d1..1c63a1ff2a 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryStructuredLoggerProviderTests.cs @@ -94,10 +94,10 @@ public void CreateLogger_DependencyInjection_CanLog() categoryName.Should().Be(typeof(SentryStructuredLoggerProviderTests).FullName); capturedLog.TryGetAttribute("sentry.sdk.name", out object? name).Should().BeTrue(); - name.Should().Be(Constants.SdkName); + name.Should().Be(SdkVersion.Instance.Name); capturedLog.TryGetAttribute("sentry.sdk.version", out object? version).Should().BeTrue(); - version.Should().Be(SentryLoggerProvider.NameAndVersion.Version); + version.Should().Be(SdkVersion.Instance.Version); capturedLog.TryGetAttribute("sentry.origin", out object? origin).Should().BeTrue(); origin.Should().Be("auto.log.extensions_logging"); From 72d27f9ddfc5656d2592b70a0e5133c4613e2027 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Tue, 22 Sep 2026 12:18:22 +0200 Subject: [PATCH 17/42] feat(serilog): configuring a DSN on the sink now fails with a migration error Serilog configuration providers bind sink arguments by parameter name, so removing the dsn-first overload made them drop `dsn` silently: the sink still binds, Sentry is never initialized, and nothing is reported. Keeping the overload as an [Obsolete(error: true)] tombstone that throws makes both Serilog.Settings.Configuration (appsettings.json) and Serilog.Settings.AppSettings (app.config) fail loudly with migration guidance, while code callers get a compile error instead of a type mismatch on the second argument. The overload mirrors the surviving overload's parameters plus `dsn`. With only `string dsn` it loses Serilog's overload ranking whenever a configuration supplies two or more of the surviving arguments, which would restore the silent behaviour. Part of #5245 Co-Authored-By: Claude Opus 5 (1M context) --- src/Sentry.Serilog/SentrySinkExtensions.cs | 35 +++++++++++++++++++ ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 ++ ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 ++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 2 ++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 2 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 ++ .../SentrySerilogSinkExtensionsTests.cs | 35 +++++++++++++++++++ 7 files changed, 80 insertions(+) diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index d35e247cf2..190ad5eda2 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -8,6 +8,41 @@ namespace Serilog; [EditorBrowsable(EditorBrowsableState.Never)] public static class SentrySinkExtensions { + private const string ObsoleteDsnOverload = + "The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. " + + "Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() " + + "on those options, and remove 'dsn' and any other core SDK settings from the sink configuration."; + + /// + /// Not supported. The Sentry sink no longer initializes the SDK. + /// + /// + /// This overload only exists so that configuration providers that bind sink arguments by name (such as + /// Serilog.Settings.Configuration reading appsettings.json) fail loudly instead of silently + /// dropping a dsn that no longer has any effect. It always throws. + /// + /// The logger configuration. + /// No longer supported. + /// Minimum log level to send an event. + /// Minimum log level to record a breadcrumb. + /// The Serilog format provider. + /// The Serilog text formatter. + /// The minimum level for events passed through the sink. + /// A switch allowing the pass-through minimum level to be changed at runtime. + /// Never returns. + /// Always. + [Obsolete(ObsoleteDsnOverload, error: true)] + public static LoggerConfiguration Sentry( + this LoggerSinkConfiguration loggerConfiguration, + string dsn, + LogEventLevel? minimumEventLevel = null, + LogEventLevel? minimumBreadcrumbLevel = null, + IFormatProvider? formatProvider = null, + ITextFormatter? textFormatter = null, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch? levelSwitch = null) + => throw new NotSupportedException(ObsoleteDsnOverload); + /// /// Adds a Sentry Sink for Serilog. /// diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index b43e743529..2953e9968b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -22,5 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index b43e743529..2953e9968b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -22,5 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index b43e743529..2953e9968b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -22,5 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index b43e743529..2953e9968b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -22,5 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index b43e743529..2953e9968b 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -22,5 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs b/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs index fcbcf55d18..df8012691a 100644 --- a/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySerilogSinkExtensionsTests.cs @@ -104,6 +104,41 @@ public void Sentry_WithRestrictedToMinimumLevel_ParameterIsAccepted() Assert.Null(ex); } + [Fact] + public void Sentry_DsnOverload_InvokedByName_Throws() + { + var method = typeof(SentrySinkExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(m => m.Name == nameof(SentrySinkExtensions.Sentry) + && m.GetParameters().Any(p => p.Name == "dsn")); + + var arguments = method.GetParameters() + .Select(p => p.Name == "dsn" + ? "https://key@sentry.io/1" + : p.HasDefaultValue ? p.DefaultValue : null) + .ToArray(); + arguments[0] = new LoggerConfiguration().WriteTo; + + var exception = Assert.Throws(() => method.Invoke(null, arguments)); + + Assert.IsType(exception.InnerException); + Assert.Contains("no longer initializes the SDK", exception.InnerException!.Message); + } + + [Fact] + public void Sentry_DsnOverload_IsObsoleteAsError() + { + var method = typeof(SentrySinkExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(m => m.Name == nameof(SentrySinkExtensions.Sentry) + && m.GetParameters().Any(p => p.Name == "dsn")); + + var obsolete = method.GetCustomAttribute(); + + Assert.NotNull(obsolete); + Assert.True(obsolete!.IsError); + } + private static void AssertEqualDeep(object expected, object actual) { var serializedLeftObject = JsonSerializer.Serialize(expected); From 9d271126dc37b8a1a677ebc86d496b966395b0d5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 10:43:03 +1200 Subject: [PATCH 18/42] test(serilog): pin the DSN tombstone against Serilog.Settings.Configuration The migration guard works only because of Serilog's overload ranking, and nothing exercised that path. These tests bind a sink from IConfiguration the way a provider does, so a Serilog change that stops selecting the tombstone fails here rather than silently dropping the DSN again. Verified they fail without the tombstone overload. Selection behaves the same on Serilog.Settings.Configuration 3.4.0 (Serilog 2.12) and 10.0.1 (Serilog 4.3); 3.4.0 is referenced to avoid bumping Serilog in the tests. Part of #5245 Co-Authored-By: Claude Opus 5 --- .../Sentry.Serilog.Tests.csproj | 1 + .../SentrySinkConfigurationBindingTests.cs | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/Sentry.Serilog.Tests/SentrySinkConfigurationBindingTests.cs diff --git a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj index ec8bc74fbc..b7879c2fe8 100644 --- a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj +++ b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/test/Sentry.Serilog.Tests/SentrySinkConfigurationBindingTests.cs b/test/Sentry.Serilog.Tests/SentrySinkConfigurationBindingTests.cs new file mode 100644 index 0000000000..0a55563216 --- /dev/null +++ b/test/Sentry.Serilog.Tests/SentrySinkConfigurationBindingTests.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.Configuration; + +namespace Sentry.Serilog.Tests; + +public class SentrySinkConfigurationBindingTests +{ + private static IConfiguration SinkConfiguration(params (string Name, string Value)[] args) + { + var settings = new Dictionary + { + ["Serilog:Using:0"] = "Sentry.Serilog", + ["Serilog:WriteTo:0:Name"] = "Sentry" + }; + + foreach (var (name, value) in args) + { + settings[$"Serilog:WriteTo:0:Args:{name}"] = value; + } + + return new ConfigurationBuilder().AddInMemoryCollection(settings).Build(); + } + + private static Exception CreateLogger(IConfiguration configuration) => Record.Exception(() => + new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger().Dispose()); + + [Fact] + public void ReadFromConfiguration_WithDsn_Throws() + { + var exception = CreateLogger(SinkConfiguration(("dsn", ValidDsn))); + + var notSupported = Assert.IsType(exception?.GetBaseException()); + Assert.Contains("SentrySdk.Init", notSupported.Message); + Assert.Contains("UseSerilog()", notSupported.Message); + } + + [Fact] + public void ReadFromConfiguration_WithDsnAndSinkArguments_Throws() + { + var exception = CreateLogger(SinkConfiguration(("dsn", ValidDsn), ("minimumEventLevel", "Error"))); + + Assert.IsType(exception?.GetBaseException()); + } + + [Fact] + public void ReadFromConfiguration_WithoutDsn_DoesNotThrow() + { + var exception = CreateLogger(SinkConfiguration( + ("minimumEventLevel", "Error"), + ("minimumBreadcrumbLevel", "Debug"))); + + Assert.Null(exception); + } + + [Fact] + public void ReadFromConfiguration_WithoutArguments_DoesNotThrow() + { + var exception = CreateLogger(SinkConfiguration()); + + Assert.Null(exception); + } +} From ab2bb8543e410a0f7cdcd408c47df3719291adc7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 10:47:09 +1200 Subject: [PATCH 19/42] feat(nlog): configuring a DSN on the target now fails with a migration error Mirrors the Serilog guard (#5611). The v6 AddSentry(dsn, ...) overloads and the SentryTarget.Dsn / InitializeSdk properties come back as tombstones: obsolete-as-error for code callers, throwing NotSupportedException so NLog.config bindings fail loudly with migration guidance instead of reporting an unknown property. Part of #5245 Co-Authored-By: Claude Opus 5 --- src/Sentry.NLog/ConfigurationExtensions.cs | 37 +++++++++ src/Sentry.NLog/SentryTarget.cs | 22 ++++++ ...iApprovalTests.Run.DotNet10_0.verified.txt | 8 ++ ...iApprovalTests.Run.DotNet11_0.verified.txt | 8 ++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 8 ++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 8 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 8 ++ .../SentryTargetConfigurationBindingTests.cs | 75 +++++++++++++++++++ 8 files changed, 174 insertions(+) create mode 100644 test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs diff --git a/src/Sentry.NLog/ConfigurationExtensions.cs b/src/Sentry.NLog/ConfigurationExtensions.cs index 7ac7c0f880..cf2aa7df00 100644 --- a/src/Sentry.NLog/ConfigurationExtensions.cs +++ b/src/Sentry.NLog/ConfigurationExtensions.cs @@ -12,6 +12,43 @@ public static class ConfigurationExtensions // Internal for testability internal const string DefaultTargetName = "sentry"; + internal const string ObsoleteDsnOverload = + "The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. " + + "Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', " + + "'initializeSdk' and any other core SDK settings from the target configuration."; + + /// + /// Not supported. The Sentry target no longer initializes the SDK. + /// + /// The NLog configuration. + /// No longer supported. + /// An optional action for configuring the Sentry target options. + /// Never returns. + /// Always. + [Obsolete(ObsoleteDsnOverload, error: true)] + public static LoggingConfiguration AddSentry( + this LoggingConfiguration configuration, + string? dsn, + Action? optionsConfig = null) + => throw new NotSupportedException(ObsoleteDsnOverload); + + /// + /// Not supported. The Sentry target no longer initializes the SDK. + /// + /// The NLog configuration. + /// No longer supported. + /// The name to give the new target. + /// An optional action for configuring the Sentry target options. + /// Never returns. + /// Always. + [Obsolete(ObsoleteDsnOverload, error: true)] + public static LoggingConfiguration AddSentry( + this LoggingConfiguration configuration, + string? dsn, + string targetName, + Action? optionsConfig = null) + => throw new NotSupportedException(ObsoleteDsnOverload); + /// /// Adds a target for Sentry to the NLog configuration. /// diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index 31c95fe85e..643a44ead2 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -55,6 +55,28 @@ internal SentryTarget(SentryNLogOptions options, Func hubAccessor, ISystem [ArrayParameter(typeof(TargetPropertyWithContext), "tag")] public IList Tags => Options.Tags; + /// + /// Not supported. The Sentry target no longer initializes the SDK. + /// + /// When set. + [Obsolete(ConfigurationExtensions.ObsoleteDsnOverload, error: true)] + public Layout? Dsn + { + get => null; + set => throw new NotSupportedException(ConfigurationExtensions.ObsoleteDsnOverload); + } + + /// + /// Not supported. The Sentry target no longer initializes the SDK. + /// + /// When set. + [Obsolete(ConfigurationExtensions.ObsoleteDsnOverload, error: true)] + public bool InitializeSdk + { + get => false; + set => throw new NotSupportedException(ConfigurationExtensions.ObsoleteDsnOverload); + } + /// /// An optional layout specific to breadcrumbs. If not set, uses the same layout as the standard . /// diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 331cfbd575..b13bb35f5f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,6 +4,10 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } @@ -48,12 +52,16 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 331cfbd575..b13bb35f5f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,6 +4,10 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } @@ -48,12 +52,16 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 331cfbd575..b13bb35f5f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -4,6 +4,10 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } @@ -48,12 +52,16 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 331cfbd575..b13bb35f5f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -4,6 +4,10 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } @@ -48,12 +52,16 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 331cfbd575..b13bb35f5f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,6 +4,10 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } } @@ -48,12 +52,16 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] public bool EnableLogs { get; set; } public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } public Sentry.NLog.SentryNLogOptions Options { get; } diff --git a/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs b/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs new file mode 100644 index 0000000000..a25df0ed93 --- /dev/null +++ b/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs @@ -0,0 +1,75 @@ +namespace Sentry.NLog.Tests; + +public class SentryTargetConfigurationBindingTests +{ + private static Exception LoadConfiguration(string targetAttributes) + { + var configXml = $@" + + + + + "; + + var logFactory = new LogFactory(); + return Record.Exception(() => logFactory.Configuration = + new XmlLoggingConfiguration(XmlReader.Create(new StringReader(configXml)), null, logFactory)); + } + + [Theory] + [InlineData("dsn='https://key@sentry.io/1'")] + [InlineData("initializeSdk='true'")] + public void LoadConfiguration_WithSdkSetting_Throws(string targetAttributes) + { + var exception = LoadConfiguration(targetAttributes); + + Assert.NotNull(exception); + Assert.Contains("SentrySdk.Init", exception.ToString()); + } + + [Fact] + public void LoadConfiguration_WithTargetSettings_DoesNotThrow() + { + Assert.Null(LoadConfiguration("minimumEventLevel='Warn' includeEventPropertiesAsTags='true'")); + } + + [Theory] + [InlineData(2)] + [InlineData(3)] + public void AddSentry_DsnOverload_InvokedByName_Throws(int parameterCount) + { + var method = DsnOverload(parameterCount); + + var arguments = method.GetParameters() + .Select(p => (object)(p.Name switch + { + "configuration" => new LoggingConfiguration(), + "dsn" => ValidDsn, + "targetName" => "sentry", + _ => p.HasDefaultValue ? p.DefaultValue : null + })!) + .ToArray(); + + var exception = Assert.Throws(() => method.Invoke(null, arguments)); + + Assert.IsType(exception.InnerException); + Assert.Contains("SentrySdk.Init", exception.InnerException!.Message); + } + + [Theory] + [InlineData(2)] + [InlineData(3)] + public void AddSentry_DsnOverload_IsObsoleteAsError(int parameterCount) + { + var obsolete = DsnOverload(parameterCount).GetCustomAttribute(); + + Assert.NotNull(obsolete); + Assert.True(obsolete!.IsError); + } + + private static MethodInfo DsnOverload(int parameterCount) => typeof(ConfigurationExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(m => m.Name == nameof(ConfigurationExtensions.AddSentry) + && m.GetParameters().Any(p => p.Name == "dsn") + && m.GetParameters().Length == parameterCount + 1); +} From ed735672aaf8dafcb55968816017a9835e85bfad Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 11:10:53 +1200 Subject: [PATCH 20/42] docs(serilog): reword the DSN migration error Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentrySinkExtensions.cs | 2 +- .../ApiApprovalTests.Run.DotNet10_0.verified.txt | 2 +- .../ApiApprovalTests.Run.DotNet11_0.verified.txt | 2 +- .../ApiApprovalTests.Run.DotNet8_0.verified.txt | 2 +- .../ApiApprovalTests.Run.DotNet9_0.verified.txt | 2 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index 190ad5eda2..f1f39ffb4e 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -10,7 +10,7 @@ public static class SentrySinkExtensions { private const string ObsoleteDsnOverload = "The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. " + - "Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() " + + "Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() " + "on those options, and remove 'dsn' and any other core SDK settings from the sink configuration."; /// diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 2953e9968b..6ca1edf510 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -22,7 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 2953e9968b..6ca1edf510 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -22,7 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 2953e9968b..6ca1edf510 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -22,7 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 2953e9968b..6ca1edf510 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -22,7 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file diff --git a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 2953e9968b..6ca1edf510 100644 --- a/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Serilog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -22,7 +22,7 @@ namespace Serilog { public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, System.Action configureOptions) { } public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } - [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] + [System.Obsolete(@"The Sentry sink no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), call UseSerilog() on those options, and remove 'dsn' and any other core SDK settings from the sink configuration.", true)] public static Serilog.LoggerConfiguration Sentry(this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration, string dsn, Serilog.Events.LogEventLevel? minimumEventLevel = default, Serilog.Events.LogEventLevel? minimumBreadcrumbLevel = default, System.IFormatProvider? formatProvider = null, Serilog.Formatting.ITextFormatter? textFormatter = null, Serilog.Events.LogEventLevel restrictedToMinimumLevel = 0, Serilog.Core.LoggingLevelSwitch? levelSwitch = null) { } } } \ No newline at end of file From 5ab9e8fcdb7e4a14177a655b39d23d00cb8e6aa5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 11:11:37 +1200 Subject: [PATCH 21/42] docs(nlog): reword the DSN migration error to match Serilog Co-Authored-By: Claude Opus 5 --- src/Sentry.NLog/ConfigurationExtensions.cs | 2 +- .../ApiApprovalTests.Run.DotNet10_0.verified.txt | 8 ++++---- .../ApiApprovalTests.Run.DotNet11_0.verified.txt | 8 ++++---- .../ApiApprovalTests.Run.DotNet8_0.verified.txt | 8 ++++---- .../ApiApprovalTests.Run.DotNet9_0.verified.txt | 8 ++++---- .../ApiApprovalTests.Run.Net4_8.verified.txt | 8 ++++---- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Sentry.NLog/ConfigurationExtensions.cs b/src/Sentry.NLog/ConfigurationExtensions.cs index cf2aa7df00..5fb8e6a3ad 100644 --- a/src/Sentry.NLog/ConfigurationExtensions.cs +++ b/src/Sentry.NLog/ConfigurationExtensions.cs @@ -14,7 +14,7 @@ public static class ConfigurationExtensions internal const string ObsoleteDsnOverload = "The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. " + - "Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', " + + "Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', " + "'initializeSdk' and any other core SDK settings from the target configuration."; /// diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index b13bb35f5f..aa4f652e7f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,9 +4,9 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } @@ -52,7 +52,7 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] @@ -60,7 +60,7 @@ namespace Sentry.NLog public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index b13bb35f5f..aa4f652e7f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,9 +4,9 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } @@ -52,7 +52,7 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] @@ -60,7 +60,7 @@ namespace Sentry.NLog public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index b13bb35f5f..aa4f652e7f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -4,9 +4,9 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } @@ -52,7 +52,7 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] @@ -60,7 +60,7 @@ namespace Sentry.NLog public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index b13bb35f5f..aa4f652e7f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -4,9 +4,9 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } @@ -52,7 +52,7 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] @@ -60,7 +60,7 @@ namespace Sentry.NLog public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } diff --git a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index b13bb35f5f..aa4f652e7f 100644 --- a/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.NLog.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,9 +4,9 @@ namespace NLog public static class ConfigurationExtensions { public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, System.Action? optionsConfig = null, string targetName = "sentry") { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, System.Action? optionsConfig = null) { } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public static NLog.Config.LoggingConfiguration AddSentry(this NLog.Config.LoggingConfiguration configuration, string? dsn, string targetName, System.Action? optionsConfig = null) { } public static void AddTag(this Sentry.NLog.SentryNLogOptions options, string name, NLog.Layouts.Layout layout) { } } @@ -52,7 +52,7 @@ namespace Sentry.NLog public SentryTarget(Sentry.NLog.SentryNLogOptions options) { } public NLog.Layouts.Layout? BreadcrumbCategory { get; set; } public NLog.Layouts.Layout? BreadcrumbLayout { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public NLog.Layouts.Layout? Dsn { get; set; } [System.Obsolete("Logs are always enabled. This option is ignored and will be removed in a future m" + "ajor version. To drop logs, use SetBeforeSendLog and return null.")] @@ -60,7 +60,7 @@ namespace Sentry.NLog public bool IgnoreEventsWithNoException { get; set; } public bool IncludeEventDataOnBreadcrumbs { get; set; } public bool IncludeEventPropertiesAsTags { get; set; } - [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] + [System.Obsolete(@"The Sentry target no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'dsn', 'initializeSdk' and any other core SDK settings from the target configuration.", true)] public bool InitializeSdk { get; set; } public string MinimumBreadcrumbLevel { get; set; } public string MinimumEventLevel { get; set; } From ca044b4172dbce63b6608da35b7e724d721202ad Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 11:45:09 +1200 Subject: [PATCH 22/42] feat(log4net): configuring a DSN on the appender now reports a migration error Brings back SentryAppender.Dsn as a tombstone: [Obsolete(error: true)] with a setter that throws NotSupportedException carrying migration guidance, so code callers get a compile error and XML configs report the message instead of log4net's "Cannot find Property [Dsn]". Unlike Serilog and NLog, this cannot fail configuration loading: log4net catches exceptions thrown while setting a parameter, so the appender is still attached and the message surfaces through log4net's internal logging. Part of #5245 Co-Authored-By: Claude Opus 5 --- src/Sentry.Log4Net/SentryAppender.cs | 16 ++++ ...iApprovalTests.Run.DotNet10_0.verified.txt | 2 + ...iApprovalTests.Run.DotNet11_0.verified.txt | 2 + .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 + ...SentryAppenderConfigurationBindingTests.cs | 93 +++++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 test/Sentry.Log4Net.Tests/SentryAppenderConfigurationBindingTests.cs diff --git a/src/Sentry.Log4Net/SentryAppender.cs b/src/Sentry.Log4Net/SentryAppender.cs index dd3072b4fe..ee0b351f57 100644 --- a/src/Sentry.Log4Net/SentryAppender.cs +++ b/src/Sentry.Log4Net/SentryAppender.cs @@ -9,6 +9,22 @@ public partial class SentryAppender : AppenderSkeleton { private readonly IHub _hub; + internal const string ObsoleteDsn = + "The Sentry appender no longer initializes the SDK, so a DSN can no longer be supplied to it. " + + "Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'Dsn' " + + "and any other core SDK settings from the appender configuration."; + + /// + /// Not supported. The Sentry appender no longer initializes the SDK. + /// + /// When set. + [Obsolete(ObsoleteDsn, error: true)] + public string? Dsn + { + get => null; + set => throw new NotSupportedException(ObsoleteDsn); + } + /// /// Whether to send the Identity or not. /// diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index a82332f24d..269b5fe6f0 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,6 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } + [System.Obsolete(@"The Sentry appender no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'Dsn' and any other core SDK settings from the appender configuration.", true)] + public string? Dsn { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index a82332f24d..269b5fe6f0 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,6 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } + [System.Obsolete(@"The Sentry appender no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'Dsn' and any other core SDK settings from the appender configuration.", true)] + public string? Dsn { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } diff --git a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index a82332f24d..269b5fe6f0 100644 --- a/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Log4Net.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,6 +4,8 @@ namespace Sentry.Log4Net public class SentryAppender : log4net.Appender.AppenderSkeleton { public SentryAppender() { } + [System.Obsolete(@"The Sentry appender no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or UseSentry via one of the integrations), and remove 'Dsn' and any other core SDK settings from the appender configuration.", true)] + public string? Dsn { get; set; } public log4net.Core.Level? MinimumEventLevel { get; set; } public bool SendIdentity { get; set; } protected override void Append(log4net.Core.LoggingEvent loggingEvent) { } diff --git a/test/Sentry.Log4Net.Tests/SentryAppenderConfigurationBindingTests.cs b/test/Sentry.Log4Net.Tests/SentryAppenderConfigurationBindingTests.cs new file mode 100644 index 0000000000..dfad573b4e --- /dev/null +++ b/test/Sentry.Log4Net.Tests/SentryAppenderConfigurationBindingTests.cs @@ -0,0 +1,93 @@ +#nullable enable + +using System.Xml; +using log4net.Config; +using log4net.Util; + +namespace Sentry.Log4Net.Tests; + +public class SentryAppenderConfigurationBindingTests +{ + private static (Exception? Exception, int AppenderCount, string Errors) LoadConfiguration(string appenderElements) + { + var configXml = $""" + + + {appenderElements} + + + + """; + + var document = new XmlDocument(); + document.LoadXml(configXml); + + var errors = new List(); + void OnLogReceived(object? sender, LogReceivedEventArgs e) + { + if (e.LogLog.Exception is { } exception) + { + errors.Add($"{e.LogLog.Message} {exception.Message}"); + } + else + { + errors.Add(e.LogLog.Message); + } + } + + LogLog.LogReceived += OnLogReceived; + try + { + var repository = LogManager.CreateRepository(Guid.NewGuid().ToString()); + var exception = Record.Exception(() => XmlConfigurator.Configure(repository, document.DocumentElement)); + return (exception, repository.GetAppenders().Length, string.Join(" | ", errors)); + } + finally + { + LogLog.LogReceived -= OnLogReceived; + } + } + + [Fact] + public void LoadConfiguration_WithDsn_ReportsMigrationError() + { + var (exception, appenderCount, errors) = LoadConfiguration(""""""); + + // log4net catches exceptions thrown while setting a parameter, so the config still loads and the appender attaches. + Assert.Null(exception); + Assert.Equal(1, appenderCount); + Assert.Contains("SentrySdk.Init", errors); + } + + [Fact] + public void LoadConfiguration_WithAppenderSettings_ReportsNoErrors() + { + var (exception, appenderCount, errors) = LoadConfiguration( + """"""); + + Assert.Null(exception); + Assert.Equal(1, appenderCount); + Assert.Empty(errors); + } + + [Fact] + public void Dsn_WhenSet_Throws() + { + var exception = Assert.Throws( + () => DsnProperty.SetValue(new SentryAppender(), "https://key@sentry.io/1")); + + Assert.IsType(exception.InnerException); + Assert.Contains("SentrySdk.Init", exception.InnerException!.Message); + } + + [Fact] + public void Dsn_IsObsoleteAsError() + { + var obsolete = DsnProperty.GetCustomAttribute(); + + Assert.NotNull(obsolete); + Assert.True(obsolete!.IsError); + } + + private static PropertyInfo DsnProperty => typeof(SentryAppender).GetProperty("Dsn")!; +} From 2e9b9c712b604bd921b6b41e86a39180550a2383 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 11:56:02 +1200 Subject: [PATCH 23/42] feat(mel): configuring a DSN through the logging integration now fails with a migration error Mirrors the Serilog (#5611), NLog and log4net guards. The v6 AddSentry(dsn) overload and the SentryLoggingOptions.Dsn / InitializeSdk properties come back as tombstones: obsolete-as-error for code callers, throwing NotSupportedException so configuration fails loudly with migration guidance instead of being ignored. Both binding paths are covered. On .NET 6 and later the Sentry section binds through BindableSentryLoggingOptions, which now carries these keys and throws when either is present; on netstandard2.0 the configuration binder sets the properties directly and the setters throw. Part of #5245 Co-Authored-By: Claude Opus 5 --- .../BindableSentryLoggingOptions.cs | 7 ++ .../LoggingBuilderExtensions.cs | 13 ++- .../SentryLoggingOptions.cs | 27 ++++++ ...iApprovalTests.Run.DotNet10_0.verified.txt | 6 ++ ...iApprovalTests.Run.DotNet11_0.verified.txt | 6 ++ ...piApprovalTests.Run.DotNet8_0.verified.txt | 6 ++ ...piApprovalTests.Run.DotNet9_0.verified.txt | 6 ++ .../ApiApprovalTests.Run.Net4_8.verified.txt | 6 ++ ...LoggingOptionsConfigurationBindingTests.cs | 89 +++++++++++++++++++ .../SentryLoggingOptionsTests.cs | 3 +- 10 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs diff --git a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs index e7ed493305..48e9d39d3e 100644 --- a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs @@ -6,9 +6,16 @@ internal class BindableSentryLoggingOptions { public LogLevel? MinimumBreadcrumbLevel { get; set; } public LogLevel? MinimumEventLevel { get; set; } + public string? Dsn { get; set; } + public bool? InitializeSdk { get; set; } public void ApplyTo(SentryLoggingOptions options) { + if (Dsn is not null || InitializeSdk is not null) + { + throw new NotSupportedException(SentryLoggingOptions.ObsoleteSdkInitialization); + } + options.MinimumBreadcrumbLevel = MinimumBreadcrumbLevel ?? options.MinimumBreadcrumbLevel; options.MinimumEventLevel = MinimumEventLevel ?? options.MinimumEventLevel; } diff --git a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs index bd704c323b..bbdb7dd90e 100644 --- a/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs +++ b/src/Sentry.Extensions.Logging/LoggingBuilderExtensions.cs @@ -19,7 +19,18 @@ public static class LoggingBuilderExtensions /// /// The builder. public static ILoggingBuilder AddSentry(this ILoggingBuilder builder) - => builder.AddSentry(null); + => builder.AddSentry((Action?)null); + + /// + /// Not supported. The logging integration no longer initializes the SDK. + /// + /// The builder. + /// No longer supported. + /// Never returns. + /// Always. + [Obsolete(SentryLoggingOptions.ObsoleteSdkInitialization, error: true)] + public static ILoggingBuilder AddSentry(this ILoggingBuilder builder, string dsn) + => throw new NotSupportedException(SentryLoggingOptions.ObsoleteSdkInitialization); /// /// Adds the Sentry logging integration. diff --git a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs index cd934d6f68..0d90b2327e 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs @@ -11,6 +11,33 @@ namespace Sentry.Extensions.Logging; /// public class SentryLoggingOptions { + internal const string ObsoleteSdkInitialization = + "The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be " + + "supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove " + + "'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration."; + + /// + /// Not supported. The logging integration no longer initializes the SDK. + /// + /// When set. + [Obsolete(ObsoleteSdkInitialization, error: true)] + public string? Dsn + { + get => null; + set => throw new NotSupportedException(ObsoleteSdkInitialization); + } + + /// + /// Not supported. The logging integration no longer initializes the SDK. + /// + /// When set. + [Obsolete(ObsoleteSdkInitialization, error: true)] + public bool InitializeSdk + { + get => false; + set => throw new NotSupportedException(ObsoleteSdkInitialization); + } + /// /// Gets or sets the minimum breadcrumb level. /// diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 26b02c5b85..3f03e8681e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -4,6 +4,8 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -37,6 +39,10 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions { public SentryLoggingOptions() { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public string? Dsn { get; set; } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt index 26b02c5b85..3f03e8681e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet11_0.verified.txt @@ -4,6 +4,8 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -37,6 +39,10 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions { public SentryLoggingOptions() { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public string? Dsn { get; set; } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 26b02c5b85..3f03e8681e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -4,6 +4,8 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -37,6 +39,10 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions { public SentryLoggingOptions() { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public string? Dsn { get; set; } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 26b02c5b85..3f03e8681e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -4,6 +4,8 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -37,6 +39,10 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions { public SentryLoggingOptions() { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public string? Dsn { get; set; } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } } diff --git a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 26b02c5b85..3f03e8681e 100644 --- a/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Extensions.Logging.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -4,6 +4,8 @@ { public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder) { } public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, System.Action? optionsConfiguration) { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public static Microsoft.Extensions.Logging.ILoggingBuilder AddSentry(this Microsoft.Extensions.Logging.ILoggingBuilder builder, string dsn) { } } public static class SentryLoggerFactoryExtensions { @@ -37,6 +39,10 @@ namespace Sentry.Extensions.Logging public class SentryLoggingOptions { public SentryLoggingOptions() { } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public string? Dsn { get; set; } + [System.Obsolete(@"The Microsoft.Extensions.Logging integration no longer initializes the SDK, so a DSN can no longer be supplied to it. Initialize Sentry with SentrySdk.Init (or an integration such as UseSentry), and remove 'Dsn', 'InitializeSdk' and any other core SDK settings from the logging configuration.", true)] + public bool InitializeSdk { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumBreadcrumbLevel { get; set; } public Microsoft.Extensions.Logging.LogLevel MinimumEventLevel { get; set; } } diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs new file mode 100644 index 0000000000..00bbabb7e3 --- /dev/null +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs @@ -0,0 +1,89 @@ +#nullable enable + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Sentry.Extensions.Logging.Tests; + +public class SentryLoggingOptionsConfigurationBindingTests +{ + private static Exception? BindConfiguration(string key, string value) + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { [key] = value }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(builder => builder.AddConfiguration(config).AddSentry()); + using var provider = services.BuildServiceProvider(); + + return Record.Exception(() => provider.GetRequiredService>().Value); + } + + [Theory] + [InlineData("Sentry:Dsn", "https://key@sentry.io/1")] + [InlineData("Sentry:InitializeSdk", "true")] + [InlineData("Sentry:InitializeSdk", "false")] + public void BindConfiguration_WithSdkSetting_Throws(string key, string value) + { + var exception = BindConfiguration(key, value); + + Assert.NotNull(exception); + Assert.Contains("SentrySdk.Init", exception.ToString()); + } + + [Fact] + public void BindConfiguration_WithLoggingSetting_DoesNotThrow() + => Assert.Null(BindConfiguration("Sentry:MinimumEventLevel", nameof(LogLevel.Warning))); + + [Theory] + [InlineData("Dsn", "https://key@sentry.io/1")] + [InlineData("InitializeSdk", true)] + public void SetProperty_SdkSetting_Throws(string propertyName, object value) + { + var property = typeof(SentryLoggingOptions).GetProperty(propertyName)!; + + var exception = Assert.Throws( + () => property.SetValue(new SentryLoggingOptions(), value)); + + Assert.IsType(exception.InnerException); + Assert.Contains("SentrySdk.Init", exception.InnerException!.Message); + } + + private static MethodInfo DsnOverload => typeof(SentryLoggingOptions).Assembly + .GetType("Microsoft.Extensions.Logging.LoggingBuilderExtensions")! + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(m => m.Name == "AddSentry" && m.GetParameters().Any(p => p.Name == "dsn")); + + [Fact] + public void AddSentry_DsnOverload_InvokedByName_Throws() + { + var exception = Assert.Throws( + () => { DsnOverload.Invoke(null, [Substitute.For(), "https://key@sentry.io/1"]); }); + + Assert.IsType(exception.InnerException); + Assert.Contains("SentrySdk.Init", exception.InnerException!.Message); + } + + [Theory] + [InlineData("Dsn")] + [InlineData("InitializeSdk")] + public void SdkSettings_AreObsoleteAsError(string propertyName) + { + var obsolete = typeof(SentryLoggingOptions).GetProperty(propertyName)!.GetCustomAttribute(); + + Assert.NotNull(obsolete); + Assert.True(obsolete!.IsError); + } + + [Fact] + public void AddSentry_DsnOverload_IsObsoleteAsError() + { + var obsolete = DsnOverload.GetCustomAttribute(); + + Assert.NotNull(obsolete); + Assert.True(obsolete!.IsError); + } +} diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs index 772473b267..b1811cbfc2 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs @@ -3,7 +3,8 @@ namespace Sentry.Extensions.Logging.Tests; -public class SentryLoggingOptionsTests : BindableTests +// Dsn and InitializeSdk are tombstones: bound so that configuration fails loudly, never applied. +public class SentryLoggingOptionsTests() : BindableTests("Dsn", "InitializeSdk") { [Fact] public void BindableProperties_MatchOptionsProperties() From 6649270d389f504dc693c81f724a3fc155364d57 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 13:06:54 +1200 Subject: [PATCH 24/42] fix(mel): only treat a supplied Dsn or InitializeSdk as a migration error The tombstone setters threw unconditionally, which broke every bind of SentryLoggingOptions on netstandard2.0: ConfigurationBinder reads each property and writes the value back, so InitializeSdk's own `false` tripped the guard even when the key was absent. That failed four tests on net48, three of them pre-existing. The setters now throw only for a value that asks for something the integration can no longer do, and BindableSentryLoggingOptions matches, so both binding paths behave the same: a Dsn or InitializeSdk=true is an error, InitializeSdk=false is accepted because not initializing is what now always happens. Covered by a test that binds onto the options directly, which reproduces the netstandard2.0 write-back on every target framework. Co-Authored-By: Claude Opus 5 --- .../BindableSentryLoggingOptions.cs | 2 +- .../SentryLoggingOptions.cs | 17 +++++++++++++-- ...LoggingOptionsConfigurationBindingTests.cs | 21 ++++++++++++++++--- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs index 48e9d39d3e..6c6757a728 100644 --- a/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/BindableSentryLoggingOptions.cs @@ -11,7 +11,7 @@ internal class BindableSentryLoggingOptions public void ApplyTo(SentryLoggingOptions options) { - if (Dsn is not null || InitializeSdk is not null) + if (Dsn is not null || InitializeSdk == true) { throw new NotSupportedException(SentryLoggingOptions.ObsoleteSdkInitialization); } diff --git a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs index 0d90b2327e..a936cfd7a1 100644 --- a/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs +++ b/src/Sentry.Extensions.Logging/SentryLoggingOptions.cs @@ -24,7 +24,14 @@ public class SentryLoggingOptions public string? Dsn { get => null; - set => throw new NotSupportedException(ObsoleteSdkInitialization); + // Only a supplied value is an error: the configuration binder writes the getter's value back. + set + { + if (value is not null) + { + throw new NotSupportedException(ObsoleteSdkInitialization); + } + } } /// @@ -35,7 +42,13 @@ public string? Dsn public bool InitializeSdk { get => false; - set => throw new NotSupportedException(ObsoleteSdkInitialization); + set + { + if (value) + { + throw new NotSupportedException(ObsoleteSdkInitialization); + } + } } /// diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs index 00bbabb7e3..e5961232a5 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsConfigurationBindingTests.cs @@ -25,7 +25,6 @@ public class SentryLoggingOptionsConfigurationBindingTests [Theory] [InlineData("Sentry:Dsn", "https://key@sentry.io/1")] [InlineData("Sentry:InitializeSdk", "true")] - [InlineData("Sentry:InitializeSdk", "false")] public void BindConfiguration_WithSdkSetting_Throws(string key, string value) { var exception = BindConfiguration(key, value); @@ -34,9 +33,25 @@ public void BindConfiguration_WithSdkSetting_Throws(string key, string value) Assert.Contains("SentrySdk.Init", exception.ToString()); } + [Theory] + [InlineData("Sentry:MinimumEventLevel", nameof(LogLevel.Warning))] + // Asking not to initialize is what now always happens, so it is not an error. + [InlineData("Sentry:InitializeSdk", "false")] + public void BindConfiguration_WithoutSdkSetting_DoesNotThrow(string key, string value) + => Assert.Null(BindConfiguration(key, value)); + [Fact] - public void BindConfiguration_WithLoggingSetting_DoesNotThrow() - => Assert.Null(BindConfiguration("Sentry:MinimumEventLevel", nameof(LogLevel.Warning))); + public void BindOptionsDirectly_WithLoggingSetting_DoesNotThrow() + { + // The netstandard2.0 path binds onto the options directly, and writes each property's value back. + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["MinimumEventLevel"] = nameof(LogLevel.Warning) }) + .Build(); + var options = new SentryLoggingOptions(); + + Assert.Null(Record.Exception(() => config.Bind(options))); + Assert.Equal(LogLevel.Warning, options.MinimumEventLevel); + } [Theory] [InlineData("Dsn", "https://key@sentry.io/1")] From a1bf334dd3eb0751c129698db0230b42501b58ee Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Wed, 23 Sep 2026 03:47:56 +0200 Subject: [PATCH 25/42] fix(serilog): the Sentry sink registers the Serilog scope event processor automatically (#5612) * fix: make the SentryOptions processor collections thread safe SentryClient enumerates these collections lazily for the whole duration of a capture, and AddEventProcessor is documented as supporting registration after the SDK is initialised. They were plain Lists, so appending to one while a capture was in flight threw InvalidOperationException - which the SDK catches and logs at Debug, silently dropping the event. Swap them for ConcurrentBagLite, which snapshots on enumeration. Scope.EventProcessors already uses it for the same reason. Co-Authored-By: Claude Opus 5 (1M context) * fix(serilog): register the Serilog scope event processor automatically The sink no longer initialises the SDK, so integrators have to call UseSerilog() on the options used to initialise Sentry. Forgetting it was only reported as a warning gated behind Debug and DiagnosticLevel, so in practice it was silent. The sink now registers SerilogScopeEventProcessor itself: at construction when Sentry is already initialised, otherwise on the first log event. The sink and the processor live in the same assembly, so no reflection is needed and this stays AOT safe. UseSerilog() is still the better option - it applies from the first event rather than from the first log line - and the warning now says so. Also fixes a feedback loop this exposed. Emit answered a reentrant log event with another diagnostic, which Serilog routed straight back into the sink, each message embedding the last. With DiagnosticLevel at Info that produced 55 MB of logs in 17 seconds and the app stopped serving requests. The SDK-namespace filter that breaks the cycle now runs before the reentrancy check instead of after it. Co-Authored-By: Claude Opus 5 (1M context) * Removed unnecessary comments Co-authored-by: James Crosswell --------- Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: James Crosswell --- src/Sentry.Serilog/SentryOptionExtensions.cs | 2 +- src/Sentry.Serilog/SentrySink.cs | 45 +++++++----- src/Sentry/Internal/ConcurrentBagLite.cs | 18 +++++ src/Sentry/SentryOptions.cs | 16 ++--- test/Sentry.Serilog.Tests/SentrySinkTests.cs | 74 ++++++++++++++++++++ test/Sentry.Tests/SentryOptionsTests.cs | 16 +++++ 6 files changed, 144 insertions(+), 27 deletions(-) diff --git a/src/Sentry.Serilog/SentryOptionExtensions.cs b/src/Sentry.Serilog/SentryOptionExtensions.cs index 662ef30b87..dd4b2b6726 100644 --- a/src/Sentry.Serilog/SentryOptionExtensions.cs +++ b/src/Sentry.Serilog/SentryOptionExtensions.cs @@ -26,5 +26,5 @@ public static void UseSerilog(this SentryOptions options) } internal static bool HasSerilogScopeEventProcessor(this SentryOptions options) - => options.EventProcessors.Exists(processor => processor.Type == typeof(SerilogScopeEventProcessor)); + => options.EventProcessors.Any(processor => processor.Type == typeof(SerilogScopeEventProcessor)); } diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 21e7b75c1f..fce2402b4a 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -11,7 +11,7 @@ internal sealed partial class SentrySink : ILogEventSink private readonly Func _hubAccessor; private readonly ISystemClock _clock; - private int _checkedUseSerilog; + private int _registeredScopeEventProcessor; public SentrySink(SentrySerilogOptions options) : this( @@ -29,12 +29,24 @@ internal SentrySink( _options = options; _hubAccessor = hubAccessor; _clock = clock; + + if (hubAccessor() is { IsEnabled: true } hub && hub.GetSentryOptions() is { } sentryOptions) + { + EnsureSerilogScopeEventProcessor(sentryOptions); + } } private static AsyncLocal 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) { _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}"); @@ -44,7 +56,7 @@ public void Emit(LogEvent logEvent) isReentrant.Value = true; try { - InnerEmit(logEvent); + InnerEmit(logEvent, context); } finally { @@ -52,16 +64,8 @@ public void Emit(LogEvent logEvent) } } - private void InnerEmit(LogEvent logEvent) + private void InnerEmit(LogEvent logEvent, string? context) { - if (logEvent.TryGetSourceContext(out var context)) - { - if (SentrySdkNamespaces.IsSentrySdk(context)) - { - return; - } - } - if (_hubAccessor() is not { IsEnabled: true } hub) { return; @@ -70,7 +74,7 @@ private void InnerEmit(LogEvent logEvent) var options = hub.GetSentryOptions(); if (options is not null) { - WarnIfUseSerilogNotCalled(options); + EnsureSerilogScopeEventProcessor(options); } var exception = logEvent.Exception; @@ -131,19 +135,24 @@ private void InnerEmit(LogEvent logEvent) } } - private void WarnIfUseSerilogNotCalled(SentryOptions options) + private void EnsureSerilogScopeEventProcessor(SentryOptions options) { - if (Interlocked.Exchange(ref _checkedUseSerilog, 1) != 0) + if (Interlocked.Exchange(ref _registeredScopeEventProcessor, 1) != 0) { return; } - if (!options.HasSerilogScopeEventProcessor()) + if (options.HasSerilogScopeEventProcessor()) { - options.LogWarning( - "The Sentry sink for Serilog is in use, but UseSerilog() was not called on the options used to initialise Sentry. " + - "Properties from the Serilog LogContext will not be applied to Sentry events."); + return; } + + options.UseSerilog(); + 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) diff --git a/src/Sentry/Internal/ConcurrentBagLite.cs b/src/Sentry/Internal/ConcurrentBagLite.cs index 353b290724..a24676c2d7 100644 --- a/src/Sentry/Internal/ConcurrentBagLite.cs +++ b/src/Sentry/Internal/ConcurrentBagLite.cs @@ -28,6 +28,24 @@ public void Add(T item) } } + public void AddRange(IEnumerable items) + { + // Materialised outside the lock: the source may be a lazy sequence running arbitrary code. + var toAdd = items.ToArray(); + lock (_items) + { + _items.AddRange(toAdd); + } + } + + public int RemoveAll(Predicate match) + { + lock (_items) + { + return _items.RemoveAll(match); + } + } + public int Count { get diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index f51356aa11..bf663d4390 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -134,32 +134,32 @@ internal ISentryStackTraceFactory SentryStackTraceFactory /// /// A list of exception processors /// - internal List<(Type Type, Lazy Lazy)> ExceptionProcessors { get; set; } + internal ConcurrentBagLite<(Type Type, Lazy Lazy)> ExceptionProcessors { get; set; } /// /// A list of transaction processors /// - internal List? TransactionProcessors { get; set; } + internal ConcurrentBagLite? TransactionProcessors { get; set; } /// /// A list of event processors /// - internal List<(Type Type, Lazy Lazy)> EventProcessors { get; set; } + internal ConcurrentBagLite<(Type Type, Lazy Lazy)> EventProcessors { get; set; } /// /// A list of providers of /// - internal List>> EventProcessorsProviders { get; set; } + internal ConcurrentBagLite>> EventProcessorsProviders { get; set; } /// /// A list of providers of /// - internal List>> TransactionProcessorsProviders { get; set; } + internal ConcurrentBagLite>> TransactionProcessorsProviders { get; set; } /// /// A list of providers of /// - internal List>> ExceptionProcessorsProviders { get; set; } + internal ConcurrentBagLite>> ExceptionProcessorsProviders { get; set; } private DefaultIntegrations _defaultIntegrations; @@ -1777,7 +1777,7 @@ public void AddTransactionProcessors(IEnumerable pr { if (TransactionProcessors == null) { - TransactionProcessors = processors.ToList(); + TransactionProcessors = new(processors); } else { @@ -1797,7 +1797,7 @@ public void RemoveTransactionProcessor() where TProcessor : ISentryT /// /// The transaction processor provider. public void AddTransactionProcessorProvider(Func> processorProvider) - => TransactionProcessorsProviders = TransactionProcessorsProviders.Concat(new[] { processorProvider }).ToList(); + => TransactionProcessorsProviders.Add(processorProvider); /// /// Add the exception processor provider. diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.cs index d0af65f399..8db0d4f37b 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.cs @@ -227,6 +227,80 @@ public void Emit_DisabledHub_NoWarning() .Should().NotContain(e => e.Message.Contains("UseSerilog()")); } + [Fact] + public void Ctor_SentryAlreadyInitialised_RegistersScopeEventProcessor() + { + _ = _fixture.GetSut(); + + _fixture.SentryOptions.HasSerilogScopeEventProcessor().Should().BeTrue(); + } + + [Fact] + public void Emit_SentryInitialisedAfterCtor_RegistersScopeEventProcessor() + { + _fixture.Hub.IsEnabled.Returns(false); + var sut = _fixture.GetSut(); + _fixture.SentryOptions.HasSerilogScopeEventProcessor().Should().BeFalse(); + + _fixture.Hub.IsEnabled.Returns(true); + sut.Emit(new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty())); + + _fixture.SentryOptions.HasSerilogScopeEventProcessor().Should().BeTrue(); + } + + [Fact] + public void Ctor_DisabledHub_DoesNotRegisterScopeEventProcessor() + { + _fixture.Hub.IsEnabled.Returns(false); + + _ = _fixture.GetSut(); + + _fixture.SentryOptions.HasSerilogScopeEventProcessor().Should().BeFalse(); + } + + [Fact] + public void Ctor_UseSerilogAlreadyCalled_DoesNotRegisterASecondProcessor() + { + _fixture.SentryOptions.UseSerilog(); + + _ = _fixture.GetSut(); + + _fixture.SentryOptions.EventProcessors + .Count(p => p.Type == typeof(SerilogScopeEventProcessor)) + .Should().Be(1); + } + + [Fact] + public void Emit_SdkDiagnosticArrivesWhileEmitting_IsIgnored() + { + var sut = _fixture.GetSut(); + var sdkDiagnostic = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Warning, null, MessageTemplate.Empty, + new[] { new LogEventProperty("SourceContext", new ScalarValue("Sentry.ISentryClient")) }); + _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())).Do(_ => sut.Emit(sdkDiagnostic)); + + sut.Emit(new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty())); + + _fixture.DiagnosticLogger.Entries + .Should().NotContain(e => e.Message.Contains("Reentrant log event detected")); + } + + [Fact] + public void Emit_ApplicationLogArrivesWhileEmitting_ReportsReentrancy() + { + var sut = _fixture.GetSut(); + var applicationLog = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Warning, null, MessageTemplate.Empty, + new[] { new LogEventProperty("SourceContext", new ScalarValue("MyApp.Service")) }); + _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())).Do(_ => sut.Emit(applicationLog)); + + sut.Emit(new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty())); + + _fixture.DiagnosticLogger.Entries + .Should().Contain(e => e.Message.Contains("Reentrant log event detected")); + } + [Fact] public void Emit_WithFormat_EventCaptured() { diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index b5e91926c6..145f01c0ab 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -485,6 +485,22 @@ public void AddEventProcessors_StoredInOptions() Assert.Contains(sut.EventProcessors, actual => actual.Lazy.Value == second); } + [Fact] + public void AddEventProcessor_WhileProcessorsAreBeingEnumerated_DoesNotThrow() + { + // SentryEventHelper.ProcessEvent enumerates these lazily for the whole duration of a capture, + // so registering a processor from another thread must not invalidate an in-flight enumeration. + var sut = new SentryOptions(); + using var inFlightCapture = sut.GetAllEventProcessors().GetEnumerator(); + inFlightCapture.MoveNext(); + + sut.AddEventProcessor(Substitute.For()); + + var exception = Record.Exception(() => inFlightCapture.MoveNext()); + + Assert.Null(exception); + } + [Fact] public void AddEventProcessorProvider_StoredInOptions() { From 7b88c800b4384e617c49cd0b46745551774195ca Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 14:33:12 +1200 Subject: [PATCH 26/42] fix(serilog): register the scope event processor atomically Sinks sharing one set of SentryOptions can reach registration concurrently - each sink's guard is per-instance - so the check and the add have to happen under a lock, not as check-then-act. The sink now learns from the result whether it was the one that registered, which is what the warning reports. Part of #5245 Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentryOptionExtensions.cs | 20 ++++++++++++++----- src/Sentry.Serilog/SentrySink.cs | 3 +-- .../SentryOptionExtensionsTests.cs | 10 ++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Sentry.Serilog/SentryOptionExtensions.cs b/src/Sentry.Serilog/SentryOptionExtensions.cs index dd4b2b6726..8719d0b964 100644 --- a/src/Sentry.Serilog/SentryOptionExtensions.cs +++ b/src/Sentry.Serilog/SentryOptionExtensions.cs @@ -5,6 +5,8 @@ namespace Sentry.Serilog; /// public static class SentryOptionExtensions { + private static readonly object Sync = new(); + /// /// Enables the Serilog integration, so that properties from the Serilog LogContext get applied to all Sentry /// events. @@ -15,16 +17,24 @@ public static class SentryOptionExtensions /// do this for you. Calling this more than once has no additional effect. /// /// The options used to initialise Sentry. - public static void UseSerilog(this SentryOptions options) + public static void UseSerilog(this SentryOptions options) => options.TryUseSerilog(); + + // Sinks sharing one set of options can reach this concurrently, so the check and the add have to be atomic. + internal static bool TryUseSerilog(this SentryOptions options) { - if (options.HasSerilogScopeEventProcessor()) + lock (Sync) { - return; - } + if (options.HasSerilogScopeEventProcessor()) + { + return false; + } - options.AddEventProcessor(new SerilogScopeEventProcessor(options)); + options.AddEventProcessor(new SerilogScopeEventProcessor(options)); + return true; + } } internal static bool HasSerilogScopeEventProcessor(this SentryOptions options) => options.EventProcessors.Any(processor => processor.Type == typeof(SerilogScopeEventProcessor)); + } diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index fce2402b4a..9a6005b1b4 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -142,12 +142,11 @@ private void EnsureSerilogScopeEventProcessor(SentryOptions options) return; } - if (options.HasSerilogScopeEventProcessor()) + if (!options.TryUseSerilog()) { return; } - options.UseSerilog(); 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 " + diff --git a/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs b/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs index 209ef079fa..6666004ded 100644 --- a/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs +++ b/test/Sentry.Serilog.Tests/SentryOptionExtensionsTests.cs @@ -12,6 +12,16 @@ public void UseSerilog_AddsSerilogScopeEventProcessor() options.GetAllEventProcessors().OfType().Should().ContainSingle(); } + [Fact] + public void UseSerilog_CalledConcurrently_AddsProcessorOnce() + { + var options = new SentryOptions(); + + Parallel.For(0, 64, _ => options.UseSerilog()); + + options.GetAllEventProcessors().OfType().Should().ContainSingle(); + } + [Fact] public void UseSerilog_CalledTwice_AddsProcessorOnce() { From 53f4144f566195a8a1698958991b4a9e4afdf75d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 16:21:45 +1200 Subject: [PATCH 27/42] refactor(serilog): use the Lock shim for the registration lock Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentryOptionExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Sentry.Serilog/SentryOptionExtensions.cs b/src/Sentry.Serilog/SentryOptionExtensions.cs index 8719d0b964..cd340eacce 100644 --- a/src/Sentry.Serilog/SentryOptionExtensions.cs +++ b/src/Sentry.Serilog/SentryOptionExtensions.cs @@ -5,7 +5,7 @@ namespace Sentry.Serilog; /// public static class SentryOptionExtensions { - private static readonly object Sync = new(); + private static readonly Lock Sync = new(); /// /// Enables the Serilog integration, so that properties from the Serilog LogContext get applied to all Sentry @@ -36,5 +36,4 @@ internal static bool TryUseSerilog(this SentryOptions options) internal static bool HasSerilogScopeEventProcessor(this SentryOptions options) => options.EventProcessors.Any(processor => processor.Type == typeof(SerilogScopeEventProcessor)); - } From 14e59b114f33134ec73e91dd6d8a1bd5c64ef863 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 23 Sep 2026 16:30:56 +1200 Subject: [PATCH 28/42] Tidy comments Co-authored-by: James Crosswell --- samples/Sentry.Samples.Log4Net/Program.cs | 2 +- samples/Sentry.Samples.Log4Net/app.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Sentry.Samples.Log4Net/Program.cs b/samples/Sentry.Samples.Log4Net/Program.cs index 5bc40b5fbb..ce7a4ec0ef 100644 --- a/samples/Sentry.Samples.Log4Net/Program.cs +++ b/samples/Sentry.Samples.Log4Net/Program.cs @@ -10,7 +10,7 @@ internal class Program private static void Main() { - // Initialise the Sentry SDK. The SentryAppender configured in app.config only sends log events to Sentry. + // Initialise the Sentry SDK. using var _ = SentrySdk.Init(options => { #if !SENTRY_DSN_DEFINED_IN_ENV diff --git a/samples/Sentry.Samples.Log4Net/app.config b/samples/Sentry.Samples.Log4Net/app.config index b61cde00af..4a6b80f74c 100644 --- a/samples/Sentry.Samples.Log4Net/app.config +++ b/samples/Sentry.Samples.Log4Net/app.config @@ -10,7 +10,7 @@ - +