From 36fde274b235d6fcf795199b308ae6eb3d7d1256 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Mon, 14 Sep 2026 12:42:15 +1200 Subject: [PATCH 01/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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 0aada8780c3b9c23503ce54dbcb88a0525850aec Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 09:30:15 +1200 Subject: [PATCH 07/23] 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 08/23] 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 3307c301f80b1ebde8aef6abf507faf2aabd158e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 22 Sep 2026 18:07:14 +1200 Subject: [PATCH 09/23] 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 10/23] 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 72d27f9ddfc5656d2592b70a0e5133c4613e2027 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Tue, 22 Sep 2026 12:18:22 +0200 Subject: [PATCH 11/23] 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 12/23] 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 13/23] 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 14/23] 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 15/23] 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 a1bf334dd3eb0751c129698db0230b42501b58ee Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Wed, 23 Sep 2026 03:47:56 +0200 Subject: [PATCH 16/23] 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 17/23] 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 18/23] 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 fbbaa39977a2aa3febbdedf597a07dddf4836799 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 24 Sep 2026 10:59:09 +1200 Subject: [PATCH 19/23] docs: samples are exempt from the no-comments rule Restores the DSN comment dropped from the Serilog sample's appsettings.json, pointing at where this sample actually sets it, and records in AGENTS.md that "prefer no comments" covers the library rather than samples - including their JSON configuration files. Part of #5245 Co-Authored-By: Claude Opus 5 --- AGENTS.md | 6 ++++++ samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json | 1 + 2 files changed, 7 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 9923b72ca8..4f35f6cc3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -181,6 +181,12 @@ Do not add comments that restate what the code already says. In particular, don' members as being exposed for tests (`// Exposed for tests`) — that's already apparent from the member being `internal` and from tests being its only callers. +**This applies to the library, not to `samples/`.** Samples are documentation: people read them +to learn how to wire Sentry up, and copy them as a starting point. Comment them generously, +including the configuration files — `appsettings.json` and the like carry `//` comments in this +repo even though JSON doesn't officially support them, and that has never caused a problem. +When a sample changes, update its comments rather than dropping them. + ## Adding New Options (AOT Compatibility) `SentryOptions` is **not** bound directly from configuration. Instead, a parallel `BindableSentryOptions` class (`src/Sentry/BindableSentryOptions.cs`) exists for AOT-safe configuration binding. diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json b/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json index df11df3e18..13721ccdfa 100644 --- a/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json +++ b/samples/Sentry.Samples.AspNetCore.Serilog/appsettings.json @@ -2,6 +2,7 @@ // Example configuration via JSON. Only a subset of the settings below: // All Sentry settings can also be configured via code or environment variables: "Sentry": { + // This sample sets the DSN in Program.cs. You can set it here instead, or via the SENTRY_DSN environment variable. // 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 5f4f1d187ffe61dfbc64b7338cbb32c764901371 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 24 Sep 2026 11:07:54 +1200 Subject: [PATCH 20/23] Apply suggestion from @jamescrosswell --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 4f35f6cc3f..0de21ccd18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -182,7 +182,7 @@ members as being exposed for tests (`// Exposed for tests`) — that's already a the member being `internal` and from tests being its only callers. **This applies to the library, not to `samples/`.** Samples are documentation: people read them -to learn how to wire Sentry up, and copy them as a starting point. Comment them generously, +to learn how to wire Sentry up, and copy them as a starting point. Comment them concisely but clearly, including the configuration files — `appsettings.json` and the like carry `//` comments in this repo even though JSON doesn't officially support them, and that has never caused a problem. When a sample changes, update its comments rather than dropping them. From 2925072e71b582c743e9b07114e310ae32b79222 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 29 Sep 2026 12:12:11 +1300 Subject: [PATCH 21/23] feat(serilog): warn at runtime when the sink drops events because Sentry is not initialized The tombstoned overloads catch everyone who passes a DSN to the sink, but they cannot see the `WriteTo.Sentry(o => ...)` callback that only sets sink options and gets its DSN from SENTRY_DSN or a [Dsn] assembly attribute. On 6.x that overload initialized the SDK itself; now it compiles, nothing calls Init, and the sink drops everything silently. Warn once, on the first event at or above MinimumEventLevel, when the hub is disabled and a DSN can still be found. There is no DiagnosticLogger to write to in that state, so the warning goes to Serilog's SelfLog and to standard error. Co-Authored-By: Claude Opus 5 --- src/Sentry.Serilog/SentrySink.cs | 19 +++- src/Sentry/Internal/SettingLocator.cs | 19 ++++ .../Internal/UninitializedSdkWarning.cs | 35 +++++++ .../SentrySinkUninitializedSdkTests.cs | 80 +++++++++++++++ .../Internals/SettingLocatorTests.cs | 47 +++++++++ .../Internals/UninitializedSdkWarningTests.cs | 97 +++++++++++++++++++ 6 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 src/Sentry/Internal/UninitializedSdkWarning.cs create mode 100644 test/Sentry.Serilog.Tests/SentrySinkUninitializedSdkTests.cs create mode 100644 test/Sentry.Tests/Internals/UninitializedSdkWarningTests.cs diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 9a6005b1b4..5bfbf230cc 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -1,3 +1,5 @@ +using Serilog.Debugging; + namespace Sentry.Serilog; /// @@ -10,9 +12,16 @@ internal sealed partial class SentrySink : ILogEventSink private readonly Func _hubAccessor; private readonly ISystemClock _clock; + private readonly UninitializedSdkWarning _uninitializedSdkWarning; private int _registeredScopeEventProcessor; + internal const string UninitializedSdkMessage = + "Sentry: the Serilog sink dropped a log event because Sentry is not initialized, but a DSN was found " + + "in the environment or in an assembly attribute. The sink no longer initializes the SDK: call " + + "SentrySdk.Init (or UseSentry via one of the integrations) at startup and UseSerilog() on those options. " + + "See https://docs.sentry.io/platforms/dotnet/guides/serilog/"; + public SentrySink(SentrySerilogOptions options) : this( options, @@ -24,11 +33,14 @@ public SentrySink(SentrySerilogOptions options) internal SentrySink( SentrySerilogOptions options, Func hubAccessor, - ISystemClock clock) + ISystemClock clock, + UninitializedSdkWarning? uninitializedSdkWarning = null) { _options = options; _hubAccessor = hubAccessor; _clock = clock; + _uninitializedSdkWarning = uninitializedSdkWarning + ?? new UninitializedSdkWarning(message => SelfLog.WriteLine("{0}", message)); if (hubAccessor() is { IsEnabled: true } hub && hub.GetSentryOptions() is { } sentryOptions) { @@ -68,6 +80,11 @@ private void InnerEmit(LogEvent logEvent, string? context) { if (_hubAccessor() is not { IsEnabled: true } hub) { + if (logEvent.Level >= _options.MinimumEventLevel) + { + _uninitializedSdkWarning.WarnOnce(UninitializedSdkMessage); + } + return; } diff --git a/src/Sentry/Internal/SettingLocator.cs b/src/Sentry/Internal/SettingLocator.cs index 4555ac7333..1afad80a18 100644 --- a/src/Sentry/Internal/SettingLocator.cs +++ b/src/Sentry/Internal/SettingLocator.cs @@ -75,6 +75,25 @@ public string GetDsn() return _options.Dsn!; } + /// + /// Probes the same sources as without throwing or assigning to the options. Returns + /// when nothing is configured, or when the nearest source disables Sentry with an empty DSN. + /// + internal string? TryGetDsn() + { + if (_options.Dsn is { } optionsDsn) + { + return optionsDsn.NullIfWhitespace(); + } + + if (GetEnvironmentVariable(Constants.DsnEnvironmentVariable) is { } environmentDsn) + { + return environmentDsn.NullIfWhitespace(); + } + + return AssemblyForAttributes?.GetCustomAttribute()?.Dsn.NullIfWhitespace(); + } + public string GetEnvironment() => GetEnvironment(true)!; public string? GetEnvironment(bool useDefaultIfNotFound) diff --git a/src/Sentry/Internal/UninitializedSdkWarning.cs b/src/Sentry/Internal/UninitializedSdkWarning.cs new file mode 100644 index 0000000000..0969789f51 --- /dev/null +++ b/src/Sentry/Internal/UninitializedSdkWarning.cs @@ -0,0 +1,35 @@ +namespace Sentry.Internal; + +/// +/// Warns, at most once, that a logging integration is dropping events because Sentry was never initialised +/// even though a DSN can be found. There is no diagnostic logger in that state, so the warning goes to the +/// logging framework's own diagnostics channel and to standard error. +/// +internal sealed class UninitializedSdkWarning +{ + private readonly Action? _writeToIntegrationLog; + private int _warned; + + public UninitializedSdkWarning(Action? writeToIntegrationLog = null) + => _writeToIntegrationLog = writeToIntegrationLog; + + public Func DsnLocator { get; set; } = static () => new SentryOptions().SettingLocator.TryGetDsn(); + + public Action WriteToStandardError { get; set; } = Console.Error.WriteLine; + + public void WarnOnce(string message) + { + if (Interlocked.Exchange(ref _warned, 1) != 0) + { + return; + } + + if (DsnLocator() is null) + { + return; + } + + _writeToIntegrationLog?.Invoke(message); + WriteToStandardError(message); + } +} diff --git a/test/Sentry.Serilog.Tests/SentrySinkUninitializedSdkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkUninitializedSdkTests.cs new file mode 100644 index 0000000000..bdd4186c63 --- /dev/null +++ b/test/Sentry.Serilog.Tests/SentrySinkUninitializedSdkTests.cs @@ -0,0 +1,80 @@ +namespace Sentry.Serilog.Tests; + +public class SentrySinkUninitializedSdkTests +{ + private readonly List _selfLog = []; + private readonly List _standardError = []; + + private SentrySink GetSut(bool sdkEnabled, string dsn = ValidDsn) + { + var hub = Substitute.For(); + hub.IsEnabled.Returns(sdkEnabled); + hub.Logger.Returns(new InMemorySentryStructuredLogger()); + + var warning = new UninitializedSdkWarning(_selfLog.Add) + { + DsnLocator = () => dsn, + WriteToStandardError = _standardError.Add + }; + + return new SentrySink(new SentrySerilogOptions(), () => hub, new MockClock(), warning); + } + + private static LogEvent LogEventAt(LogEventLevel level) => + new(DateTimeOffset.UtcNow, level, null, MessageTemplate.Empty, []); + + [Fact] + public void Emit_SdkNotInitialized_WarnsToSelfLogAndStandardError() + { + var sut = GetSut(sdkEnabled: false); + + sut.Emit(LogEventAt(LogEventLevel.Error)); + + Assert.Equal(new[] { SentrySink.UninitializedSdkMessage }, _selfLog); + Assert.Equal(new[] { SentrySink.UninitializedSdkMessage }, _standardError); + } + + [Fact] + public void Emit_SdkNotInitialized_WarnsOnceForManyEvents() + { + var sut = GetSut(sdkEnabled: false); + + sut.Emit(LogEventAt(LogEventLevel.Error)); + sut.Emit(LogEventAt(LogEventLevel.Fatal)); + + Assert.Single(_standardError); + } + + [Fact] + public void Emit_SdkNotInitializedAndBelowMinimumEventLevel_DoesNotWarn() + { + var sut = GetSut(sdkEnabled: false); + + sut.Emit(LogEventAt(LogEventLevel.Warning)); + + Assert.Empty(_selfLog); + Assert.Empty(_standardError); + } + + [Fact] + public void Emit_SdkNotInitializedAndNoDsnFound_DoesNotWarn() + { + var sut = GetSut(sdkEnabled: false, dsn: null); + + sut.Emit(LogEventAt(LogEventLevel.Error)); + + Assert.Empty(_selfLog); + Assert.Empty(_standardError); + } + + [Fact] + public void Emit_SdkInitialized_DoesNotWarn() + { + var sut = GetSut(sdkEnabled: true); + + sut.Emit(LogEventAt(LogEventLevel.Error)); + + Assert.Empty(_selfLog); + Assert.Empty(_standardError); + } +} diff --git a/test/Sentry.Tests/Internals/SettingLocatorTests.cs b/test/Sentry.Tests/Internals/SettingLocatorTests.cs index 9491a410a9..f9fe0e3ade 100644 --- a/test/Sentry.Tests/Internals/SettingLocatorTests.cs +++ b/test/Sentry.Tests/Internals/SettingLocatorTests.cs @@ -342,4 +342,51 @@ public void GetRelease_WithNoValueAnywhere_ReturnsAndSetsDefault() Assert.Equal(expected, release); Assert.Equal(expected, options.Release); } + + [Fact] + public void TryGetDsn_WithNoValueAnywhere_ReturnsNull() + { + var options = new SentryOptions(); + options.FakeSettings().AssemblyForAttributes = AssemblyCreationHelper.CreateAssembly(); + + Assert.Null(options.SettingLocator.TryGetDsn()); + } + + [Fact] + public void TryGetDsn_WithDsnInEnvironmentVariable_ReturnsDsnWithoutSettingIt() + { + var options = new SentryOptions(); + options.FakeSettings().EnvironmentVariables[DsnEnvironmentVariable] = ValidDsn; + + Assert.Equal(ValidDsn, options.SettingLocator.TryGetDsn()); + Assert.Null(options.Dsn); + } + + [Fact] + public void TryGetDsn_WithDsnInAttribute_ReturnsDsn() + { + var options = new SentryOptions(); + options.FakeSettings().AssemblyForAttributes = GetAssemblyWithDsn(ValidDsn); + + Assert.Equal(ValidDsn, options.SettingLocator.TryGetDsn()); + } + + [Fact] + public void TryGetDsn_WithDisabledDsnInEnvironmentVariableButValidDsnInAttribute_ReturnsNull() + { + var options = new SentryOptions(); + options.FakeSettings().EnvironmentVariables[DsnEnvironmentVariable] = DisableSdkDsnValue; + options.FakeSettings().AssemblyForAttributes = GetAssemblyWithDsn(ValidDsn); + + Assert.Null(options.SettingLocator.TryGetDsn()); + } + + [Fact] + public void TryGetDsn_WithDisabledDsnInOptions_ReturnsNull() + { + var options = new SentryOptions { Dsn = DisableSdkDsnValue }; + options.FakeSettings().EnvironmentVariables[DsnEnvironmentVariable] = ValidDsn; + + Assert.Null(options.SettingLocator.TryGetDsn()); + } } diff --git a/test/Sentry.Tests/Internals/UninitializedSdkWarningTests.cs b/test/Sentry.Tests/Internals/UninitializedSdkWarningTests.cs new file mode 100644 index 0000000000..cfb95ac02c --- /dev/null +++ b/test/Sentry.Tests/Internals/UninitializedSdkWarningTests.cs @@ -0,0 +1,97 @@ +namespace Sentry.Tests.Internals; + +public class UninitializedSdkWarningTests +{ + private const string Message = "Sentry is not initialized."; + + private readonly List _integrationLog = []; + private readonly List _standardError = []; + + private UninitializedSdkWarning GetSut(string dsn = ValidDsn) => + new(_integrationLog.Add) + { + DsnLocator = () => dsn, + WriteToStandardError = _standardError.Add + }; + + [Fact] + public void WarnOnce_DsnFound_WritesToBothChannels() + { + GetSut().WarnOnce(Message); + + Assert.Equal(new[] { Message }, _integrationLog); + Assert.Equal(new[] { Message }, _standardError); + } + + [Fact] + public void WarnOnce_NoDsnFound_WritesNothing() + { + GetSut(dsn: null).WarnOnce(Message); + + Assert.Empty(_integrationLog); + Assert.Empty(_standardError); + } + + [Fact] + public void WarnOnce_CalledRepeatedly_WritesOnce() + { + var sut = GetSut(); + + sut.WarnOnce(Message); + sut.WarnOnce(Message); + sut.WarnOnce(Message); + + Assert.Equal(new[] { Message }, _integrationLog); + Assert.Equal(new[] { Message }, _standardError); + } + + [Fact] + public void WarnOnce_CalledConcurrently_WritesOnce() + { + var integrationWrites = 0; + var standardErrorWrites = 0; + var sut = new UninitializedSdkWarning(_ => Interlocked.Increment(ref integrationWrites)) + { + DsnLocator = () => ValidDsn, + WriteToStandardError = _ => Interlocked.Increment(ref standardErrorWrites) + }; + + Parallel.For(0, 64, _ => sut.WarnOnce(Message)); + + Assert.Equal(1, integrationWrites); + Assert.Equal(1, standardErrorWrites); + } + + [Fact] + public void WarnOnce_NoIntegrationLog_StillWritesToStandardError() + { + var sut = new UninitializedSdkWarning + { + DsnLocator = () => ValidDsn, + WriteToStandardError = _standardError.Add + }; + + sut.WarnOnce(Message); + + Assert.Equal(new[] { Message }, _standardError); + } + + [Fact] + public void DsnLocator_ByDefault_ReadsTheEnvironment() + { + var variable = Constants.DsnEnvironmentVariable; + var original = Environment.GetEnvironmentVariable(variable); + try + { + Environment.SetEnvironmentVariable(variable, ValidDsn); + Assert.Equal(ValidDsn, new UninitializedSdkWarning().DsnLocator()); + + Environment.SetEnvironmentVariable(variable, SentryConstants.DisableSdkDsnValue); + Assert.Null(new UninitializedSdkWarning().DsnLocator()); + } + finally + { + Environment.SetEnvironmentVariable(variable, original); + } + } +} From 0a57f71a67c444f453c5c4d977b24a176ee98a61 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 29 Sep 2026 12:15:38 +1300 Subject: [PATCH 22/23] feat(nlog): warn at runtime when the target drops events because Sentry is not initialized Mirrors the Serilog sink: the tombstoned Dsn/InitializeSdk properties cannot see an AddSentry(o => ...) call that only sets target options and gets its DSN from SENTRY_DSN or a [Dsn] assembly attribute, so warn once on the first event at or above MinimumEventLevel when the hub is disabled and a DSN can still be found. The warning goes to NLog's InternalLogger and to standard error. Co-Authored-By: Claude Opus 5 --- src/Sentry.NLog/Sentry.NLog.csproj | 1 + src/Sentry.NLog/SentryTarget.cs | 20 ++++- .../SentryTargetUninitializedSdkTests.cs | 80 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/Sentry.NLog.Tests/SentryTargetUninitializedSdkTests.cs diff --git a/src/Sentry.NLog/Sentry.NLog.csproj b/src/Sentry.NLog/Sentry.NLog.csproj index 66aa9ef4b2..c1834bcaac 100644 --- a/src/Sentry.NLog/Sentry.NLog.csproj +++ b/src/Sentry.NLog/Sentry.NLog.csproj @@ -28,6 +28,7 @@ + diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index 643a44ead2..d26eff2eb1 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -10,9 +10,16 @@ public sealed partial class SentryTarget : TargetWithContext internal Func HubAccessor { get; } private readonly ISystemClock _clock; + private readonly UninitializedSdkWarning _uninitializedSdkWarning; internal static readonly string AdditionalGroupingKeyProperty = "AdditionalGroupingKey"; + internal const string UninitializedSdkMessage = + "Sentry: the Sentry target for NLog dropped a log event because Sentry is not initialized, but a DSN " + + "was found in the environment or in an assembly attribute. The target no longer initializes the SDK: " + + "call SentrySdk.Init (or UseSentry via one of the integrations) at startup. " + + "See https://docs.sentry.io/platforms/dotnet/guides/nlog/"; + /// /// Creates a new instance of . /// @@ -31,11 +38,17 @@ public SentryTarget(SentryNLogOptions options) { } - internal SentryTarget(SentryNLogOptions options, Func hubAccessor, ISystemClock clock) + internal SentryTarget( + SentryNLogOptions options, + Func hubAccessor, + ISystemClock clock, + UninitializedSdkWarning? uninitializedSdkWarning = null) { Options = options; HubAccessor = hubAccessor; _clock = clock; + _uninitializedSdkWarning = uninitializedSdkWarning + ?? new UninitializedSdkWarning(message => InternalLogger.Warn("{0}", message)); // Overrides default layout. Still will be explicitly overwritten if manually configured in the // NLog.config file. @@ -251,6 +264,11 @@ private void InnerWrite(LogEventInfo logEvent) var hub = HubAccessor(); if (!hub.IsEnabled) { + if (logEvent.Level >= Options.MinimumEventLevel) + { + _uninitializedSdkWarning.WarnOnce(UninitializedSdkMessage); + } + return; } diff --git a/test/Sentry.NLog.Tests/SentryTargetUninitializedSdkTests.cs b/test/Sentry.NLog.Tests/SentryTargetUninitializedSdkTests.cs new file mode 100644 index 0000000000..9cf269f737 --- /dev/null +++ b/test/Sentry.NLog.Tests/SentryTargetUninitializedSdkTests.cs @@ -0,0 +1,80 @@ +namespace Sentry.NLog.Tests; + +public class SentryTargetUninitializedSdkTests +{ + private readonly List _internalLog = []; + private readonly List _standardError = []; + + private Logger GetLogger(bool sdkEnabled, string dsn = ValidDsn) + { + var hub = Substitute.For(); + hub.IsEnabled.Returns(sdkEnabled); + hub.Logger.Returns(new InMemorySentryStructuredLogger()); + + var warning = new UninitializedSdkWarning(_internalLog.Add) + { + DsnLocator = () => dsn, + WriteToStandardError = _standardError.Add + }; + + var target = new SentryTarget(new SentryNLogOptions(), () => hub, new MockClock(), warning) + { + Name = "sentry" + }; + + var factory = new LogFactory(); + var configuration = new LoggingConfiguration(factory); + configuration.AddTarget("sentry", target); + configuration.AddRule(LogLevel.Trace, LogLevel.Fatal, target); + factory.Configuration = configuration; + + return factory.GetLogger("sentry"); + } + + [Fact] + public void Write_SdkNotInitialized_WarnsToInternalLoggerAndStandardError() + { + GetLogger(sdkEnabled: false).Error("message"); + + Assert.Equal(new[] { SentryTarget.UninitializedSdkMessage }, _internalLog); + Assert.Equal(new[] { SentryTarget.UninitializedSdkMessage }, _standardError); + } + + [Fact] + public void Write_SdkNotInitialized_WarnsOnceForManyEvents() + { + var logger = GetLogger(sdkEnabled: false); + + logger.Error("first"); + logger.Fatal("second"); + + Assert.Single(_standardError); + } + + [Fact] + public void Write_SdkNotInitializedAndBelowMinimumEventLevel_DoesNotWarn() + { + GetLogger(sdkEnabled: false).Warn("message"); + + Assert.Empty(_internalLog); + Assert.Empty(_standardError); + } + + [Fact] + public void Write_SdkNotInitializedAndNoDsnFound_DoesNotWarn() + { + GetLogger(sdkEnabled: false, dsn: null).Error("message"); + + Assert.Empty(_internalLog); + Assert.Empty(_standardError); + } + + [Fact] + public void Write_SdkInitialized_DoesNotWarn() + { + GetLogger(sdkEnabled: true).Error("message"); + + Assert.Empty(_internalLog); + Assert.Empty(_standardError); + } +} From ae9cdda13b709ae27f221b0334335f64830acac0 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Thu, 1 Oct 2026 01:17:40 +0200 Subject: [PATCH 23/23] fix(nlog): report a stale dsn and accept initializeSdk=false (#5652) * fix(nlog): Report a stale dsn and accept initializeSdk=false Both tombstones could leave an upgraded app worse off than it needed to be. A dsn left in NLog.config was silent with NLog's default settings. NLog swallows the setter's exception unless throwConfigExceptions is on, so the target attached, Sentry was never initialized and nothing was printed. The runtime warning also stayed quiet, because it only looks for a DSN in the environment or an assembly attribute. The Dsn setter now writes the migration message to standard error before it throws. initializeSdk="false" was the recommended v6 setting next to UseSentry, and it already matches the new behavior. With throwConfigExceptions on, it still threw, NLog rejected the whole configuration and the app lost every NLog target. The setter now only throws for true, like the Microsoft.Extensions.Logging tombstone. Co-Authored-By: Claude Opus 5.5 * fix(nlog): report a stale initializeSdk="true" as well The stale dsn message only came from the Dsn setter, so a config carrying initializeSdk="true" instead was still silent with NLog's default throwConfigExceptions: the setter threw, NLog discarded it, the target attached and nothing was printed. Both setters now go through one report-and-throw helper. Reporting from both setters means a v6 config carrying dsn and initializeSdk="true" together would print the same message twice, so the helper reports at most once per target. A configuration reload builds a new target, and so reports again. Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 5.5 Co-authored-by: James Crosswell --- src/Sentry.NLog/SentryTarget.cs | 23 +++++++- .../SentryTargetConfigurationBindingTests.cs | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/Sentry.NLog/SentryTarget.cs b/src/Sentry.NLog/SentryTarget.cs index 596c895d4a..d2a2eaf6c8 100644 --- a/src/Sentry.NLog/SentryTarget.cs +++ b/src/Sentry.NLog/SentryTarget.cs @@ -11,6 +11,7 @@ public sealed partial class SentryTarget : TargetWithContext private readonly ISystemClock _clock; private readonly UninitializedSdkWarning _uninitializedSdkWarning; + private bool _reportedUnsupportedSdkSetting; internal static readonly string AdditionalGroupingKeyProperty = "AdditionalGroupingKey"; @@ -76,7 +77,7 @@ internal SentryTarget( public Layout? Dsn { get => null; - set => throw new NotSupportedException(ConfigurationExtensions.ObsoleteDsnOverload); + set => throw ReportUnsupportedSdkSetting(); } /// @@ -87,7 +88,13 @@ public Layout? Dsn public bool InitializeSdk { get => false; - set => throw new NotSupportedException(ConfigurationExtensions.ObsoleteDsnOverload); + set + { + if (value) + { + throw ReportUnsupportedSdkSetting(); + } + } } /// @@ -181,6 +188,18 @@ public SentryNLogUser? User set => Options.User = value; } + // NLog discards the exception unless throwConfigExceptions is on. Report once: a v6 config may set both. + private NotSupportedException ReportUnsupportedSdkSetting() + { + if (!_reportedUnsupportedSdkSetting) + { + _reportedUnsupportedSdkSetting = true; + _uninitializedSdkWarning.WriteToStandardError("Sentry: " + ConfigurationExtensions.ObsoleteDsnOverload); + } + + return new NotSupportedException(ConfigurationExtensions.ObsoleteDsnOverload); + } + /// protected override void InitializeTarget() { diff --git a/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs b/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs index a25df0ed93..b367f2b9e4 100644 --- a/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs +++ b/test/Sentry.NLog.Tests/SentryTargetConfigurationBindingTests.cs @@ -33,6 +33,12 @@ public void LoadConfiguration_WithTargetSettings_DoesNotThrow() Assert.Null(LoadConfiguration("minimumEventLevel='Warn' includeEventPropertiesAsTags='true'")); } + [Fact] + public void LoadConfiguration_WithInitializeSdkFalse_DoesNotThrow() + { + Assert.Null(LoadConfiguration("initializeSdk='false'")); + } + [Theory] [InlineData(2)] [InlineData(3)] @@ -67,6 +73,55 @@ public void AddSentry_DsnOverload_IsObsoleteAsError(int parameterCount) Assert.True(obsolete!.IsError); } + [Fact] + public void Dsn_WhenSet_WritesMigrationMessageToStandardError() + { + var standardError = new List(); + + Set(GetTarget(standardError), "Dsn", (Layout)ValidDsn); + + Assert.Contains(ConfigurationExtensions.ObsoleteDsnOverload, Assert.Single(standardError)); + } + + [Fact] + public void InitializeSdk_WhenSetToTrue_WritesMigrationMessageToStandardError() + { + var standardError = new List(); + + Set(GetTarget(standardError), "InitializeSdk", true); + + Assert.Contains(ConfigurationExtensions.ObsoleteDsnOverload, Assert.Single(standardError)); + } + + [Fact] + public void InitializeSdk_WhenSetToFalse_WritesNothing() + { + var standardError = new List(); + + Set(GetTarget(standardError), "InitializeSdk", false); + + Assert.Empty(standardError); + } + + [Fact] + public void ReportUnsupportedSdkSetting_BothSettingsOnOneTarget_WritesOnce() + { + var standardError = new List(); + var target = GetTarget(standardError); + + Set(target, "Dsn", (Layout)ValidDsn); + Set(target, "InitializeSdk", true); + + Assert.Single(standardError); + } + + private static SentryTarget GetTarget(List standardError) => + new(new SentryNLogOptions(), () => Substitute.For(), new MockClock(), + new UninitializedSdkWarning { WriteToStandardError = standardError.Add }); + + private static void Set(SentryTarget target, string property, object value) => + Record.Exception(() => typeof(SentryTarget).GetProperty(property)!.SetValue(target, value)); + private static MethodInfo DsnOverload(int parameterCount) => typeof(ConfigurationExtensions) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(m => m.Name == nameof(ConfigurationExtensions.AddSentry)