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: Allow the agent to run in environments with read-only filesystems #2085

Merged
merged 9 commits into from
Nov 21, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,7 @@ private void CollectOneTimeMetrics()
ReportLogForwardingConfiguredValues();
ReportIfAppDomainCachingDisabled();
ReportInfiniteTracingOneTimeMetrics();
ReportIfLoggingDisabled();
}

public void CollectMetrics()
Expand Down Expand Up @@ -829,5 +830,17 @@ protected override void OnConfigurationUpdated(ConfigurationUpdateSource configu
// Some one time metrics are reporting configured values, so we want to re-report them if the configuration changed
_oneTimeMetricsCollected = false;
}

private void ReportIfLoggingDisabled()
{
if (!_configuration.LoggingEnabled)
{
ReportSupportabilityCountMetric(MetricNames.SupportabilityLoggingDisabled);
}
if (Log.FileLoggingHasFailed)
{
ReportSupportabilityCountMetric(MetricNames.SupportabilityLoggingFatalError);
}
}
}
}
2 changes: 2 additions & 0 deletions src/Agent/NewRelic/Agent/Core/AgentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ private void LogInitialized()
"NEW_RELIC_LOG",
"NEWRELIC_PROFILER_LOG_DIRECTORY",
"NEWRELIC_LOG_LEVEL",
"NEW_RELIC_LOG_ENABLED",
"NEW_RELIC_LOG_CONSOLE",
"NEW_RELIC_LABELS",
"NEW_RELIC_PROXY_HOST",
"NEW_RELIC_PROXY_URI_PATH",
Expand Down
16 changes: 16 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Config/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,7 @@ public virtual configurationApplication Clone()
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:newrelic-config")]
public partial class configurationLog
{
private bool enabledField;

private string levelField;

Expand All @@ -1300,6 +1301,7 @@ public partial class configurationLog
/// </summary>
public configurationLog()
{
this.enabledField = true;
this.levelField = "info";
this.consoleField = false;
this.auditLogField = false;
Expand Down Expand Up @@ -1358,6 +1360,20 @@ public bool console
this.consoleField = value;
}
}

[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(true)]
public bool enabled
{
get
{
return this.enabledField;
}
set
{
this.enabledField = value;
}
}

