Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Internal logging now uses Serilog instead of log4net #1661

Merged
merged 10 commits into from May 30, 2023
2 changes: 1 addition & 1 deletion src/Agent/NewRelic/Agent/Core/AgentInitializer.cs
Expand Up @@ -26,7 +26,7 @@ private static class CallOnce
{
static CallOnce()
{
// we must ensure that we hook up to ProcessExit and DomainUnload *before* log4net. Otherwise we can't log anything during OnExit.
// we must ensure that we hook up to ProcessExit and DomainUnload *before* log initialization. Otherwise we can't log anything during OnExit.
AppDomain.CurrentDomain.ProcessExit += (sender, args) => OnExit(sender, args);
AppDomain.CurrentDomain.DomainUnload += (sender, args) => OnExit(sender, args);
LoggerBootstrapper.Initialize();
Expand Down
57 changes: 0 additions & 57 deletions src/Agent/NewRelic/Agent/Core/Config/Configuration.cs
Expand Up @@ -9,12 +9,6 @@
// ------------------------------------------------------------------------------
namespace NewRelic.Agent.Core.Config
{
using System;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Collections;
using System.Xml.Schema;
using System.ComponentModel;
using System.Collections.Generic;


Expand Down Expand Up @@ -1301,8 +1295,6 @@ public partial class configurationLog

private bool auditLogField;

private System.Nullable<configurationLogFileLockingModel> fileLockingModelField;

/// <summary>
/// configurationLog class constructor
/// </summary>
Expand Down Expand Up @@ -1381,42 +1373,6 @@ public bool auditLog
}
}

[System.Xml.Serialization.XmlAttributeAttribute()]
public configurationLogFileLockingModel fileLockingModel
{
get
{
if (this.fileLockingModelField.HasValue)
{
return this.fileLockingModelField.Value;
}
else
{
return default(configurationLogFileLockingModel);
}
}
set
{
this.fileLockingModelField = value;
}
}

[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool fileLockingModelSpecified
{
get
{
return this.fileLockingModelField.HasValue;
}
set
{
if (value==false)
{
this.fileLockingModelField = null;
}
}
}

#region Clone method
/// <summary>
/// Create a clone of this configurationLog object
Expand All @@ -1428,19 +1384,6 @@ public virtual configurationLog Clone()
#endregion
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("Xsd2Code", "3.6.0.20097")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:newrelic-config")]
public enum configurationLogFileLockingModel
{

/// <remarks/>
exclusive,

/// <remarks/>
minimal,
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("Xsd2Code", "3.6.0.20097")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
Expand Down
13 changes: 0 additions & 13 deletions src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd
Expand Up @@ -347,19 +347,6 @@
</xs:annotation>
</xs:attribute>

<xs:attribute name="fileLockingModel">
tippmar-nr marked this conversation as resolved.
Show resolved Hide resolved
<xs:annotation>
<xs:documentation>
Controls how agent-produced log files are to be locked. Experimental.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="exclusive"/>
<xs:enumeration value="minimal"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>

Expand Down
16 changes: 0 additions & 16 deletions src/Agent/NewRelic/Agent/Core/Config/ConfigurationLoader.cs
Expand Up @@ -586,22 +586,6 @@ private string GetLogFileName()
return "newrelic_agent_" + Strings.SafeFileName(name) + ".log";
}

public bool FileLockingModelSpecified
{
get
{
return fileLockingModelSpecified;
}
}

public configurationLogFileLockingModel FileLockingModel
{
get
{
return fileLockingModel;
}
}

public bool Console
{
get
Expand Down
3 changes: 0 additions & 3 deletions src/Agent/NewRelic/Agent/Core/Config/ILogConfig.cs
Expand Up @@ -9,9 +9,6 @@ public interface ILogConfig

string GetFullLogFileName();

bool FileLockingModelSpecified { get; }
configurationLogFileLockingModel FileLockingModel { get; }

bool Console { get; }

bool IsAuditLogEnabled { get; }
Expand Down
Expand Up @@ -63,15 +63,8 @@ private void OnConfigurationDeserialized(ConfigurationDeserializedEvent configur

private static void UpdateLogLevel(configuration localConfiguration)
{
var hierarchy = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly()) as log4net.Repository.Hierarchy.Hierarchy;
var logger = hierarchy.Root;

var logLevel = logger.Hierarchy.LevelMap[localConfiguration.LogConfig.LogLevel];
if (logLevel != null && logLevel != logger.Level)
{
Log.InfoFormat("The log level was updated to {0}", logLevel);
logger.Level = logLevel;
}
Log.InfoFormat("The log level was updated to {0}", localConfiguration.LogConfig.LogLevel);
LoggerBootstrapper.UpdateLoggingLevel(localConfiguration.LogConfig.LogLevel);
}

private void OnServerConfigurationUpdated(ServerConfigurationUpdatedEvent serverConfigurationUpdatedEvent)
Expand Down
32 changes: 22 additions & 10 deletions src/Agent/NewRelic/Agent/Core/Core.csproj
Expand Up @@ -37,9 +37,14 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Expressions" Version="3.4.1" />
<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.File" Version="5.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="ILRepack" Version="2.0.16" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
</ItemGroup>
Expand All @@ -56,6 +61,7 @@
<PackageReference Include="Castle.Core" Version="3.3.0" />
<PackageReference Include="Castle.Windsor" Version="3.3.0" />
<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 All @@ -79,11 +85,6 @@
<Reference Include="System.Configuration" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RootProjectDirectory)\src\Agent\Shared\SharedLog4NetRepository.cs">
<Link>Properties\SharedLog4NetRepository.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
Expand All @@ -105,8 +106,14 @@
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Castle.Core'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Castle.Windsor'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'ICSharpCode.SharpZipLib'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'log4net'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Newtonsoft.Json'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Expressions'" />
<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)' == 'System.ValueTuple'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'System.Memory'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'System.Runtime.CompilerServices.Unsafe'" />
Expand All @@ -120,14 +127,19 @@
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Grpc.Net.Common'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Grpc.Net.Client'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Autofac'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'log4net'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Newtonsoft.Json'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'Serilog.Expressions'" />
<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.File'" />
<ILRepackInclude Include="@(PossibleRefsForILRepack)" Condition="'%(FileName)' == 'ICSharpCode.SharpZipLib'" />
</ItemGroup>

<PropertyGroup>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'net462'">15</ILRepackIncludeCount>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'netstandard2.0'">11</ILRepackIncludeCount>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'net462'">21</ILRepackIncludeCount>
<ILRepackIncludeCount Condition="'$(TargetFramework)' == 'netstandard2.0'">16</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
12 changes: 6 additions & 6 deletions src/Agent/NewRelic/Agent/Core/Labels/LabelsService.cs
Expand Up @@ -12,7 +12,7 @@ namespace NewRelic.Agent.Core.Labels
{
public class LabelsService : ILabelsService
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(LabelsService));
private readonly Serilog.ILogger Log = Serilog.Log.Logger;

private const int MaxLabels = 64;
private const int MaxLength = 255;
Expand Down Expand Up @@ -45,18 +45,18 @@ private IEnumerable<Label> GetLabelsFromConfiguration()
.ToList();

if (labels.Count == MaxLabels)
Log.WarnFormat("Maximum number of labels reached, some may have been dropped.");
Log.Warning("Maximum number of labels reached, some may have been dropped.");

return labels;
}
catch (Exception exception)
{
Log.WarnFormat("Failed to parse labels configuration string: {0}", exception);
Log.Warning("Failed to parse labels configuration string: {0}", exception);
return Enumerable.Empty<Label>();
}
}

