Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add Serilog integration #118

Merged
merged 7 commits into from
Nov 21, 2018
Merged
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
25 changes: 23 additions & 2 deletions Sentry.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27705.0
Expand Down Expand Up @@ -71,7 +71,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Log4Net", "src\Sentr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Log4Net.Tests", "test\Sentry.Log4Net.Tests\Sentry.Log4Net.Tests.csproj", "{F275C86B-8F62-4070-9544-83CC5B68B751}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.GenericHost", "samples\Sentry.Samples.GenericHost\Sentry.Samples.GenericHost.csproj", "{5CBEFF17-71BF-407B-868F-C784E6385DC8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.GenericHost", "samples\Sentry.Samples.GenericHost\Sentry.Samples.GenericHost.csproj", "{5CBEFF17-71BF-407B-868F-C784E6385DC8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Serilog", "src\Sentry.Serilog\Sentry.Serilog.csproj", "{2CF1FBEF-93C4-404E-94F6-543D08842633}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Serilog.Tests", "test\Sentry.Serilog.Tests\Sentry.Serilog.Tests.csproj", "{EBABB411-4481-478B-BEAD-009D1EE6D259}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Serilog", "samples\Sentry.Samples.Serilog\Sentry.Samples.Serilog.csproj", "{B7EDB922-4024-4546-B6E4-E5AB9016369F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -147,6 +153,18 @@ Global
{5CBEFF17-71BF-407B-868F-C784E6385DC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CBEFF17-71BF-407B-868F-C784E6385DC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CBEFF17-71BF-407B-868F-C784E6385DC8}.Release|Any CPU.Build.0 = Release|Any CPU
{2CF1FBEF-93C4-404E-94F6-543D08842633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CF1FBEF-93C4-404E-94F6-543D08842633}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CF1FBEF-93C4-404E-94F6-543D08842633}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CF1FBEF-93C4-404E-94F6-543D08842633}.Release|Any CPU.Build.0 = Release|Any CPU
{EBABB411-4481-478B-BEAD-009D1EE6D259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBABB411-4481-478B-BEAD-009D1EE6D259}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBABB411-4481-478B-BEAD-009D1EE6D259}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBABB411-4481-478B-BEAD-009D1EE6D259}.Release|Any CPU.Build.0 = Release|Any CPU
{B7EDB922-4024-4546-B6E4-E5AB9016369F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7EDB922-4024-4546-B6E4-E5AB9016369F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7EDB922-4024-4546-B6E4-E5AB9016369F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7EDB922-4024-4546-B6E4-E5AB9016369F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -169,6 +187,9 @@ Global
{D1D3BD5F-0C3A-4DA4-BAA2-2E725E74176F} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{F275C86B-8F62-4070-9544-83CC5B68B751} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{5CBEFF17-71BF-407B-868F-C784E6385DC8} = {77454495-55EE-4B40-A089-71B9E8F82E89}
{2CF1FBEF-93C4-404E-94F6-543D08842633} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{EBABB411-4481-478B-BEAD-009D1EE6D259} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{B7EDB922-4024-4546-B6E4-E5AB9016369F} = {77454495-55EE-4B40-A089-71B9E8F82E89}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6}
Expand Down
50 changes: 50 additions & 0 deletions samples/Sentry.Samples.Serilog/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using Sentry.Serilog;
using Serilog;
using Serilog.Context;
using Serilog.Events;

internal class Program
{
private static void Main()
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Sentry("https://5fd7a6cda8444965bade9ccfd3df9882@sentry.io/1188141", restrictedToMinimumLevel: LogEventLevel.Information)
.CreateLogger();

// The following anonymous object gets serialized and sent with log messages
using (LogContext.PushProperty("inventory", new
{
SmallPotion = 3,
BigPotion = 0,
CheeseWheels = 512
}))
{
// Logger config enables the Sink only for level INFO or higher so the Debug
// Does not result in an event in Sentry
Log.Debug("Debug message which is not sent.");

try
{
DoWork();
}
catch (Exception e)
{
e.Data.Add("details", "Do work always throws.");
Log.Error(e, "Error: with exception");
}
}

Log.CloseAndFlush();
}

