diff --git a/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs b/samples/Sentry.Samples.AspNetCore.Serilog/Program.cs index 65d3d64732..ff974a903f 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,28 @@ public static WebApplication BuildWebApp(string[] args) c.Enrich.FromLogContext() .MinimumLevel.Debug() .WriteTo.Console() - // Add Sentry integration with Serilog + // 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 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"; ...) + // 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 - 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..b4c0075c56 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 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; + // 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. + // Configure Serilog to send logs to Sentry .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) + // Error and higher are sent as events (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); });