-
-
Notifications
You must be signed in to change notification settings - Fork 235
feat(logs): add NLog integration
#5176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| namespace Sentry.NLog; | ||
|
|
||
| public sealed partial class SentryTarget | ||
| { | ||
| private static void CaptureStructuredLog(IHub hub, SentryOptions options, LogEventInfo logEvent) | ||
| { | ||
| var level = logEvent.Level.ToSentryLogLevel(); | ||
| if (level.HasValue) | ||
| { | ||
| DateTimeOffset timestamp = new(logEvent.TimeStamp); | ||
| GetStructuredLoggingParametersAndAttributes(logEvent, out var parameters, out var attributes); | ||
|
|
||
| var log = SentryLog.Create(hub, timestamp, level.Value, logEvent.FormattedMessage, logEvent.Message, parameters); | ||
|
|
||
| log.SetDefaultAttributes(options, Sdk); | ||
| log.SetOrigin("auto.log.nlog"); | ||
|
|
||
| foreach (var attribute in attributes) | ||
| { | ||
| log.SetAttribute(attribute.Key, attribute.Value); | ||
| } | ||
|
|
||
| hub.Logger.CaptureLog(log); | ||
| } | ||
| } | ||
|
|
||
| private static void GetStructuredLoggingParametersAndAttributes(LogEventInfo logEvent, out ImmutableArray<KeyValuePair<string, object>> parameters, out List<KeyValuePair<string, object>> attributes) | ||
| { | ||
| parameters = GetParameters(logEvent, out var parameterNames); | ||
| attributes = new List<KeyValuePair<string, object>>(); | ||
|
|
||
| if (logEvent.HasProperties) | ||
| { | ||
| foreach (var property in logEvent.Properties) | ||
| { | ||
| if (property.Key is string key && !string.IsNullOrWhiteSpace(key) && | ||
| property.Value is { } value && | ||
| !parameterNames.Contains(key)) | ||
| { | ||
| attributes.Add(new KeyValuePair<string, object>($"property.{key}", value)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static ImmutableArray<KeyValuePair<string, object>> GetParameters(LogEventInfo logEvent, out HashSet<string> parameterNames) | ||
| { | ||
| var parameters = logEvent.MessageTemplateParameters; | ||
|
|
||
| if (parameters.Count == 0) | ||
| { | ||
| parameterNames = new HashSet<string>(); | ||
| return ImmutableArray<KeyValuePair<string, object>>.Empty; | ||
| } | ||
|
|
||
| #if NETSTANDARD2_1_OR_GREATER || NET472_OR_GREATER || NETCOREAPP2_0_OR_GREATER | ||
| parameterNames = new HashSet<string>(parameters.Count); | ||
| #else | ||
| parameterNames = new HashSet<string>(); | ||
| #endif | ||
|
|
||
| var @params = ImmutableArray.CreateBuilder<KeyValuePair<string, object>>(parameters.Count); | ||
|
|
||
| foreach (var parameter in parameters) | ||
| { | ||
| parameterNames.Add(parameter.Name); | ||
| @params.Add(new KeyValuePair<string, object>(parameter.Name, parameter.Value)); | ||
| } | ||
|
|
||
| return @params.DrainToImmutable(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,7 @@ | |||||||||||||||||
| /// Sentry NLog Target. | ||||||||||||||||||
| /// </summary> | ||||||||||||||||||
| [Target("Sentry")] | ||||||||||||||||||
| public sealed class SentryTarget : TargetWithContext | ||||||||||||||||||
| public sealed partial class SentryTarget : TargetWithContext | ||||||||||||||||||
| { | ||||||||||||||||||
| // For testing: | ||||||||||||||||||
| internal Func<IHub> HubAccessor { get; } | ||||||||||||||||||
|
|
@@ -14,6 +14,12 @@ | |||||||||||||||||
|
|
||||||||||||||||||
| 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; | ||||||||||||||||||
|
|
@@ -129,6 +135,15 @@ | |||||||||||||||||
| set => Options.MinimumBreadcrumbLevel = LogLevel.FromString(value); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <summary> | ||||||||||||||||||
| /// Controls whether logs are generated and sent. | ||||||||||||||||||
| /// </summary> | ||||||||||||||||||
| public bool EnableLogs | ||||||||||||||||||
| { | ||||||||||||||||||
| get => Options.EnableLogs; | ||||||||||||||||||
| set => Options.EnableLogs = value; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <summary> | ||||||||||||||||||
| /// Whether the NLog integration should initialize the SDK. | ||||||||||||||||||
| /// </summary> | ||||||||||||||||||
|
|
@@ -331,6 +346,11 @@ | |||||||||||||||||
| var shouldOnlyLogExceptions = exception == null && IgnoreEventsWithNoException; | ||||||||||||||||||
| var shouldIncludeProperties = ContextProperties?.Count > 0 || ShouldIncludeProperties(logEvent); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (Options.EnableLogs) | ||||||||||||||||||
| { | ||||||||||||||||||
| CaptureStructuredLog(hub, Options, logEvent); | ||||||||||||||||||
| } | ||||||||||||||||||
|
Check warning on line 352 in src/Sentry.NLog/SentryTarget.cs
|
||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NLog reads EnableLogs from wrong options sourceHigh Severity The NLog integration checks Additional Locations (1)Reviewed by Cursor Bugbot for commit f558baf. Configure here.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This checks out - I think the same reasoning applies here: sentry-dotnet/src/Sentry.Serilog/SentrySink.cs Lines 168 to 175 in d3d2f71
NB: More evidence to support #5245 |
||||||||||||||||||
|
|
||||||||||||||||||
| if (logEvent.Level >= Options.MinimumEventLevel && !shouldOnlyLogExceptions) | ||||||||||||||||||
| { | ||||||||||||||||||
| var formatted = RenderLogEvent(Layout, logEvent); | ||||||||||||||||||
|
|
||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a big fan of splitting a single method out into a separate file (and the extra overhead that adds to the csproj file). My preference would be to simply add it to SentryLog.cs... the method name itself lets you know it's a factory method. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| namespace Sentry; | ||
|
|
||
| public sealed partial class SentryLog | ||
| { | ||
| internal static SentryLog Create(IHub hub, DateTimeOffset timestamp, SentryLogLevel level, string message, string? template, ImmutableArray<KeyValuePair<string, object>> parameters) | ||
| { | ||
| hub.GetTraceIdAndSpanId(out var traceId, out var spanId); | ||
|
|
||
| SentryLog log = new(timestamp, traceId, level, message) | ||
| { | ||
| Template = template, | ||
| Parameters = parameters, | ||
| SpanId = spanId, | ||
| }; | ||
|
|
||
| return log; | ||
| } | ||
| } |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially simplify this. I think the only scenario where we don't take this codepath is if the TFM is
netstandard2.0right?