From 13311244405fa4677087d4214a8adf81d693f56d Mon Sep 17 00:00:00 2001 From: Marty Tippin <120425148+tippmar-nr@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:49:25 -0500 Subject: [PATCH] feat: Enable startup logging to Event Log for all applications on Windows. --- src/Agent/NewRelic/Agent/Core/Core.csproj | 5 +- .../Agent/Core/Logging/LoggerBootstrapper.cs | 63 +++++++++++-------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/Agent/NewRelic/Agent/Core/Core.csproj b/src/Agent/NewRelic/Agent/Core/Core.csproj index eb3592914e..d0a8c083bd 100644 --- a/src/Agent/NewRelic/Agent/Core/Core.csproj +++ b/src/Agent/NewRelic/Agent/Core/Core.csproj @@ -43,6 +43,7 @@ + @@ -60,7 +61,6 @@ - @@ -129,13 +129,14 @@ + 20 - 16 + 17 diff --git a/src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs b/src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs index fb3538e62b..5d3827ffdf 100644 --- a/src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs +++ b/src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs @@ -11,8 +11,9 @@ using Logger = NewRelic.Agent.Core.Logging.Logger; using NewRelic.Agent.Core.Logging; using Serilog.Templates; -#if NETFRAMEWORK using Serilog.Events; +#if NETSTANDARD2_0 +using System.Runtime.InteropServices; #endif namespace NewRelic.Agent.Core @@ -107,33 +108,46 @@ private static LoggerConfiguration ConfigureInMemoryLogSink(this LoggerConfigura } /// - /// Add the Event Log sink if running on .NET Framework + /// Configure an Event Log sink if running on Windows. Logs messages at Warning level and above. Intended for + /// use during bootstrapping and as a fallback if the file logging sink can't be created. + /// + /// The Agent will create the event log source if it doesn't exist *and* if the app is running with + /// administrator privileges. Otherwise, it will silently do nothing. + /// + /// It is possible to manually create the event log source in an elevated Powershell window: + /// New-EventLog -LogName "Application" -Source "New Relic .NET Agent" + /// /// /// private static LoggerConfiguration ConfigureEventLogSink(this LoggerConfiguration loggerConfiguration) { -#if NETFRAMEWORK - const string eventLogName = "Application"; - const string eventLogSourceName = "New Relic .NET Agent"; - try +#if NETSTANDARD2_0 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // event log is a Windows-only construct { - loggerConfiguration - .WriteTo.Logger(configuration => - { - configuration - .ExcludeAuditLog() - .WriteTo.EventLog( - source: eventLogSourceName, - logName: eventLogName, - restrictedToMinimumLevel: LogEventLevel.Warning, - outputTemplate: "{Level}: {Message}{NewLine}{Exception}", - manageEventSource: true // Serilog will create the event source if it doesn't exist *and* if the app is running with admin privileges - ); - }); - } - catch - { - // ignored -- there's nothing we can do at this point, as EventLog is our "fallback" logger and if it fails, we're out of luck +#endif + const string eventLogName = "Application"; + const string eventLogSourceName = "New Relic .NET Agent"; + try + { + loggerConfiguration + .WriteTo.Logger(configuration => + { + configuration + .ExcludeAuditLog() + .WriteTo.EventLog( + source: eventLogSourceName, + logName: eventLogName, + restrictedToMinimumLevel: LogEventLevel.Warning, + outputTemplate: "{Level}: {Message}{NewLine}{Exception}", + manageEventSource: true + ); + }); + } + catch + { + // ignored -- there's nothing we can do at this point, as EventLog is our "fallback" logger and if it fails, we're out of luck + } +#if NETSTANDARD2_0 } #endif return loggerConfiguration; @@ -197,11 +211,10 @@ private static LoggerConfiguration ConfigureFileSink(this LoggerConfiguration lo catch (Exception ex) { Log.Logger.Warning(ex, "Unexpected exception when configuring file sink."); -#if NETFRAMEWORK + // Fallback to the event log sink if we cannot setup a file logger. Log.Logger.Warning("Falling back to EventLog sink."); loggerConfiguration.ConfigureEventLogSink(); -#endif } return loggerConfiguration;