From e5e6895b15e1a0726b7f4987ef4bb3621ee5e66b Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Thu, 11 Oct 2018 13:32:38 -0700 Subject: [PATCH 1/7] Add Serilog integration --- Sentry.sln | 18 +- src/Sentry.Serilog/Constants.cs | 11 + src/Sentry.Serilog/LevelMapping.cs | 30 +++ src/Sentry.Serilog/Properties/AssemblyInfo.cs | 6 + src/Sentry.Serilog/Sentry.Serilog.csproj | 20 ++ src/Sentry.Serilog/SentrySink.cs | 113 ++++++++++ src/Sentry.Serilog/SentrySinkExtensions.cs | 18 ++ src/Sentry/Properties/AssemblyInfo.cs | 1 + .../Sentry.Serilog.Tests.csproj | 11 + test/Sentry.Serilog.Tests/SentrySinkTests.cs | 200 ++++++++++++++++++ 10 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 src/Sentry.Serilog/Constants.cs create mode 100644 src/Sentry.Serilog/LevelMapping.cs create mode 100644 src/Sentry.Serilog/Properties/AssemblyInfo.cs create mode 100644 src/Sentry.Serilog/Sentry.Serilog.csproj create mode 100644 src/Sentry.Serilog/SentrySink.cs create mode 100644 src/Sentry.Serilog/SentrySinkExtensions.cs create mode 100644 test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj create mode 100644 test/Sentry.Serilog.Tests/SentrySinkTests.cs diff --git a/Sentry.sln b/Sentry.sln index b9fb172df7..2c191c7a76 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27705.0 @@ -71,7 +71,11 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -147,6 +151,14 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -169,6 +181,8 @@ 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/src/Sentry.Serilog/Constants.cs b/src/Sentry.Serilog/Constants.cs new file mode 100644 index 0000000000..12d49ffa99 --- /dev/null +++ b/src/Sentry.Serilog/Constants.cs @@ -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"; + } +} diff --git a/src/Sentry.Serilog/LevelMapping.cs b/src/Sentry.Serilog/LevelMapping.cs new file mode 100644 index 0000000000..042430ef6f --- /dev/null +++ b/src/Sentry.Serilog/LevelMapping.cs @@ -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; + } + } +} diff --git a/src/Sentry.Serilog/Properties/AssemblyInfo.cs b/src/Sentry.Serilog/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4caf74c58c --- /dev/null +++ b/src/Sentry.Serilog/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Sentry.Serilog.Tests,PublicKey=002400000480000094000000060200000024000052534131000400000100010059964a931488bcdbd14657f1ee0df32df61b57b3d14d7290c262c2cc9ddaad6ec984044f761f778e1823049d2cb996a4f58c8ea5b46c37891414cb34b4036b1c178d7b582289d2eef3c0f1e9b692c229a306831ee3d371d9e883f0eb0f74aeac6c6ab8c85fd1ec04b267e15a31532c4b4e2191f5980459db4dce0081f1050fb8")] + +[assembly: CLSCompliant(true)] diff --git a/src/Sentry.Serilog/Sentry.Serilog.csproj b/src/Sentry.Serilog/Sentry.Serilog.csproj new file mode 100644 index 0000000000..c5308f944f --- /dev/null +++ b/src/Sentry.Serilog/Sentry.Serilog.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + $(PackageTags);Logging;Serilog + Sentry.Serilog + Sentry.Serilog + Sentry.Serilog + Official Serilog integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time. + + + + + + + + + + + diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs new file mode 100644 index 0000000000..206724cf46 --- /dev/null +++ b/src/Sentry.Serilog/SentrySink.cs @@ -0,0 +1,113 @@ +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 _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 initAction, + IHub hubGetter) + { + Debug.Assert(initAction != null); + Debug.Assert(hubGetter != null); + + _formatProvider = formatProvider; + _initAction = initAction; + Hub = hubGetter; + } + + public void Emit(LogEvent logEvent) + { + if (logEvent == null) + { + return; + } + + if (_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 + }, + Message = logEvent.RenderMessage(_formatProvider), + Level = logEvent.Level.ToSentryLevel() + }; + + evt.Sdk.AddPackage(ProtocolPackageName, NameAndVersion.Version); + + evt.SetExtras(GetLoggingEventProperties(logEvent)); + + Hub.CaptureEvent(evt); + } + + private IEnumerable> 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(property.Key, scalarValue.Value); + } + else if (value != null) + { + yield return new KeyValuePair(property.Key, value); + } + } + } + + public void Dispose() + { + _sdkHandle?.Dispose(); + } + } +} diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs new file mode 100644 index 0000000000..971a31a703 --- /dev/null +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Serilog; +using Serilog.Configuration; + +namespace Sentry.Serilog +{ + public static class SentrySinkExtensions + { + public static LoggerConfiguration Sentry( + this LoggerSinkConfiguration loggerConfiguration, + IFormatProvider formatProvider = null) + { + return loggerConfiguration.Sink(new SentrySink(formatProvider)); + } + } +} diff --git a/src/Sentry/Properties/AssemblyInfo.cs b/src/Sentry/Properties/AssemblyInfo.cs index 612622152a..37e48b2aee 100644 --- a/src/Sentry/Properties/AssemblyInfo.cs +++ b/src/Sentry/Properties/AssemblyInfo.cs @@ -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")] diff --git a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj new file mode 100644 index 0000000000..97ee8031bb --- /dev/null +++ b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj @@ -0,0 +1,11 @@ + + + + net462 + + + + + + + diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.cs new file mode 100644 index 0000000000..667606d974 --- /dev/null +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.cs @@ -0,0 +1,200 @@ +using System; +using System.Linq; +using NSubstitute; +using Sentry.Protocol; +using Sentry.Reflection; +using Serilog.Events; +using Serilog.Parsing; +using Xunit; + +namespace Sentry.Serilog.Tests +{ + public class SentrySinkTests + { + private class Fixture + { + public bool InitInvoked { get; set; } + public string DsnReceivedOnInit { get; set; } + public IDisposable SdkDisposeHandle { get; set; } = Substitute.For(); + public Func InitAction { get; set; } + public IHub Hub { get; set; } = Substitute.For(); + public string Dsn { get; set; } = "dsn"; + + public Fixture() + { + InitAction = s => + { + DsnReceivedOnInit = s; + InitInvoked = true; + return SdkDisposeHandle; + }; + } + + public SentrySink GetSut() + { + var sut = new SentrySink(null, InitAction, Hub) + { + Dsn = Dsn + }; + + return sut; + } + } + + private readonly Fixture _fixture = new Fixture(); + + [Fact] + public void Sink_WithException_CreatesEventWithException() + { + var sut = _fixture.GetSut(); + + var expected = new Exception("expected"); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, expected, MessageTemplate.Empty, + Enumerable.Empty()); + + sut.Emit(evt); + + _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Exception == expected)); + } + + [Fact] + public void Sink_SerilogSdk_Name() + { + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + + sut.Emit(evt); + + var expected = typeof(SentrySink).Assembly.GetNameAndVersion(); + _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Sdk.Name == Constants.SdkName + && e.Sdk.Version == expected.Version)); + } + + [Fact] + public void Sink_SerilogSdk_Packages() + { + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + + SentryEvent actual = null; + _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())) + .Do(c => actual = c.Arg()); + + sut.Emit(evt); + + var expected = typeof(SentrySink).Assembly.GetNameAndVersion(); + + Assert.NotNull(actual); + var package = Assert.Single(actual.Sdk.Packages); + Assert.Equal("nuget:" + expected.Name, package.Name); + Assert.Equal(expected.Version, package.Version); + + } + + [Fact] + public void Sink_LoggerLevel_Set() + { + const SentryLevel expectedLevel = SentryLevel.Error; + + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + + sut.Emit(evt); + + _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Level == expectedLevel)); + } + + [Fact] + public void Sink_RenderedMessage_Set() + { + const string expected = "message"; + + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, new MessageTemplate(string.Empty, + new[] { new TextToken(expected) }), Enumerable.Empty()); + + sut.Emit(evt); + + _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Message == expected)); + } + + [Fact] + public void Sink_NoDsn_InitNotCalled() + { + _fixture.Dsn = null; + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + sut.Emit(evt); + + Assert.False(_fixture.InitInvoked); + } + + [Fact] + public void Sink_WithDsn_InitCalled() + { + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + sut.Emit(evt); + + Assert.True(_fixture.InitInvoked); + Assert.Same(_fixture.Dsn, _fixture.DsnReceivedOnInit); + } + + [Fact] + public void Sink_NoDsn_HubNotCalled() + { + _fixture.Dsn = null; + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + sut.Emit(evt); + + Assert.False(_fixture.InitInvoked); + _fixture.Hub.DidNotReceiveWithAnyArgs().CaptureEvent(null); + } + + [Fact] + public void Sink_Properties_AsExtra() + { + const string expectedIp = "127.0.0.1"; + + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + new[] { new LogEventProperty("IPAddress", new ScalarValue(expectedIp)) }); + + sut.Emit(evt); + + _fixture.Hub.Received(1) + .CaptureEvent(Arg.Is(e => e.Extra["IPAddress"].ToString() == expectedIp)); + } + + [Fact] + public void Close_DisposesSdk() + { + const string expectedDsn = "dsn"; + var sut = _fixture.GetSut(); + sut.Dsn = expectedDsn; + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + sut.Emit(evt); + + _fixture.SdkDisposeHandle.DidNotReceive().Dispose(); + + sut.Dispose(); + + _fixture.SdkDisposeHandle.Received(1).Dispose(); + } + } +} From 6ce7f1955a0866b162dfaf82296edd9eb2b3cc23 Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Fri, 12 Oct 2018 10:55:42 -0700 Subject: [PATCH 2/7] Add option to pass in dsn to Sink --- src/Sentry.Serilog/SentrySink.cs | 2 +- src/Sentry.Serilog/SentrySinkExtensions.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 206724cf46..c9dd1e6bed 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -50,7 +50,7 @@ public void Emit(LogEvent logEvent) return; } - if (_sdkHandle == null) + if (!Hub.IsEnabled && _sdkHandle == null) { if (Dsn == null) { diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index 971a31a703..5ddc559913 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -10,9 +10,10 @@ public static class SentrySinkExtensions { public static LoggerConfiguration Sentry( this LoggerSinkConfiguration loggerConfiguration, + string dsn = null, IFormatProvider formatProvider = null) { - return loggerConfiguration.Sink(new SentrySink(formatProvider)); + return loggerConfiguration.Sink(new SentrySink(formatProvider) { Dsn = dsn }); } } } From fb53d8bea7c6b6d42d9c1b13d6d4f097385138be Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Fri, 12 Oct 2018 11:12:48 -0700 Subject: [PATCH 3/7] Add argument for minimum Sink log level --- src/Sentry.Serilog/SentrySinkExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Serilog/SentrySinkExtensions.cs b/src/Sentry.Serilog/SentrySinkExtensions.cs index 5ddc559913..878d75a049 100644 --- a/src/Sentry.Serilog/SentrySinkExtensions.cs +++ b/src/Sentry.Serilog/SentrySinkExtensions.cs @@ -3,6 +3,7 @@ using System.Text; using Serilog; using Serilog.Configuration; +using Serilog.Events; namespace Sentry.Serilog { @@ -10,10 +11,11 @@ public static class SentrySinkExtensions { public static LoggerConfiguration Sentry( this LoggerSinkConfiguration loggerConfiguration, - string dsn = null, + string dsn = null, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, IFormatProvider formatProvider = null) { - return loggerConfiguration.Sink(new SentrySink(formatProvider) { Dsn = dsn }); + return loggerConfiguration.Sink(new SentrySink(formatProvider) { Dsn = dsn }, restrictedToMinimumLevel); } } } From f5d8f5797bceacc8051dd3282a18c0b46d47b481 Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Fri, 12 Oct 2018 11:47:20 -0700 Subject: [PATCH 4/7] Add sample for Serilog integration --- Sentry.sln | 7 +++ samples/Sentry.Samples.Serilog/Program.cs | 50 +++++++++++++++++++ .../Sentry.Samples.Serilog.csproj | 17 +++++++ 3 files changed, 74 insertions(+) create mode 100644 samples/Sentry.Samples.Serilog/Program.cs create mode 100644 samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj diff --git a/Sentry.sln b/Sentry.sln index 2c191c7a76..e7438b8849 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -77,6 +77,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Serilog", "src\Sentr 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 Debug|Any CPU = Debug|Any CPU @@ -159,6 +161,10 @@ Global {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 @@ -183,6 +189,7 @@ Global {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} diff --git a/samples/Sentry.Samples.Serilog/Program.cs b/samples/Sentry.Samples.Serilog/Program.cs new file mode 100644 index 0000000000..50e22a3e40 --- /dev/null +++ b/samples/Sentry.Samples.Serilog/Program.cs @@ -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(); + } +} diff --git a/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj new file mode 100644 index 0000000000..4fc5b1b7b3 --- /dev/null +++ b/samples/Sentry.Samples.Serilog/Sentry.Samples.Serilog.csproj @@ -0,0 +1,17 @@ + + + + Exe + net462 + 3.5.234 + + + + + + + + + + + From 6289801f352b52e859328607f36ac0940da6eed7 Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Wed, 24 Oct 2018 12:46:02 -0700 Subject: [PATCH 5/7] React to new LogEntry interface from Sentry.Protocol --- src/Sentry.Serilog/SentrySink.cs | 6 ++- test/Sentry.Serilog.Tests/SentrySinkTests.cs | 47 +++++++++++++++----- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index c9dd1e6bed..3429c523e4 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -76,7 +76,11 @@ public void Emit(LogEvent logEvent) Name = Constants.SdkName, Version = NameAndVersion.Version }, - Message = logEvent.RenderMessage(_formatProvider), + LogEntry = new LogEntry + { + Formatted = logEvent.RenderMessage(_formatProvider), + Message = logEvent.MessageTemplate.Text + }, Level = logEvent.Level.ToSentryLevel() }; diff --git a/test/Sentry.Serilog.Tests/SentrySinkTests.cs b/test/Sentry.Serilog.Tests/SentrySinkTests.cs index 667606d974..25df6be009 100644 --- a/test/Sentry.Serilog.Tests/SentrySinkTests.cs +++ b/test/Sentry.Serilog.Tests/SentrySinkTests.cs @@ -64,7 +64,8 @@ public void Sink_SerilogSdk_Name() { var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); @@ -79,7 +80,8 @@ public void Sink_SerilogSdk_Packages() { var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); SentryEvent actual = null; _fixture.Hub.When(h => h.CaptureEvent(Arg.Any())) @@ -93,7 +95,6 @@ public void Sink_SerilogSdk_Packages() var package = Assert.Single(actual.Sdk.Packages); Assert.Equal("nuget:" + expected.Name, package.Name); Assert.Equal(expected.Version, package.Version); - } [Fact] @@ -103,7 +104,8 @@ public void Sink_LoggerLevel_Set() var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); @@ -118,13 +120,13 @@ public void Sink_RenderedMessage_Set() var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, new MessageTemplate(string.Empty, - new[] { new TextToken(expected) }), Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, + new MessageTemplateParser().Parse(expected), Enumerable.Empty()); sut.Emit(evt); _fixture.Hub.Received(1) - .CaptureEvent(Arg.Is(e => e.Message == expected)); + .CaptureEvent(Arg.Is(e => e.LogEntry.Formatted == expected)); } [Fact] @@ -133,7 +135,8 @@ public void Sink_NoDsn_InitNotCalled() _fixture.Dsn = null; var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); Assert.False(_fixture.InitInvoked); @@ -144,7 +147,8 @@ public void Sink_WithDsn_InitCalled() { var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); Assert.True(_fixture.InitInvoked); @@ -157,7 +161,8 @@ public void Sink_NoDsn_HubNotCalled() _fixture.Dsn = null; var sut = _fixture.GetSut(); - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); Assert.False(_fixture.InitInvoked); @@ -187,7 +192,8 @@ public void Close_DisposesSdk() var sut = _fixture.GetSut(); sut.Dsn = expectedDsn; - var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, Enumerable.Empty()); + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, MessageTemplate.Empty, + Enumerable.Empty()); sut.Emit(evt); _fixture.SdkDisposeHandle.DidNotReceive().Dispose(); @@ -196,5 +202,24 @@ public void Close_DisposesSdk() _fixture.SdkDisposeHandle.Received(1).Dispose(); } + + [Fact] + public void Sink_WithFormat_EventCaptured() + { + const string expectedMessage = "Test {structured} log"; + const int param = 10; + + var sut = _fixture.GetSut(); + + var evt = new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Error, null, + new MessageTemplateParser().Parse(expectedMessage), + new[] { new LogEventProperty("structured", new ScalarValue(param)) }); + + sut.Emit(evt); + + _fixture.Hub.Received(1).CaptureEvent(Arg.Is(p => + p.LogEntry.Formatted == $"Test {param} log" + && p.LogEntry.Message == expectedMessage)); + } } } From ed128f12d2a58268e072f29321803048241ed1b3 Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Sun, 28 Oct 2018 11:14:09 -0700 Subject: [PATCH 6/7] Add more target frameworks to Serilog tests --- test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj index 97ee8031bb..d9f3513008 100644 --- a/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj +++ b/test/Sentry.Serilog.Tests/Sentry.Serilog.Tests.csproj @@ -1,7 +1,7 @@  - net462 + netcoreapp2.1;netcoreapp2.0;net462 From d564ba94e6fea3af322bad7274218bcb7283b29a Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Sun, 28 Oct 2018 11:14:39 -0700 Subject: [PATCH 7/7] Rename hub parameter --- src/Sentry.Serilog/SentrySink.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs index 3429c523e4..7d928e1b79 100644 --- a/src/Sentry.Serilog/SentrySink.cs +++ b/src/Sentry.Serilog/SentrySink.cs @@ -33,14 +33,14 @@ public SentrySink(IFormatProvider formatProvider) : this(formatProvider, SentryS internal SentrySink( IFormatProvider formatProvider, Func initAction, - IHub hubGetter) + IHub hub) { Debug.Assert(initAction != null); - Debug.Assert(hubGetter != null); + Debug.Assert(hub != null); _formatProvider = formatProvider; _initAction = initAction; - Hub = hubGetter; + Hub = hub; } public void Emit(LogEvent logEvent)