Skip to content

Commit

Permalink
feat: Enable startup logging to Event Log for all applications on Win…
Browse files Browse the repository at this point in the history
…dows. (#1969)
  • Loading branch information
tippmar-nr committed Oct 12, 2023
1 parent f4658ed commit cdac8b7
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
5 changes: 3 additions & 2 deletions src/Agent/NewRelic/Agent/Core/Core.csproj
Expand Up @@ -43,6 +43,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.EventLog" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="ILRepack" Version="2.0.16" />
Expand All @@ -60,7 +61,6 @@
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
<PackageReference Include="Serilog.Sinks.EventLog" Version="3.1.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
Expand Down Expand Up @@ -129,13 +129,14 @@
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Sinks.Async'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Sinks.Console'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Sinks.Debug'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Sinks.EventLog'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Sinks.File'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'ICSharpCode.SharpZipLib'" />
</ItemGroup>

<PropertyGroup>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'net462'">20</ILRepackIncludeCount>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'netstandard2.0'">16</ILRepackIncludeCount>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'netstandard2.0'">17</ILRepackIncludeCount>
</PropertyGroup>

<Error Text="ILRepack of $(AssemblyName) ($(TargetFramework)) failed. A dependency is missing. Expected $(ILRepackIncludeCount) dependencies but found @(ILRepackInclude-&gt;Count())." Condition="@(ILRepackInclude-&gt;Count()) != $(ILRepackIncludeCount)" />
Expand Down
66 changes: 40 additions & 26 deletions src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs
Expand Up @@ -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
Expand Down Expand Up @@ -107,35 +108,49 @@ private static LoggerConfiguration ConfigureInMemoryLogSink(this LoggerConfigura
}

/// <summary>
/// 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"
///
/// </summary>
/// <param name="loggerConfiguration"></param>
private static LoggerConfiguration ConfigureEventLogSink(this LoggerConfiguration loggerConfiguration)
{
#if NETFRAMEWORK
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 // Serilog will create the event source if it doesn't exist *and* if the app is running with admin privileges
);
});
}
catch
#if NETSTANDARD2_0
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#else
var isWindows = true;
#endif
if (isWindows)
{
// 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
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
}
}
#endif
return loggerConfiguration;
}

Expand Down Expand Up @@ -197,11 +212,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;
Expand Down

0 comments on commit cdac8b7

Please sign in to comment.