diff --git a/src/Agent/NewRelic/Agent/Core/Config/Configuration.cs b/src/Agent/NewRelic/Agent/Core/Config/Configuration.cs index 8bd464b15b..8acea3728f 100644 --- a/src/Agent/NewRelic/Agent/Core/Config/Configuration.cs +++ b/src/Agent/NewRelic/Agent/Core/Config/Configuration.cs @@ -1539,6 +1539,8 @@ public partial class configurationInstrumentation private bool logField; + private bool disableFileSystemWatcherField; + /// /// configurationInstrumentation class constructor /// @@ -1547,6 +1549,7 @@ public configurationInstrumentation() this.applicationsField = new List(); this.rulesField = new List(); this.logField = false; + this.disableFileSystemWatcherField = false; } [System.Xml.Serialization.XmlArrayItemAttribute("ignore", IsNullable=false)] @@ -1589,6 +1592,20 @@ public bool log } } + [System.Xml.Serialization.XmlAttributeAttribute()] + [System.ComponentModel.DefaultValueAttribute(false)] + public bool disableFileSystemWatcher + { + get + { + return this.disableFileSystemWatcherField; + } + set + { + this.disableFileSystemWatcherField = value; + } + } + #region Clone method /// /// Create a clone of this configurationInstrumentation object diff --git a/src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd b/src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd index cfbd1e6508..6baf646283 100644 --- a/src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd +++ b/src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd @@ -487,6 +487,14 @@ + + + + If disableFileSystemWatcher is set to true, then instrumentation XML file changes, additions, or deletions will not be detected and applied after the application has been started. + + + + diff --git a/src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs b/src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs index 1df9bb67a5..bdd4cec642 100644 --- a/src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs +++ b/src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs @@ -2058,6 +2058,14 @@ public IEnumerable> IgnoredInstrumentation } } + public bool DisableFileSystemWatcher + { + get + { + return EnvironmentOverrides(_localConfiguration.instrumentation.disableFileSystemWatcher, "NEW_RELIC_DISABLE_FILE_SYSTEM_WATCHER"); + } + } + #region AI Monitoring public bool AiMonitoringEnabled diff --git a/src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs b/src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs index 124738043b..032c84a196 100644 --- a/src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs +++ b/src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs @@ -662,6 +662,9 @@ public ReportedConfiguration(IConfiguration configuration) [JsonProperty("agent.instrumentation.ignore")] public IEnumerable> IgnoredInstrumentation => _configuration.IgnoredInstrumentation; + [JsonProperty("agent.disable_file_system_watcher")] + public bool DisableFileSystemWatcher => _configuration.DisableFileSystemWatcher; + [JsonProperty("agent.ai_monitoring.enabled")] public bool AiMonitoringEnabled => _configuration.AiMonitoringEnabled; diff --git a/src/Agent/NewRelic/Agent/Core/Instrumentation/InstrumentationWatcher.cs b/src/Agent/NewRelic/Agent/Core/Instrumentation/InstrumentationWatcher.cs index 6825b17a42..cf17659c5f 100644 --- a/src/Agent/NewRelic/Agent/Core/Instrumentation/InstrumentationWatcher.cs +++ b/src/Agent/NewRelic/Agent/Core/Instrumentation/InstrumentationWatcher.cs @@ -1,6 +1,7 @@ // Copyright 2020 New Relic, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +using NewRelic.Agent.Configuration; using NewRelic.Agent.Core.Utilities; using NewRelic.Agent.Core.Wrapper; using NewRelic.Core.Logging; @@ -16,18 +17,26 @@ public class InstrumentationWatcher : IDisposable private readonly IInstrumentationService _instrumentationService; private readonly IWrapperService _wrapperService; + private readonly IConfigurationService _configurationService; private List _fileWatchers; private SignalableAction _action; - public InstrumentationWatcher(IWrapperService wrapperService, IInstrumentationService instrumentationService) + public InstrumentationWatcher(IWrapperService wrapperService, IInstrumentationService instrumentationService, IConfigurationService configurationService) { _wrapperService = wrapperService; _instrumentationService = instrumentationService; + _configurationService = configurationService; } public void Start() { + if (_configurationService.Configuration.DisableFileSystemWatcher) + { + Log.Debug("Live instrumentation updates due to instrumentation file changes will not be applied because they have been disabled by local configuration."); + return; + } + if (AgentInstallConfiguration.HomeExtensionsDirectory == null) { Log.Warn("Live instrumentation updates due to instrumentation file changes will not be applied because HomeExtensionsDirectory is null."); diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs index 73f7653a35..62f2f9723e 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs @@ -212,7 +212,7 @@ public interface IConfiguration bool LoggingEnabled { get; } string LoggingLevel { get; } IEnumerable> IgnoredInstrumentation { get; } - + bool DisableFileSystemWatcher { get; } bool AiMonitoringEnabled { get; } bool AiMonitoringStreamingEnabled { get; } bool AiMonitoringRecordContentEnabled { get; } diff --git a/tests/Agent/IntegrationTests/IntegrationTestHelpers/NewRelicConfigModifier.cs b/tests/Agent/IntegrationTests/IntegrationTestHelpers/NewRelicConfigModifier.cs index a73363f0c7..ced7d0d667 100644 --- a/tests/Agent/IntegrationTests/IntegrationTestHelpers/NewRelicConfigModifier.cs +++ b/tests/Agent/IntegrationTests/IntegrationTestHelpers/NewRelicConfigModifier.cs @@ -497,5 +497,11 @@ public void SetDebugStartupDelaySeconds(int delaySeconds) CommonUtils.ModifyOrCreateXmlAttributeInNewRelicConfig(_configFilePath, new[] { "configuration", }, "debugStartupDelaySeconds", delaySeconds.ToString()); } + + public NewRelicConfigModifier SetDisableFileSystemWatcher(bool enabled = true) + { + CommonUtils.ModifyOrCreateXmlAttributeInNewRelicConfig(_configFilePath, new[] { "configuration", "instrumentation" }, "disableFileSystemWatcher", enabled.ToString().ToLower()); + return this; + } } } diff --git a/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetCore/RejitAddFile.cs b/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetCore/RejitAddFile.cs index 11a38fc6c6..d76b184cfc 100644 --- a/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetCore/RejitAddFile.cs +++ b/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetCore/RejitAddFile.cs @@ -25,10 +25,12 @@ public abstract class RejitAddFileBase : NewRelicIntegrationTest { @@ -61,20 +64,29 @@ public void Test() { //transactions new Assertions.ExpectedMetric { metricName = @"WebTransaction/MVC/Home/Index", callCount = 1 }, - new Assertions.ExpectedMetric { metricName = @"WebTransaction/Custom/MyCustomAddMetricName", callCount = 1 }, new Assertions.ExpectedMetric { metricName = @"WebTransaction/MVC/Rejit/GetAddFile", callCount = 1 }, // Unscoped new Assertions.ExpectedMetric { metricName = @"DotNet/HomeController/Index", callCount = 1 }, - new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", callCount = 1 }, - new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", callCount = 2 }, // Scoped new Assertions.ExpectedMetric { metricName = @"DotNet/HomeController/Index", metricScope = "WebTransaction/MVC/Home/Index", callCount = 1 }, - new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", metricScope = "WebTransaction/Custom/MyCustomAddMetricName", callCount = 1 }, new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", metricScope = "WebTransaction/MVC/Rejit/GetAddFile", callCount = 1 } }; + // Id file system watcher is disabled, these won't exist. + if (_disableFileSystemWatcher) + { + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", callCount = 1 }); + } + else + { + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"WebTransaction/Custom/MyCustomAddMetricName", callCount = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", callCount = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", metricScope = "WebTransaction/Custom/MyCustomAddMetricName", callCount = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", callCount = 2 }); + } + var metrics = CommonUtils.GetMetrics(_fixture.AgentLog); _fixture.TestLogger?.WriteLine(_fixture.AgentLog.GetFullLogAsString()); @@ -84,10 +96,18 @@ public void Test() } } - public class RejitAddFile : RejitAddFileBase + public class RejitAddFileWithFileWatcherEnabled : RejitAddFileBase + { + public RejitAddFileWithFileWatcherEnabled(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output) + : base(fixture, output, false) + { + } + } + + public class RejitAddFileWithFileWatcherDisabled : RejitAddFileBase { - public RejitAddFile(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output) - : base(fixture, output) + public RejitAddFileWithFileWatcherDisabled(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output) + : base(fixture, output, true) { } } @@ -95,7 +115,7 @@ public RejitAddFile(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHel public class RejitAddFileWithTieredCompilation : RejitAddFileBase { public RejitAddFileWithTieredCompilation(AspNetCoreReJitMvcApplicationFixtureWithTieredCompilation fixture, ITestOutputHelper output) - : base(fixture, output) + : base(fixture, output, false) { } } diff --git a/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetFramework/RejitAddFile.cs b/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetFramework/RejitAddFile.cs index 5fe21cc78b..ee3ae72ec6 100644 --- a/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetFramework/RejitAddFile.cs +++ b/tests/Agent/IntegrationTests/IntegrationTests/ReJit/NetFramework/RejitAddFile.cs @@ -20,14 +20,17 @@ namespace NewRelic.Agent.IntegrationTests.ReJit.NetFramework /// Files: Integration.Testing.AddXmlFileTest.xml /// [NetFrameworkTest] - public class RejitAddFile : NewRelicIntegrationTest + public abstract class RejitAddFileBase : NewRelicIntegrationTest + where TFixture : AspNetFrameworkReJitMvcApplicationFixture { private readonly AspNetFrameworkReJitMvcApplicationFixture _fixture; + private readonly bool _disableFileSystemWatcher; - public RejitAddFile(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutputHelper output) + public RejitAddFileBase(TFixture fixture, ITestOutputHelper output, bool disableFileSystemWatcher) : base(fixture) { _fixture = fixture; + _disableFileSystemWatcher = disableFileSystemWatcher; _fixture.TestLogger = output; _fixture.Actions( @@ -35,6 +38,7 @@ public RejitAddFile(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutp { var configModifier = new NewRelicConfigModifier(_fixture.DestinationNewRelicConfigFilePath); configModifier.AutoInstrumentBrowserMonitoring(false); + configModifier.SetDisableFileSystemWatcher(disableFileSystemWatcher); }, exerciseApplication: () => { @@ -59,20 +63,29 @@ public void Test() { //transactions new Assertions.ExpectedMetric { metricName = @"WebTransaction/MVC/HomeController/Index", CallCountAllHarvests = 1 }, - new Assertions.ExpectedMetric { metricName = @"WebTransaction/Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }, new Assertions.ExpectedMetric { metricName = @"WebTransaction/MVC/RejitController/GetAddFile", CallCountAllHarvests = 1 }, // Unscoped new Assertions.ExpectedMetric { metricName = @"DotNet/HomeController/Index", CallCountAllHarvests = 1 }, - new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }, - new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", CallCountAllHarvests = 2 }, // Scoped new Assertions.ExpectedMetric { metricName = @"DotNet/HomeController/Index", metricScope = "WebTransaction/MVC/HomeController/Index", CallCountAllHarvests = 1 }, - new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", metricScope = "WebTransaction/Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }, new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", metricScope = "WebTransaction/MVC/RejitController/GetAddFile", CallCountAllHarvests = 1 } }; + // Id file system watcher is disabled, these won't exist. + if (_disableFileSystemWatcher) + { + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", CallCountAllHarvests = 1 }); + } + else + { + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"WebTransaction/Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"Custom/MyCustomAddMetricName", metricScope = "WebTransaction/Custom/MyCustomAddMetricName", CallCountAllHarvests = 1 }); + expectedMetrics.Add(new Assertions.ExpectedMetric { metricName = @"DotNet/RejitController/GetAddFile", CallCountAllHarvests = 2 }); + } + var metrics = CommonUtils.GetMetrics(_fixture.AgentLog); _fixture.TestLogger?.WriteLine(_fixture.AgentLog.GetFullLogAsString()); @@ -81,4 +94,20 @@ public void Test() ); } } + + public class RejitAddFileWithFileWatcherEnabled : RejitAddFileBase + { + public RejitAddFileWithFileWatcherEnabled(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutputHelper output) + : base(fixture, output, false) + { + } + } + + public class RejitAddFileWithFileWatcherDisabled : RejitAddFileBase + { + public RejitAddFileWithFileWatcherDisabled(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutputHelper output) + : base(fixture, output, true) + { + } + } } diff --git a/tests/Agent/UnitTests/Core.UnitTest/DataTransport/ExhaustiveTestConfiguration.cs b/tests/Agent/UnitTests/Core.UnitTest/DataTransport/ExhaustiveTestConfiguration.cs index fe6d2eb5bf..90a99a52dd 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/DataTransport/ExhaustiveTestConfiguration.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/DataTransport/ExhaustiveTestConfiguration.cs @@ -440,6 +440,8 @@ public class ExhaustiveTestConfiguration : IConfiguration new Dictionary { { "assemblyName", "AssemblyToIgnore2" }, { "className", "ClassNameToIgnore" } } }; + public bool DisableFileSystemWatcher => false; + public TimeSpan MetricsHarvestCycle => TimeSpan.FromMinutes(1); public TimeSpan TransactionTracesHarvestCycle => TimeSpan.FromMinutes(1);