Skip to content

Commit 09ab29d

Browse files
nr-ahemsathchynesNRgithub-actions[bot]tippmar-nr
authored
feat: Allow the agent to run in environments with read-only filesystems. (#2085)
* Don't bail if log file can't be created * Support logging to stdout in the Profiler (#2025) * Agent support for read-only file systems (#2045) * New option to disable logging * Env var for console logging * Supportability metric if logging fails * Don't allow audit logging if logging is disabled * chore: Update Profiler NuGet Package Reference to v10.18.0.15. (#2077) Co-authored-by: nr-ahemsath <[email protected]> * Add integration tests for file logging disabled and console logging enabled (#2076) * Checkpoint in order to switch branches * Refactored AgentLogFile Moved AgentLogFile to the fixtures instead of the applications * Shorten some names * Fix bug caused by Visual Studio autocorrect * WIP * Working tests for logging disabled * Working console logging tests * Fix nuget package warnings-as-errors * Fix GuidConfigurationTests EndsWith() fails if the profiler logs the version, e.g. New Relic .NET CoreCLR Agent v10.18.0.26. Use Contains() instead * Fix integration test build error * PR feedback --------- Co-authored-by: chynesNR <[email protected]> Co-authored-by: Chris Hynes <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: nr-ahemsath <[email protected]> Co-authored-by: Marty Tippin <[email protected]>
1 parent 9e355f3 commit 09ab29d

File tree

55 files changed

+925
-207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+925
-207
lines changed

src/Agent/NewRelic/Agent/Core/AgentHealth/AgentHealthReporter.cs

+13
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ private void CollectOneTimeMetrics()
695695
ReportLogForwardingConfiguredValues();
696696
ReportIfAppDomainCachingDisabled();
697697
ReportInfiniteTracingOneTimeMetrics();
698+
ReportIfLoggingDisabled();
698699
}
699700

700701
public void CollectMetrics()
@@ -829,5 +830,17 @@ protected override void OnConfigurationUpdated(ConfigurationUpdateSource configu
829830
// Some one time metrics are reporting configured values, so we want to re-report them if the configuration changed
830831
_oneTimeMetricsCollected = false;
831832
}
833+
834+
private void ReportIfLoggingDisabled()
835+
{
836+
if (!_configuration.LoggingEnabled)
837+
{
838+
ReportSupportabilityCountMetric(MetricNames.SupportabilityLoggingDisabled);
839+
}
840+
if (Log.FileLoggingHasFailed)
841+
{
842+
ReportSupportabilityCountMetric(MetricNames.SupportabilityLoggingFatalError);
843+
}
844+
}
832845
}
833846
}

src/Agent/NewRelic/Agent/Core/AgentManager.cs

+2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ private void LogInitialized()
220220
"NEW_RELIC_LOG",
221221
"NEWRELIC_PROFILER_LOG_DIRECTORY",
222222
"NEWRELIC_LOG_LEVEL",
223+
"NEW_RELIC_LOG_ENABLED",
224+
"NEW_RELIC_LOG_CONSOLE",
223225
"NEW_RELIC_LABELS",
224226
"NEW_RELIC_PROXY_HOST",
225227
"NEW_RELIC_PROXY_URI_PATH",

src/Agent/NewRelic/Agent/Core/Config/Configuration.cs

+16
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ public virtual configurationApplication Clone()
12841284
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:newrelic-config")]
12851285
public partial class configurationLog
12861286
{
1287+
private bool enabledField;
12871288

12881289
private string levelField;
12891290

@@ -1300,6 +1301,7 @@ public partial class configurationLog
13001301
/// </summary>
13011302
public configurationLog()
13021303
{
1304+
this.enabledField = true;
13031305
this.levelField = "info";
13041306
this.consoleField = false;
13051307
this.auditLogField = false;
@@ -1358,6 +1360,20 @@ public bool console
13581360
this.consoleField = value;
13591361
}
13601362
}
1363+
1364+
[System.Xml.Serialization.XmlAttributeAttribute()]
1365+
[System.ComponentModel.DefaultValueAttribute(true)]
1366+
public bool enabled
1367+
{
1368+
get
1369+
{
1370+
return this.enabledField;
1371+
}
1372+
set
1373+
{
1374+
this.enabledField = value;
1375+
}
1376+
}
13611377

13621378
[System.Xml.Serialization.XmlAttributeAttribute()]
13631379
[System.ComponentModel.DefaultValueAttribute(false)]

src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd

+8
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,14 @@
313313
</xs:annotation>
314314
</xs:attribute>
315315

316+
<xs:attribute name="enabled" type="xs:boolean" default="true">
317+
<xs:annotation>
318+
<xs:documentation>
319+
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.
320+
</xs:documentation>
321+
</xs:annotation>
322+
</xs:attribute>
323+
316324
<xs:attribute name="directory" type="xs:string" use="optional">
317325
<xs:annotation>
318326
<xs:documentation>

src/Agent/NewRelic/Agent/Core/Config/ConfigurationLoader.cs

+42-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private static string InternalGetAppDomainAppPath()
5959

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

