diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 4b1904b08..4da8e5095 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -99,6 +99,35 @@
$(MSBuildProjectName.Replace('Readarr','NzbDrone'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+ true
+
+ true
+
+
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 6e456510b..21be647e6 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -43,7 +43,7 @@
-
+
diff --git a/src/NzbDrone.Common.Test/InstrumentationTests/SentryTargetFixture.cs b/src/NzbDrone.Common.Test/InstrumentationTests/SentryTargetFixture.cs
index a2400c55b..2c730bd93 100644
--- a/src/NzbDrone.Common.Test/InstrumentationTests/SentryTargetFixture.cs
+++ b/src/NzbDrone.Common.Test/InstrumentationTests/SentryTargetFixture.cs
@@ -4,6 +4,7 @@ using System.Linq;
using FluentAssertions;
using NLog;
using NUnit.Framework;
+using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Sentry;
using NzbDrone.Test.Common;
@@ -27,7 +28,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[SetUp]
public void Setup()
{
- _subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111");
+ _subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111", Mocker.GetMock().Object);
}
private LogEventInfo GivenLogEvent(LogLevel level, Exception ex, string message)
diff --git a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs
index de463c442..22617e7e9 100644
--- a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs
+++ b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs
@@ -41,7 +41,7 @@ namespace NzbDrone.Common.Instrumentation
RegisterDebugger();
}
- RegisterSentry(updateApp);
+ RegisterSentry(updateApp, appFolderInfo);
if (updateApp)
{
@@ -62,7 +62,7 @@ namespace NzbDrone.Common.Instrumentation
LogManager.ReconfigExistingLoggers();
}
- private static void RegisterSentry(bool updateClient)
+ private static void RegisterSentry(bool updateClient, IAppFolderInfo appFolderInfo)
{
string dsn;
@@ -77,7 +77,7 @@ namespace NzbDrone.Common.Instrumentation
: "https://31e00a6c63ea42c8b5fe70358526a30d@sentry.servarr.com/4";
}
- var target = new SentryTarget(dsn)
+ var target = new SentryTarget(dsn, appFolderInfo)
{
Name = "sentryTarget",
Layout = "${message}"
diff --git a/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs b/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs
index 83b5b7eb7..494614fd4 100644
--- a/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs
+++ b/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs
@@ -9,6 +9,7 @@ using NLog;
using NLog.Common;
using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo;
+using NzbDrone.Common.Extensions;
using Sentry;
namespace NzbDrone.Common.Instrumentation.Sentry
@@ -99,7 +100,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
public bool FilterEvents { get; set; }
public bool SentryEnabled { get; set; }
- public SentryTarget(string dsn)
+ public SentryTarget(string dsn, IAppFolderInfo appFolderInfo)
{
_sdk = SentrySdk.Init(o =>
{
@@ -107,9 +108,33 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.AttachStacktrace = true;
o.MaxBreadcrumbs = 200;
o.Release = $"{BuildInfo.AppName}@{BuildInfo.Release}";
- o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
- o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
+ o.SetBeforeSend(x => SentryCleanser.CleanseEvent(x));
+ o.SetBeforeBreadcrumb(x => SentryCleanser.CleanseBreadcrumb(x));
o.Environment = BuildInfo.Branch;
+
+ // Crash free run statistics (sends a ping for healthy and for crashes sessions)
+ o.AutoSessionTracking = true;
+
+ // Caches files in the event device is offline
+ // Sentry creates a 'sentry' sub directory, no need to concat here
+ o.CacheDirectoryPath = appFolderInfo.GetAppDataPath();
+
+ // default environment is production
+ if (!RuntimeInfo.IsProduction)
+ {
+ if (RuntimeInfo.IsDevelopment)
+ {
+ o.Environment = "development";
+ }
+ else if (RuntimeInfo.IsTesting)
+ {
+ o.Environment = "testing";
+ }
+ else
+ {
+ o.Environment = "other";
+ }
+ }
});
InitializeScope();
@@ -127,7 +152,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
SentrySdk.ConfigureScope(scope =>
{
- scope.User = new User
+ scope.User = new SentryUser
{
Id = HashUtil.AnonymousToken()
};
@@ -169,9 +194,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
private void OnError(Exception ex)
{
- var webException = ex as WebException;
-
- if (webException != null)
+ if (ex is WebException webException)
{
var response = webException.Response as HttpWebResponse;
var statusCode = response?.StatusCode;
@@ -290,13 +313,21 @@ namespace NzbDrone.Common.Instrumentation.Sentry
}
}
+ var level = LoggingLevelMap[logEvent.Level];
var sentryEvent = new SentryEvent(logEvent.Exception)
{
- Level = LoggingLevelMap[logEvent.Level],
+ Level = level,
Logger = logEvent.LoggerName,
Message = logEvent.FormattedMessage
};
+ if (level is SentryLevel.Fatal && logEvent.Exception is not null)
+ {
+ // Usages of 'fatal' here indicates the process will crash. In Sentry this is represented with
+ // the 'unhandled' exception flag
+ logEvent.Exception.SetSentryMechanism("Logger.Fatal", "Logger.Fatal was called", false);
+ }
+
sentryEvent.SetExtras(extras);
sentryEvent.SetFingerprint(fingerPrint);