private static void DoWork()
{
Log.Information("About to throw {ExceptionType} type of exception.", nameof(NotImplementedException));

throw new NotImplementedException();
}
}
17 changes: 17 additions & 0 deletions samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net462</TargetFrameworks>
<Version>3.5.234</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry.Serilog/Sentry.Serilog.csproj" />
</ItemGroup>

</Project>
11 changes: 11 additions & 0 deletions src/Sentry.Serilog/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Sentry.Serilog
{
internal static class Constants
{
public const string SdkName = "sentry.dotnet.serilog";
}
}
30 changes: 30 additions & 0 deletions src/Sentry.Serilog/LevelMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
using Sentry.Protocol;
using Serilog.Events;

namespace Sentry.Serilog
{
internal static class LevelMapping
{
public static SentryLevel? ToSentryLevel(this LogEventLevel loggingLevel)
{
switch (loggingLevel)
{
case LogEventLevel.Fatal:
return SentryLevel.Fatal;
case LogEventLevel.Error:
return SentryLevel.Error;
case LogEventLevel.Warning:
return SentryLevel.Warning;
case LogEventLevel.Information:
return SentryLevel.Info;
case LogEventLevel.Debug:
return SentryLevel.Debug;
}

return null;
}
}
}
6 changes: 6 additions & 0 deletions src/Sentry.Serilog/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Sentry.Serilog.Tests,PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]

[assembly: CLSCompliant(true)]
20 changes: 20 additions & 0 deletions src/Sentry.Serilog/Sentry.Serilog.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageTags>$(PackageTags);Logging;Serilog</PackageTags>
<PackageId>Sentry.Serilog</PackageId>
<AssemblyName>Sentry.Serilog</AssemblyName>
<RootNamespace>Sentry.Serilog</RootNamespace>
<Description>Official Serilog integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry/Sentry.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="2.7.1" />
</ItemGroup>

</Project>
117 changes: 117 additions & 0 deletions src/Sentry.Serilog/SentrySink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Sentry.Extensibility;
using Sentry.Protocol;
using Sentry.Reflection;
using Serilog.Core;
using Serilog.Events;