[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
Expand Down
8 changes: 8 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@
</xs:annotation>
</xs:attribute>

<xs:attribute name="enabled" type="xs:boolean" default="true">
<xs:annotation>
<xs:documentation>
If this is false, no attempts will be made to write to a log file. This is primarily used for installations on read-only file systems.
</xs:documentation>
</xs:annotation>
</xs:attribute>

<xs:attribute name="directory" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>
Expand Down
44 changes: 42 additions & 2 deletions src/Agent/NewRelic/Agent/Core/Config/ConfigurationLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private static string InternalGetAppDomainAppPath()

public static Func<string, bool> FileExists = File.Exists;
public static Func<string, string> PathGetDirectoryName = Path.GetDirectoryName;
public static Func<string, string> GetEnvironmentVar = System.Environment.GetEnvironmentVariable;

private static string InternalGetNewRelicHome()
{
Expand Down Expand Up @@ -522,6 +523,10 @@ public string LogLevel
{
get
{
if (!Enabled)
{
return "off";
}
// Environment variable or log.level from config...
return (AgentInstallConfiguration.NewRelicLogLevel
?? this.level).ToUpper();
Expand Down Expand Up @@ -559,7 +564,7 @@ public string GetFullLogFileName()

private string GetLogFileName()
{
string name = System.Environment.GetEnvironmentVariable("NEW_RELIC_LOG");
string name = ConfigurationLoader.GetEnvironmentVar("NEW_RELIC_LOG");
if (name != null)
{
return Strings.SafeFileName(name);
Expand Down Expand Up @@ -594,11 +599,46 @@ private string GetLogFileName()
return "newrelic_agent_" + Strings.SafeFileName(name) + ".log";
}

private bool GetOverride(string name, bool fallback)
{
var val = ConfigurationLoader.GetEnvironmentVar(name);

if (val != null)
{
val = val.ToLower();
}

if (bool.TryParse(val, out var parsedValue))
{
return parsedValue;
}

if ("0" == val)
{
return false;
}

if ("1" == val)
{
return true;
}

return fallback;
}

public bool Console
{
get
{
return console;
return GetOverride("NEW_RELIC_LOG_CONSOLE", console);
}
}

public bool Enabled
{
get
{
return GetOverride("NEW_RELIC_LOG_ENABLED", enabled);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Config/ILogConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace NewRelic.Agent.Core.Config
{
public interface ILogConfig
{
bool Enabled { get; }

string LogLevel { get; }

string GetFullLogFileName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private void OnConfigurationDeserialized(ConfigurationDeserializedEvent configur
private static void UpdateLogLevel(configuration localConfiguration)
{
Log.Info("The log level was updated to {0}", localConfiguration.LogConfig.LogLevel);
LoggerBootstrapper.UpdateLoggingLevel(localConfiguration.LogConfig.LogLevel);
LoggerBootstrapper.SetLoggingLevel(localConfiguration.LogConfig.LogLevel);
}

private void OnServerConfigurationUpdated(ServerConfigurationUpdatedEvent serverConfigurationUpdatedEvent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2775,6 +2775,8 @@ public bool CodeLevelMetricsEnabled

#endregion

public bool LoggingEnabled => _localConfiguration.log.Enabled;

private const bool CaptureTransactionTraceAttributesDefault = true;
private const bool CaptureErrorCollectorAttributesDefault = true;
private const bool CaptureBrowserMonitoringAttributesDefault = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,9 @@ public ReportedConfiguration(IConfiguration configuration)
[JsonProperty("stackexchangeredis_cleanup.cycle")]
public TimeSpan StackExchangeRedisCleanupCycle => _configuration.StackExchangeRedisCleanupCycle;

[JsonProperty("agent.logging_enabled")]
public bool LoggingEnabled => _configuration.LoggingEnabled;

public IReadOnlyDictionary<string, string> GetAppSettings()
{
return _configuration.GetAppSettings();
Expand Down
22 changes: 10 additions & 12 deletions src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ public static class LoggerBootstrapper

private static InMemorySink _inMemorySink = new InMemorySink();

public static void UpdateLoggingLevel(string newLogLevel)
{
_loggingLevelSwitch.MinimumLevel = newLogLevel.MapToSerilogLogLevel();
}
public static void SetLoggingLevel(string newLogLevel) => _loggingLevelSwitch.MinimumLevel = newLogLevel.MapToSerilogLogLevel();

public static void Initialize()
{
Expand All @@ -54,7 +51,7 @@ public static void Initialize()
/// <remarks>This should only be called once, as soon as you have a valid config.</remarks>
public static void ConfigureLogger(ILogConfig config)
{
SetupLogLevel(config);
SetLoggingLevel(config.LogLevel);

AuditLog.IsAuditLogEnabled = config.IsAuditLogEnabled;

Expand All @@ -78,6 +75,8 @@ public static void ConfigureLogger(ILogConfig config)
Log.Logger = configuredLogger;

NewRelic.Core.Logging.Log.Initialize(new Logger());

Log.Logger.Information("Log level set to {0}", config.LogLevel);
}

private static void EchoInMemoryLogsToConfiguredLogger(ILogger configuredLogger)
Expand All @@ -90,12 +89,6 @@ private static void EchoInMemoryLogsToConfiguredLogger(ILogger configuredLogger)
_inMemorySink.Dispose();
}

/// <summary>
/// Sets the log level for logger to either the level provided by the config or an public default.
/// </summary>
/// <param name="config">The LogConfig to look for the level setting in.</param>
private static void SetupLogLevel(ILogConfig config) => _loggingLevelSwitch.MinimumLevel = config.LogLevel.MapToSerilogLogLevel();

private static LoggerConfiguration ConfigureInMemoryLogSink(this LoggerConfiguration loggerConfiguration)
{
// formatter not needed since this will be pushed to other sinks for output.
Expand Down Expand Up @@ -195,6 +188,10 @@ private static LoggerConfiguration ConfigureConsoleSink(this LoggerConfiguration
/// <param name="config">The configuration for the appender.</param>
private static LoggerConfiguration ConfigureFileSink(this LoggerConfiguration loggerConfiguration, ILogConfig config)
{
if (!config.Enabled)
{
return loggerConfiguration;
}
string logFileName = config.GetFullLogFileName();

try
Expand All @@ -215,6 +212,7 @@ private static LoggerConfiguration ConfigureFileSink(this LoggerConfiguration lo
Log.Logger.Warning(ex, "Unexpected exception when configuring file sink.");

// Fallback to the event log sink if we cannot setup a file logger.
NewRelic.Core.Logging.Log.FileLoggingHasFailed = true;
Log.Logger.Warning("Falling back to EventLog sink.");
loggerConfiguration.ConfigureEventLogSink();
}
Expand All @@ -227,7 +225,7 @@ private static LoggerConfiguration ConfigureFileSink(this LoggerConfiguration lo
/// </summary>
private static LoggerConfiguration ConfigureAuditLogSink(this LoggerConfiguration loggerConfiguration, ILogConfig config)
{
if (!config.IsAuditLogEnabled) return loggerConfiguration;
if (!config.IsAuditLogEnabled || !config.Enabled) return loggerConfiguration;

string logFileName = config.GetFullLogFileName().Replace(".log", "_audit.log");

Expand Down
3 changes: 3 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Metrics/MetricNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,9 @@ public static string GetSupportabilityInstallType(string installType)
// AppDomain caching disabled
public const string SupportabilityAppDomainCachingDisabled = "Supportability/DotNET/AppDomainCaching/Disabled";

public const string SupportabilityLoggingDisabled = "Supportability/DotNET/AgentLogging/Disabled";
public const string SupportabilityLoggingFatalError = "Supportability/DotNET/AgentLogging/DisabledDueToError";

#endregion Supportability

#region Distributed Trace Metrics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,6 @@ public interface IConfiguration
TimeSpan SqlTracesHarvestCycle { get; }
TimeSpan UpdateLoadedModulesCycle { get; }
TimeSpan StackExchangeRedisCleanupCycle { get; }
bool LoggingEnabled { get; }
}
}
2 changes: 1 addition & 1 deletion src/Agent/NewRelic/Home/Home.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</Target>

<ItemGroup>
<PackageReference Include="NewRelic.Agent.Internal.Profiler" Version="10.17.0.8"/>
<PackageReference Include="NewRelic.Agent.Internal.Profiler" Version="10.18.0.15"/>
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions src/Agent/NewRelic/Profiler/Configuration/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
: _agentEnabled(true)
, _agentEnabledInLocalConfig(false)
, _logLevel(Logger::Level::LEVEL_INFO)
, _consoleLogging(false)
, _loggingEnabled(true)
, _processes(new Processes())
, _applicationPoolsWhiteList(new ApplicationPools())
, _applicationPoolsBlackList(new ApplicationPools())
Expand Down Expand Up @@ -118,6 +120,8 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
: _agentEnabled(agentEnabled)
, _agentEnabledInLocalConfig(false)
, _logLevel(logLevel)
, _consoleLogging(false)
, _loggingEnabled(true)
, _processes(processes)
, _applicationPoolsWhiteList(whiteList)
, _applicationPoolsBlackList(blackList)
Expand Down Expand Up @@ -188,10 +192,21 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
return _logLevel;
}

bool GetConsoleLogging()
{
return _consoleLogging;
}
bool GetLoggingEnabled()
{
return _loggingEnabled;
}

private:
bool _agentEnabled;
bool _agentEnabledInLocalConfig;
Logger::Level _logLevel;
bool _consoleLogging;
bool _loggingEnabled;
ProcessesPtr _processes;
ApplicationPoolsPtr _applicationPoolsWhiteList;
ApplicationPoolsPtr _applicationPoolsBlackList;
Expand Down Expand Up @@ -250,6 +265,18 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
if (logNode == nullptr)
return;

auto consoleAttribute = logNode->first_attribute(_X("console"), 0, false);
if (consoleAttribute != nullptr)
{
_consoleLogging = Strings::AreEqualCaseInsensitive(consoleAttribute->value(), _X("true"));
}

auto enabledAttribute = logNode->first_attribute(_X("enabled"), 0, false);
if (enabledAttribute != nullptr)
{
_loggingEnabled = Strings::AreEqualCaseInsensitive(enabledAttribute->value(), _X("true"));
}

auto logLevelAttribute = logNode->first_attribute(_X("level"), 0, false);
if (logLevelAttribute == nullptr)
return;
Expand Down
Loading
Loading