private static Label CreateLabelFromString(string typeAndValueString)
private Label CreateLabelFromString(string typeAndValueString)
{
if (typeAndValueString == null)
throw new ArgumentNullException("typeAndValueString");
Expand Down Expand Up @@ -87,11 +87,11 @@ private static Label CreateLabelFromString(string typeAndValueString)
return new Label(typeTruncated, valueTruncated);
}

private static string Truncate(string value)
private string Truncate(string value)
{
var result = value.TruncateUnicodeStringByLength(MaxLength);
if (result.Length != value.Length)
Log.WarnFormat("Truncated label key from {0} to {1}", value, result);
Log.Warning("Truncated label key from {0} to {1}", value, result);

return result;
}
Expand Down
26 changes: 22 additions & 4 deletions src/Agent/NewRelic/Agent/Core/Logging/AuditLog.cs
@@ -1,19 +1,37 @@
// Copyright 2020 New Relic, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

using System;
using Serilog;
using ILogger = Serilog.ILogger;

namespace NewRelic.Agent.Core.Logging
{
public static class AuditLog
{
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(typeof(AuditLog));
// a lazy ILogger instance that injects an "Audit" property
private static Lazy<ILogger> _lazyAuditLogger = new Lazy<ILogger>(() =>
Serilog.Log.Logger.ForContext(LogLevelExtensions.AuditLevel, LogLevelExtensions.AuditLevel));

/// <summary>
/// Logs <paramref name="message"/> at the AUDIT level, a custom log level that is not well-defined in popular logging providers like log4net. This log level should be used only as dictated by the security team to satisfy auditing requirements.
/// Logs <paramref name="message"/> at the AUDIT level. This log level should be used only as dictated by the security team to satisfy auditing requirements.
/// </summary>
public static void Log(string message)
{
var auditLogLevel = LoggerBootstrapper.GetAuditLevel();
Logger.Logger.Log(typeof(AuditLog), auditLogLevel, message, null);
// use Fatal log level to ensure audit log messages never get filtered due to level restrictions
_lazyAuditLogger.Value.Fatal(message);
}

internal static LoggerConfiguration IncludeOnlyAuditLog(this LoggerConfiguration loggerConfiguration)
{
return loggerConfiguration.Filter.ByIncludingOnly($"{LogLevelExtensions.AuditLevel} is not null");

//return loggerConfiguration.Filter.ByIncludingOnly(logEvent =>
// logEvent.Properties.ContainsKey(LogLevelExtensions.AuditLevel));
}
internal static LoggerConfiguration ExcludeAuditLog(this LoggerConfiguration loggerConfiguration)
{
return loggerConfiguration.Filter.ByIncludingOnly($"{LogLevelExtensions.AuditLevel} is null");
}
}
}
45 changes: 45 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Logging/InMemorySink.cs
@@ -0,0 +1,45 @@
// Copyright 2020 New Relic, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

using System;
using Serilog.Core;
using Serilog.Events;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace NewRelic.Agent.Core.Logging
{
public class InMemorySink : ILogEventSink, IDisposable
{
private readonly ConcurrentQueue<LogEvent> _logEvents;

public InMemorySink()
{
_logEvents = new ConcurrentQueue<LogEvent>();
}

public void Emit(LogEvent logEvent)
{
_logEvents.Enqueue(logEvent);
}

public IEnumerable<LogEvent> LogEvents
{
get
{
return _logEvents;
}
}

public void Clear()
{
while (_logEvents.TryDequeue(out _))
{ }
}

public void Dispose()
{
Clear();
}
}
}