6364
private static string InternalGetNewRelicHome()
6465
{
@@ -522,6 +523,10 @@ public string LogLevel
522523
{
523524
get
524525
{
526+
if (!Enabled)
527+
{
528+
return "off";
529+
}
525530
// Environment variable or log.level from config...
526531
return (AgentInstallConfiguration.NewRelicLogLevel
527532
?? this.level).ToUpper();
@@ -559,7 +564,7 @@ public string GetFullLogFileName()
559564

560565
private string GetLogFileName()
561566
{
562-
string name = System.Environment.GetEnvironmentVariable("NEW_RELIC_LOG");
567+
string name = ConfigurationLoader.GetEnvironmentVar("NEW_RELIC_LOG");
563568
if (name != null)
564569
{
565570
return Strings.SafeFileName(name);
@@ -594,11 +599,46 @@ private string GetLogFileName()
594599
return "newrelic_agent_" + Strings.SafeFileName(name) + ".log";
595600
}
596601

602+
private bool GetOverride(string name, bool fallback)
603+
{
604+
var val = ConfigurationLoader.GetEnvironmentVar(name);
605+
606+
if (val != null)
607+
{
608+
val = val.ToLower();
609+
}
610+
611+
if (bool.TryParse(val, out var parsedValue))
612+
{
613+
return parsedValue;
614+
}
615+
616+
if ("0" == val)
617+
{
618+
return false;
619+
}
620+
621+
if ("1" == val)
622+
{
623+
return true;
624+
}
625+
626+
return fallback;
627+
}
628+
597629
public bool Console
598630
{
599631
get
600632
{
601-
return console;
633+
return GetOverride("NEW_RELIC_LOG_CONSOLE", console);
634+
}
635+
}
636+
637+
public bool Enabled
638+
{
639+
get
640+
{
641+
return GetOverride("NEW_RELIC_LOG_ENABLED", enabled);
602642
}
603643
}
604644

src/Agent/NewRelic/Agent/Core/Config/ILogConfig.cs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace NewRelic.Agent.Core.Config
55
{
66
public interface ILogConfig
77
{
8+
bool Enabled { get; }
9+
810
string LogLevel { get; }
911

1012
string GetFullLogFileName();

src/Agent/NewRelic/Agent/Core/Configuration/ConfigurationService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private void OnConfigurationDeserialized(ConfigurationDeserializedEvent configur
6464
private static void UpdateLogLevel(configuration localConfiguration)
6565
{
6666
Log.Info("The log level was updated to {0}", localConfiguration.LogConfig.LogLevel);
67-
LoggerBootstrapper.UpdateLoggingLevel(localConfiguration.LogConfig.LogLevel);
67+
LoggerBootstrapper.SetLoggingLevel(localConfiguration.LogConfig.LogLevel);
6868
}
6969

7070
private void OnServerConfigurationUpdated(ServerConfigurationUpdatedEvent serverConfigurationUpdatedEvent)

src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2775,6 +2775,8 @@ public bool CodeLevelMetricsEnabled
27752775

27762776
#endregion
27772777

2778+
public bool LoggingEnabled => _localConfiguration.log.Enabled;
2779+
27782780
private const bool CaptureTransactionTraceAttributesDefault = true;
27792781
private const bool CaptureErrorCollectorAttributesDefault = true;
27802782
private const bool CaptureBrowserMonitoringAttributesDefault = false;

src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs

+3
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ public ReportedConfiguration(IConfiguration configuration)
647647
[JsonProperty("stackexchangeredis_cleanup.cycle")]
648648
public TimeSpan StackExchangeRedisCleanupCycle => _configuration.StackExchangeRedisCleanupCycle;
649649

650+
[JsonProperty("agent.logging_enabled")]
651+
public bool LoggingEnabled => _configuration.LoggingEnabled;
652+
650653
public IReadOnlyDictionary<string, string> GetAppSettings()
651654
{
652655
return _configuration.GetAppSettings();

src/Agent/NewRelic/Agent/Core/Logging/LoggerBootstrapper.cs

+10-12
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ public static class LoggerBootstrapper
3131

3232
private static InMemorySink _inMemorySink = new InMemorySink();
3333

34-
public static void UpdateLoggingLevel(string newLogLevel)
35-
{
36-
_loggingLevelSwitch.MinimumLevel = newLogLevel.MapToSerilogLogLevel();
37-
}
34+
public static void SetLoggingLevel(string newLogLevel) => _loggingLevelSwitch.MinimumLevel = newLogLevel.MapToSerilogLogLevel();
3835

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

5956
AuditLog.IsAuditLogEnabled = config.IsAuditLogEnabled;
6057

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

8077
NewRelic.Core.Logging.Log.Initialize(new Logger());
78+
79+
Log.Logger.Information("Log level set to {0}", config.LogLevel);
8180
}
8281

8382
private static void EchoInMemoryLogsToConfiguredLogger(ILogger configuredLogger)
@@ -90,12 +89,6 @@ private static void EchoInMemoryLogsToConfiguredLogger(ILogger configuredLogger)
9089
_inMemorySink.Dispose();
9190
}
9291

93-
/// <summary>
94-
/// Sets the log level for logger to either the level provided by the config or an public default.
95-
/// </summary>
96-
/// <param name="config">The LogConfig to look for the level setting in.</param>
97-
private static void SetupLogLevel(ILogConfig config) => _loggingLevelSwitch.MinimumLevel = config.LogLevel.MapToSerilogLogLevel();
98-
9992
private static LoggerConfiguration ConfigureInMemoryLogSink(this LoggerConfiguration loggerConfiguration)
10093
{
10194
// formatter not needed since this will be pushed to other sinks for output.
@@ -195,6 +188,10 @@ private static LoggerConfiguration ConfigureConsoleSink(this LoggerConfiguration
195188
/// <param name="config">The configuration for the appender.</param>
196189
private static LoggerConfiguration ConfigureFileSink(this LoggerConfiguration loggerConfiguration, ILogConfig config)
197190
{
191+
if (!config.Enabled)
192+
{
193+
return loggerConfiguration;
194+
}
198195
string logFileName = config.GetFullLogFileName();
199196

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

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

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

src/Agent/NewRelic/Agent/Core/Metrics/MetricNames.cs

+3
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,9 @@ public static string GetSupportabilityInstallType(string installType)
833833
// AppDomain caching disabled
834834
public const string SupportabilityAppDomainCachingDisabled = "Supportability/DotNET/AppDomainCaching/Disabled";
835835

836+
public const string SupportabilityLoggingDisabled = "Supportability/DotNET/AgentLogging/Disabled";
837+
public const string SupportabilityLoggingFatalError = "Supportability/DotNET/AgentLogging/DisabledDueToError";
838+
836839
#endregion Supportability
837840

838841
#region Distributed Trace Metrics

src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs

+1
Original file line numberDiff line numberDiff line change
@@ -205,5 +205,6 @@ public interface IConfiguration
205205
TimeSpan SqlTracesHarvestCycle { get; }
206206
TimeSpan UpdateLoadedModulesCycle { get; }
207207
TimeSpan StackExchangeRedisCleanupCycle { get; }
208+
bool LoggingEnabled { get; }
208209
}
209210
}

src/Agent/NewRelic/Home/Home.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</Target>
1414

1515
<ItemGroup>
16-
<PackageReference Include="NewRelic.Agent.Internal.Profiler" Version="10.17.0.8"/>
16+
<PackageReference Include="NewRelic.Agent.Internal.Profiler" Version="10.18.0.15"/>
1717
</ItemGroup>
1818

1919
</Project>

src/Agent/NewRelic/Profiler/Configuration/Configuration.h

+27
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
3232
: _agentEnabled(true)
3333
, _agentEnabledInLocalConfig(false)
3434
, _logLevel(Logger::Level::LEVEL_INFO)
35+
, _consoleLogging(false)
36+
, _loggingEnabled(true)
3537
, _processes(new Processes())
3638
, _applicationPoolsWhiteList(new ApplicationPools())
3739
, _applicationPoolsBlackList(new ApplicationPools())
@@ -118,6 +120,8 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
118120
: _agentEnabled(agentEnabled)
119121
, _agentEnabledInLocalConfig(false)
120122
, _logLevel(logLevel)
123+
, _consoleLogging(false)
124+
, _loggingEnabled(true)
121125
, _processes(processes)
122126
, _applicationPoolsWhiteList(whiteList)
123127
, _applicationPoolsBlackList(blackList)
@@ -188,10 +192,21 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
188192
return _logLevel;
189193
}
190194

195+
bool GetConsoleLogging()
196+
{
197+
return _consoleLogging;
198+
}
199+
bool GetLoggingEnabled()
200+
{
201+
return _loggingEnabled;
202+
}
203+
191204
private:
192205
bool _agentEnabled;
193206
bool _agentEnabledInLocalConfig;
194207
Logger::Level _logLevel;
208+
bool _consoleLogging;
209+
bool _loggingEnabled;
195210
ProcessesPtr _processes;
196211
ApplicationPoolsPtr _applicationPoolsWhiteList;
197212
ApplicationPoolsPtr _applicationPoolsBlackList;
@@ -250,6 +265,18 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
250265
if (logNode == nullptr)
251266
return;
252267

268+
auto consoleAttribute = logNode->first_attribute(_X("console"), 0, false);
269+
if (consoleAttribute != nullptr)
270+
{
271+
_consoleLogging = Strings::AreEqualCaseInsensitive(consoleAttribute->value(), _X("true"));
272+
}
273+
274+
auto enabledAttribute = logNode->first_attribute(_X("enabled"), 0, false);
275+
if (enabledAttribute != nullptr)
276+
{
277+
_loggingEnabled = Strings::AreEqualCaseInsensitive(enabledAttribute->value(), _X("true"));
278+
}
279+
253280
auto logLevelAttribute = logNode->first_attribute(_X("level"), 0, false);
254281
if (logLevelAttribute == nullptr)
255282
return;

0 commit comments

Comments
 (0)