Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/Sentry.Samples.NLog/NLog.config
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
ignoreEventsWithNoException="False"
includeEventDataOnBreadcrumbs="False"
includeEventPropertiesAsTags="True"
minimumEventLevel="Error">
minimumEventLevel="Error"
enableLogs="True">

<!-- Advanced options can be configured here-->
<options
Expand Down
1 change: 1 addition & 0 deletions samples/Sentry.Samples.NLog/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ private static void UsingCodeConfiguration()
options.MinimumEventLevel = LogLevel.Error; // Error and higher is sent as event (default is Error)

options.AttachStacktrace = true;
options.EnableLogs = true; // send structured logs to Sentry
options.SendDefaultPii = true; // Send Personal Identifiable information like the username of the user logged in to the device

options.IncludeEventDataOnBreadcrumbs = true; // Optionally include event properties with breadcrumbs
Expand Down
15 changes: 15 additions & 0 deletions src/Sentry.NLog/LogLevelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,19 @@ public static BreadcrumbLevel ToBreadcrumbLevel(this LogLevel level)
_ => BreadcrumbLevel.Info
};
}

public static SentryLogLevel? ToSentryLogLevel(this LogLevel level)
{
return level.Name switch
{
nameof(LogLevel.Trace) => SentryLogLevel.Trace,
nameof(LogLevel.Debug) => SentryLogLevel.Debug,
nameof(LogLevel.Info) => SentryLogLevel.Info,
nameof(LogLevel.Warn) => SentryLogLevel.Warning,
nameof(LogLevel.Error) => SentryLogLevel.Error,
nameof(LogLevel.Fatal) => SentryLogLevel.Fatal,
nameof(LogLevel.Off) => null,
_ => null,
};
}
}
6 changes: 6 additions & 0 deletions src/Sentry.NLog/Sentry.NLog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@
<InternalsVisibleTo Include="Sentry.NLog.Tests" PublicKey="$(SentryPublicKey)" />
</ItemGroup>

<ItemGroup>
<Compile Update="SentryTarget.Structured.cs">
<DependentUpon>SentryTarget.cs</DependentUpon>
</Compile>
</ItemGroup>

</Project>
72 changes: 72 additions & 0 deletions src/Sentry.NLog/SentryTarget.Structured.cs
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();
}
}
22 changes: 21 additions & 1 deletion src/Sentry.NLog/SentryTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Sentry.NLog;
/// Sentry NLog Target.
/// </summary>
[Target("Sentry")]
public sealed class SentryTarget : TargetWithContext
public sealed partial class SentryTarget : TargetWithContext
{
// For testing:
internal Func<IHub> HubAccessor { get; }
Expand All @@ -14,6 +14,12 @@ public sealed class SentryTarget : TargetWithContext

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;
Expand Down Expand Up @@ -129,6 +135,15 @@ public string MinimumBreadcrumbLevel
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>
Expand Down Expand Up @@ -331,6 +346,11 @@ private void InnerWrite(LogEventInfo logEvent)
var shouldOnlyLogExceptions = exception == null && IgnoreEventsWithNoException;
var shouldIncludeProperties = ContextProperties?.Count > 0 || ShouldIncludeProperties(logEvent);

if (Options.EnableLogs)
{
CaptureStructuredLog(hub, Options, logEvent);
}

if (logEvent.Level >= Options.MinimumEventLevel && !shouldOnlyLogExceptions)
{
var formatted = RenderLogEvent(Layout, logEvent);
Expand Down
3 changes: 3 additions & 0 deletions src/Sentry/Sentry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@
<Compile Update="MeasurementUnit.Duration.cs;MeasurementUnit.Fraction.cs;MeasurementUnit.Information.cs">
<DependentUpon>MeasurementUnit.cs</DependentUpon>
</Compile>
<Compile Update="SentryLog.Factory.cs">
<DependentUpon>SentryLog.cs</DependentUpon>
</Compile>
<Compile Update="SentryMetric.Factory.cs;SentryMetric.Generic.cs">
<DependentUpon>SentryMetric.cs</DependentUpon>
</Compile>
Expand Down
18 changes: 18 additions & 0 deletions src/Sentry/SentryLog.Factory.cs
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;
}
}
2 changes: 1 addition & 1 deletion src/Sentry/SentryLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Sentry;
/// Sentry .NET SDK Docs: <see href="https://docs.sentry.io/platforms/dotnet/logs/"/>.
/// </remarks>
[DebuggerDisplay(@"SentryLog \{ Level = {Level}, Message = '{Message}' \}")]
public sealed class SentryLog
public sealed partial class SentryLog
{
private readonly Dictionary<string, SentryAttribute> _attributes;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace Sentry.NLog
public NLog.Layouts.Layout? BreadcrumbCategory { get; set; }
public NLog.Layouts.Layout? BreadcrumbLayout { get; set; }
public NLog.Layouts.Layout? Dsn { get; set; }
public bool EnableLogs { get; set; }
public NLog.Layouts.Layout? Environment { get; set; }
public int FlushTimeoutSeconds { get; set; }
public bool IgnoreEventsWithNoException { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace Sentry.NLog
public NLog.Layouts.Layout? BreadcrumbCategory { get; set; }
public NLog.Layouts.Layout? BreadcrumbLayout { get; set; }
public NLog.Layouts.Layout? Dsn { get; set; }
public bool EnableLogs { get; set; }
public NLog.Layouts.Layout? Environment { get; set; }
public int FlushTimeoutSeconds { get; set; }
public bool IgnoreEventsWithNoException { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace Sentry.NLog
public NLog.Layouts.Layout? BreadcrumbCategory { get; set; }
public NLog.Layouts.Layout? BreadcrumbLayout { get; set; }
public NLog.Layouts.Layout? Dsn { get; set; }
public bool EnableLogs { get; set; }
public NLog.Layouts.Layout? Environment { get; set; }
public int FlushTimeoutSeconds { get; set; }
public bool IgnoreEventsWithNoException { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace Sentry.NLog
public NLog.Layouts.Layout? BreadcrumbCategory { get; set; }
public NLog.Layouts.Layout? BreadcrumbLayout { get; set; }
public NLog.Layouts.Layout? Dsn { get; set; }
public bool EnableLogs { get; set; }
public NLog.Layouts.Layout? Environment { get; set; }
public int FlushTimeoutSeconds { get; set; }
public bool IgnoreEventsWithNoException { get; set; }
Expand Down
6 changes: 6 additions & 0 deletions test/Sentry.NLog.Tests/Sentry.NLog.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@

</ItemGroup>

<ItemGroup>
<Compile Update="SentryTargetTests.Structured.cs">
<DependentUpon>SentryTargetTests.cs</DependentUpon>
</Compile>
</ItemGroup>

</Project>
Loading
Loading