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;