namespace Sentry.Serilog
{
public sealed class SentrySink : ILogEventSink, IDisposable
{
private readonly IFormatProvider _formatProvider;
private readonly Func<string, IDisposable> _initAction;
private volatile IDisposable _sdkHandle;

private readonly object _initSync = new object();

internal static readonly (string Name, string Version) NameAndVersion
= typeof(SentrySink).Assembly.GetNameAndVersion();

private static readonly string ProtocolPackageName = "nuget:" + NameAndVersion.Name;

internal IHub Hub { get; set; }

public string Dsn { get; set; }

public SentrySink(IFormatProvider formatProvider) : this(formatProvider, SentrySdk.Init, HubAdapter.Instance)
{ }

internal SentrySink(
IFormatProvider formatProvider,
Func<string, IDisposable> initAction,
IHub hub)
{
Mitch528 marked this conversation as resolved.
Show resolved Hide resolved
Debug.Assert(initAction != null);
Debug.Assert(hub != null);

_formatProvider = formatProvider;
_initAction = initAction;
Hub = hub;
}

public void Emit(LogEvent logEvent)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Emit is simply raising an event to Sentry with any log message created.
It would be nicer to enable a support like the one done for Microsoft.Extensions.Logging which have 2 log levels switches. One controls what is sent to sentry (creating an event) and one controls what should be added as breadcrumb.

This is great because we can store logs messages like Information as breadcrumbs and in the event of a LogError all the Information from that same scope are sent out with the the error event.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entierly accurate. If you take a look here:

public static LoggerConfiguration Sentry(
this LoggerSinkConfiguration loggerConfiguration,
string dsn = null,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new SentrySink(formatProvider) { Dsn = dsn }, restrictedToMinimumLevel);
}

The restrictedToMinimumLevel option sets the minimum level for which Emit is called by Serilog. I'm not really sure what the best way would be to have a minimum breadcrumb level, since Serilog will not call Emit if the aforementioned option is, for example, set to Error and the miniumum breadcrumb level is set to Info.

We could override restrictedToMinimumLevel and set it to the lowest level and then add our own switch that checks it within Emit, but that seems to be a bit counterintuitive I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restrictedToMinimumLevel is the minimal level to either event or breadcrumb then.

For example: restrictedToMinimumLevel = Info, we would capture breadcrumbs (for Info or higher) and send events (error or higher)

If restrictedToMinimumLevel is Warning, we'd only capture breadcrumbs for Warnings or higher, and send events Error or higher.

Finally if restrictedToMinimumLevel is Error, then breadcrumbs would be tracked only for Error or higher as well as sending events.

A logging integration with Sentry that sends all Log.. to Sentry doesn't make much sense. Sentry is not a logging tool and flooding it with events that are not grouped together create a storm of noise (i.e: Notifications).

Keeping the Info/Warn logs around (breadcrumbs) for a while to be sent in case there's an Error is, in my opinion, the best approach as it gives you the context you need to troubleshoot an issue.

{
if (logEvent == null)
{
return;
}

if (!Hub.IsEnabled && _sdkHandle == null)
{
if (Dsn == null)
{
return;
}

lock (_initSync)
{
if (_sdkHandle == null)
{
_sdkHandle = _initAction(Dsn);
Debug.Assert(_sdkHandle != null);
}
}
}

var exception = logEvent.Exception;

var evt = new SentryEvent(exception)
{
Sdk =
{
Name = Constants.SdkName,
Version = NameAndVersion.Version
},
LogEntry = new LogEntry
{
Formatted = logEvent.RenderMessage(_formatProvider),
Message = logEvent.MessageTemplate.Text
},
Level = logEvent.Level.ToSentryLevel()
};

evt.Sdk.AddPackage(ProtocolPackageName, NameAndVersion.Version);

evt.SetExtras(GetLoggingEventProperties(logEvent));

Hub.CaptureEvent(evt);
}

private IEnumerable<KeyValuePair<string, object>> GetLoggingEventProperties(LogEvent logEvent)
{
var properties = logEvent.Properties;

foreach (var property in properties)
{
var value = property.Value;
if (value is ScalarValue scalarValue)
{
yield return new KeyValuePair<string, object>(property.Key, scalarValue.Value);
}
else if (value != null)
{
yield return new KeyValuePair<string, object>(property.Key, value);
}
}
}

public void Dispose()
{
_sdkHandle?.Dispose();
}
}
}
21 changes: 21 additions & 0 deletions src/Sentry.Serilog/SentrySinkExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using Serilog;
using Serilog.Configuration;
using Serilog.Events;

namespace Sentry.Serilog
{
public static class SentrySinkExtensions
{
public static LoggerConfiguration Sentry(
this LoggerSinkConfiguration loggerConfiguration,
string dsn = null,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new SentrySink(formatProvider) { Dsn = dsn }, restrictedToMinimumLevel);
}
}
}
1 change: 1 addition & 0 deletions src/Sentry/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[assembly: InternalsVisibleTo("Sentry.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.Testing, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.Log4Net.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.Serilog.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.AspNetCore, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
[assembly: InternalsVisibleTo("Sentry.Extensions.Logging, PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")]
Expand Down
11 changes: 11 additions & 0 deletions test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0;net462</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry.Serilog/Sentry.Serilog.csproj" />
</ItemGroup>

</Project>
Loading