diff --git a/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs index def6de02c..4579ca48f 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs @@ -90,10 +90,6 @@ namespace NzbDrone.Common.EnvironmentInfo { IsDocker = true; } - - Environment.SetEnvironmentVariable("OS_NAME", Name); - Environment.SetEnvironmentVariable("OS_VERSION", Version); - Environment.SetEnvironmentVariable("OS_IS_DOCKER", IsDocker.ToString()); } } diff --git a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs index 47cae6010..8651f6861 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs @@ -37,7 +37,16 @@ namespace NzbDrone.Common.EnvironmentInfo static RuntimeInfo() { - IsProduction = InternalIsProduction(); + var officialBuild = InternalIsOfficialBuild(); + + // An build running inside of the testing environment. (Analytics disabled) + IsTesting = InternalIsTesting(); + + // An official build running outside of the testing environment. (Analytics configurable) + IsProduction = !IsTesting && officialBuild; + + // An unofficial build running outside of the testing environment. (Analytics enabled) + IsDevelopment = !IsTesting && !officialBuild && !InternalIsDebug(); } public DateTime StartTime @@ -108,23 +117,21 @@ namespace NzbDrone.Common.EnvironmentInfo public bool RestartPending { get; set; } public string ExecutingApplication { get; } + public static bool IsTesting { get; } public static bool IsProduction { get; } + public static bool IsDevelopment { get; } - private static bool InternalIsProduction() - { - if (BuildInfo.IsDebug || Debugger.IsAttached) return false; - - //Official builds will never have such a high revision - if (BuildInfo.Version.Revision > 10000) return false; + private static bool InternalIsTesting() + { try { var lowerProcessName = Process.GetCurrentProcess().ProcessName.ToLower(); - if (lowerProcessName.Contains("vshost")) return false; - if (lowerProcessName.Contains("nunit")) return false; - if (lowerProcessName.Contains("jetbrain")) return false; - if (lowerProcessName.Contains("resharper")) return false; + if (lowerProcessName.Contains("vshost")) return true; + if (lowerProcessName.Contains("nunit")) return true; + if (lowerProcessName.Contains("jetbrain")) return true; + if (lowerProcessName.Contains("resharper")) return true; } catch { @@ -134,7 +141,7 @@ namespace NzbDrone.Common.EnvironmentInfo try { var currentAssemblyLocation = typeof(RuntimeInfo).Assembly.Location; - if (currentAssemblyLocation.ToLower().Contains("_output")) return false; + if (currentAssemblyLocation.ToLower().Contains("_output")) return true; } catch { @@ -142,9 +149,24 @@ namespace NzbDrone.Common.EnvironmentInfo } var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower(); - if (lowerCurrentDir.Contains("vsts")) return false; - if (lowerCurrentDir.Contains("buildagent")) return false; - if (lowerCurrentDir.Contains("_output")) return false; + if (lowerCurrentDir.Contains("vsts")) return true; + if (lowerCurrentDir.Contains("buildagent")) return true; + if (lowerCurrentDir.Contains("_output")) return true; + + return false; + } + + private static bool InternalIsDebug() + { + if (BuildInfo.IsDebug || Debugger.IsAttached) return true; + + return false; + } + + private static bool InternalIsOfficialBuild() + { + //Official builds will never have such a high revision + if (BuildInfo.Version.Major >= 10 || BuildInfo.Version.Revision > 10000) return false; return true; } diff --git a/src/NzbDrone.Common/Instrumentation/InitializeLogger.cs b/src/NzbDrone.Common/Instrumentation/InitializeLogger.cs new file mode 100644 index 000000000..aa7696204 --- /dev/null +++ b/src/NzbDrone.Common/Instrumentation/InitializeLogger.cs @@ -0,0 +1,28 @@ +using System.Linq; +using NLog; +using NLog.Fluent; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Common.Instrumentation.Sentry; + +namespace NzbDrone.Common.Instrumentation +{ + public class InitializeLogger + { + private readonly IOsInfo _osInfo; + + public InitializeLogger(IOsInfo osInfo) + { + _osInfo = osInfo; + } + + public void Initialize() + { + var sentryTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); + if (sentryTarget != null) + { + sentryTarget.UpdateScope(_osInfo); + } + } + } +} diff --git a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs index 934c00ca9..a4cff59e5 100644 --- a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs +++ b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs @@ -58,18 +58,6 @@ namespace NzbDrone.Common.Instrumentation LogManager.ReconfigExistingLoggers(); } - public static void UnRegisterRemoteLoggers() - { - var sentryRules = LogManager.Configuration.LoggingRules.Where(r => r.Targets.Any(t => t.Name == "sentryTarget")); - - foreach (var rules in sentryRules) - { - rules.Targets.Clear(); - } - - LogManager.ReconfigExistingLoggers(); - } - private static void RegisterSentry(bool updateClient) { diff --git a/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs b/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs index 622473441..15b2956c9 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; using Sentry.Protocol; @@ -77,6 +78,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry {LogLevel.Warn, BreadcrumbLevel.Warning}, }; + private readonly DateTime _startTime = DateTime.UtcNow; private readonly IDisposable _sdk; private bool _disposed; @@ -84,10 +86,8 @@ namespace NzbDrone.Common.Instrumentation.Sentry private bool _unauthorized; public bool FilterEvents { get; set; } - public string UpdateBranch { get; set; } - public Version DatabaseVersion { get; set; } - public int DatabaseMigration { get; set; } - + public bool SentryEnabled { get; set; } + public SentryTarget(string dsn) { _sdk = SentrySdk.Init(o => @@ -95,34 +95,81 @@ namespace NzbDrone.Common.Instrumentation.Sentry o.Dsn = new Dsn(dsn); o.AttachStacktrace = true; o.MaxBreadcrumbs = 200; - o.SendDefaultPii = true; + o.SendDefaultPii = false; o.Debug = false; o.DiagnosticsLevel = SentryLevel.Debug; o.Release = BuildInfo.Release; + if (PlatformInfo.IsMono) + { + // Mono 6.0 broke GzipStream.WriteAsync + // TODO: Check specific version + o.RequestBodyCompressionLevel = System.IO.Compression.CompressionLevel.NoCompression; + } o.BeforeSend = x => SentryCleanser.CleanseEvent(x); o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x); + o.Environment = BuildInfo.Branch; }); - SentrySdk.ConfigureScope(scope => - { - scope.User = new User { - Username = HashUtil.AnonymousToken() - }; - - scope.SetTag("osfamily", OsInfo.Os.ToString()); - scope.SetTag("runtime", PlatformInfo.PlatformName); - scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name); - scope.SetTag("branch", BuildInfo.Branch); - scope.SetTag("version", BuildInfo.Version.ToString()); - scope.SetTag("production", RuntimeInfo.IsProduction.ToString()); - }); - + InitializeScope(); + _debounce = new SentryDebounce(); // initialize to true and reconfigure later // Otherwise it will default to false and any errors occuring // before config file gets read will not be filtered FilterEvents = true; + SentryEnabled = true; + } + + public void InitializeScope() + { + SentrySdk.ConfigureScope(scope => + { + scope.User = new User + { + Id = HashUtil.AnonymousToken() + }; + + scope.Contexts.App.Name = BuildInfo.AppName; + scope.Contexts.App.Version = BuildInfo.Version.ToString(); + scope.Contexts.App.StartTime = _startTime; + scope.Contexts.App.Hash = HashUtil.AnonymousToken(); + scope.Contexts.App.Build = BuildInfo.Release; // Git commit cache? + + scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name); + scope.SetTag("branch", BuildInfo.Branch); + }); + } + + public void UpdateScope(IOsInfo osInfo) + { + SentrySdk.ConfigureScope(scope => + { + scope.SetTag("is_docker", $"{osInfo.IsDocker}"); + + if (osInfo.Name != null && PlatformInfo.IsMono) + { + // Sentry auto-detection of non-Windows platforms isn't that accurate on certain devices. + scope.Contexts.OperatingSystem.Name = osInfo.Name.FirstCharToUpper(); + scope.Contexts.OperatingSystem.RawDescription = osInfo.FullName; + scope.Contexts.OperatingSystem.Version = osInfo.Version.ToString(); + } + }); + } + + public void UpdateScope(Version databaseVersion, int migration, string updateBranch, IPlatformInfo platformInfo) + { + SentrySdk.ConfigureScope(scope => + { + scope.Environment = updateBranch; + scope.SetTag("runtime_version", $"{PlatformInfo.PlatformName} {platformInfo.Version}"); + + if (databaseVersion != default(Version)) + { + scope.SetTag("sqlite_version", $"{databaseVersion}"); + scope.SetTag("database_migration", $"{migration}"); + } + }); } private void OnError(Exception ex) @@ -213,7 +260,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry protected override void Write(LogEventInfo logEvent) { - if (_unauthorized) + if (_unauthorized || !SentryEnabled) { return; } @@ -249,27 +296,12 @@ namespace NzbDrone.Common.Instrumentation.Sentry { Level = LoggingLevelMap[logEvent.Level], Logger = logEvent.LoggerName, - Message = logEvent.FormattedMessage, - Environment = UpdateBranch + Message = logEvent.FormattedMessage }; sentryEvent.SetExtras(extras); sentryEvent.SetFingerprint(fingerPrint); - // this can't be in the constructor as at that point OsInfo won't have - // populated these values yet - var osName = Environment.GetEnvironmentVariable("OS_NAME"); - var osVersion = Environment.GetEnvironmentVariable("OS_VERSION"); - var isDocker = Environment.GetEnvironmentVariable("OS_IS_DOCKER"); - var runTimeVersion = Environment.GetEnvironmentVariable("RUNTIME_VERSION"); - - sentryEvent.SetTag("os_name", osName); - sentryEvent.SetTag("os_version", $"{osName} {osVersion}"); - sentryEvent.SetTag("is_docker", isDocker); - sentryEvent.SetTag("runtime_version", $"{PlatformInfo.PlatformName} {runTimeVersion}"); - sentryEvent.SetTag("sqlite_version", $"{DatabaseVersion}"); - sentryEvent.SetTag("database_migration", $"{DatabaseMigration}"); - SentrySdk.CaptureEvent(sentryEvent); } catch (Exception e) diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index 8c64be329..9cb37f2f7 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -376,11 +376,6 @@ namespace NzbDrone.Core.Configuration { EnsureDefaultConfigFile(); DeleteOldValues(); - - if (!AnalyticsEnabled) - { - NzbDroneLogger.UnRegisterRemoteLoggers(); - } } public void Execute(ResetApiKeyCommand message) diff --git a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs index 9c99dfb02..2de9edfe1 100644 --- a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs +++ b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Linq; using NLog; using NLog.Config; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Instrumentation.Sentry; using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Messaging.Events; @@ -40,6 +42,9 @@ namespace NzbDrone.Core.Instrumentation SetMinimumLogLevel(rules, "appFileDebug", minimumLogLevel <= LogLevel.Debug ? LogLevel.Debug : LogLevel.Off); SetMinimumLogLevel(rules, "appFileTrace", minimumLogLevel <= LogLevel.Trace ? LogLevel.Trace : LogLevel.Off); + //Sentry + ReconfigureSentry(); + LogManager.ReconfigExistingLoggers(); } @@ -67,6 +72,16 @@ namespace NzbDrone.Core.Instrumentation } } + private void ReconfigureSentry() + { + var sentryTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); + if (sentryTarget != null) + { + sentryTarget.SentryEnabled = RuntimeInfo.IsProduction && _configFileProvider.AnalyticsEnabled || RuntimeInfo.IsDevelopment; + sentryTarget.FilterEvents = _configFileProvider.FilterSentryEvents; + } + } + private List GetLogLevels() { return new List diff --git a/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs b/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs index 1cd144ec0..56274f6c9 100644 --- a/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs +++ b/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs @@ -1,4 +1,6 @@ +using System.Linq; using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Sentry; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; @@ -10,25 +12,26 @@ namespace NzbDrone.Core.Instrumentation public class ReconfigureSentry : IHandleAsync { private readonly IConfigFileProvider _configFileProvider; + private readonly IPlatformInfo _platformInfo; private readonly IMainDatabase _database; public ReconfigureSentry(IConfigFileProvider configFileProvider, - IMainDatabase database) + IPlatformInfo platformInfo, + IMainDatabase database) { _configFileProvider = configFileProvider; + _platformInfo = platformInfo; _database = database; } public void Reconfigure() { // Extended sentry config - var sentry = LogManager.Configuration.FindTargetByName("sentryTarget"); - sentry.FilterEvents = _configFileProvider.FilterSentryEvents; - sentry.UpdateBranch = _configFileProvider.Branch; - sentry.DatabaseVersion = _database.Version; - sentry.DatabaseMigration = _database.Migration; - - LogManager.ReconfigExistingLoggers(); + var sentryTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); + if (sentryTarget != null) + { + sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.Branch, _platformInfo); + } } public void HandleAsync(ApplicationStartedEvent message) diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index a562f44e8..b3fa8ca99 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -36,6 +36,7 @@ namespace NzbDrone.Host LongPathSupport.Enable(); _container = MainAppContainerBuilder.BuildContainer(startupContext); + _container.Resolve().Initialize(); _container.Resolve().Register(); _container.Resolve().Write(); diff --git a/src/NzbDrone.Mono/EnvironmentInfo/MonoPlatformInfo.cs b/src/NzbDrone.Mono/EnvironmentInfo/MonoPlatformInfo.cs index e883cea94..e85540dfa 100644 --- a/src/NzbDrone.Mono/EnvironmentInfo/MonoPlatformInfo.cs +++ b/src/NzbDrone.Mono/EnvironmentInfo/MonoPlatformInfo.cs @@ -30,7 +30,6 @@ namespace NzbDrone.Mono.EnvironmentInfo if (versionMatch.Success) { runTimeVersion = new Version(versionMatch.Groups["version"].Value); - Environment.SetEnvironmentVariable("RUNTIME_VERSION", runTimeVersion.ToString()); } } } diff --git a/src/NzbDrone.Update/UpdateApp.cs b/src/NzbDrone.Update/UpdateApp.cs index 5acecbb09..25bb5ab63 100644 --- a/src/NzbDrone.Update/UpdateApp.cs +++ b/src/NzbDrone.Update/UpdateApp.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Update Logger.Info("Starting Lidarr Update Client"); _container = UpdateContainerBuilder.Build(startupContext); - + _container.Resolve().Initialize(); _container.Resolve().Start(args); Logger.Info("Update completed successfully"); diff --git a/src/NzbDrone.Windows/EnvironmentInfo/DotNetPlatformInfo.cs b/src/NzbDrone.Windows/EnvironmentInfo/DotNetPlatformInfo.cs index 015b1270c..f05cba7bb 100644 --- a/src/NzbDrone.Windows/EnvironmentInfo/DotNetPlatformInfo.cs +++ b/src/NzbDrone.Windows/EnvironmentInfo/DotNetPlatformInfo.cs @@ -13,7 +13,6 @@ namespace NzbDrone.Windows.EnvironmentInfo { _logger = logger; var version = GetFrameworkVersion(); - Environment.SetEnvironmentVariable("RUNTIME_VERSION", version.ToString()); Version = version; } @@ -33,6 +32,22 @@ namespace NzbDrone.Windows.EnvironmentInfo var releaseKey = (int)ndpKey.GetValue("Release"); + if (releaseKey >= 528040) + { + return new Version(4, 8, 0); + } + if (releaseKey >= 461808) + { + return new Version(4, 7, 2); + } + if (releaseKey >= 461308) + { + return new Version(4, 7, 1); + } + if (releaseKey >= 460798) + { + return new Version(4, 7); + } if (releaseKey >= 394802) { return new Version(4, 6, 2);