Skip to content

Commit

Permalink
feat: Add an option to disable the file system watcher. (#2536)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaffinito authored Jun 12, 2024
1 parent a3d1d9e commit 27d0ed0
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 19 deletions.
17 changes: 17 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Config/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ public partial class configurationService

private bool forceNewTransactionOnNewThreadField;

private bool disableFileSystemWatcherField;

/// <summary>
/// configurationService class constructor
/// </summary>
Expand All @@ -810,6 +812,7 @@ public configurationService()
this.sendDataOnExitThresholdField = ((float)(60000F));
this.completeTransactionsOnThreadField = false;
this.forceNewTransactionOnNewThreadField = false;
this.disableFileSystemWatcherField = false;
}

public string obscuringKey
Expand Down Expand Up @@ -1032,6 +1035,20 @@ public bool forceNewTransactionOnNewThread
}
}

[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool disableFileSystemWatcher
{
get
{
return this.disableFileSystemWatcherField;
}
set
{
this.disableFileSystemWatcherField = value;
}
}

#region Clone method
/// <summary>
/// Create a clone of this configurationService object
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 @@ -223,6 +223,14 @@
</xs:annotation>
</xs:attribute>

<xs:attribute name="disableFileSystemWatcher" type="xs:boolean" default="false">
<xs:annotation>
<xs:documentation>
If disableFileSystemWatcher is set to true, then newrelic.config and instrumentation XML file changes, additions, or deletions will not be detected and applied after the application has been started.
</xs:documentation>
</xs:annotation>
</xs:attribute>

</xs:complexType>
</xs:element>

Expand Down
6 changes: 6 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Config/ConfigurationTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public class ConfigurationTracker : IDisposable

public ConfigurationTracker(IConfigurationService configurationService, INativeMethods nativeMethods)
{
if (configurationService.Configuration.DisableFileSystemWatcher)
{
Log.Debug("Live updates to newrelic.config will not be applied because they have been disabled by local configuration.");
return;
}

_nativeMethods = nativeMethods;
var fileName = configurationService.Configuration.NewRelicConfigFilePath;
if (fileName == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,19 @@ public IEnumerable<IDictionary<string, string>> IgnoredInstrumentation
}
}

public bool DisableFileSystemWatcher
{
get
{
if(ServerlessModeEnabled || !LoggingEnabled)
{
return true;
}

return EnvironmentOverrides(_localConfiguration.service.disableFileSystemWatcher, "NEW_RELIC_DISABLE_FILE_SYSTEM_WATCHER");
}
}

#region AI Monitoring

public bool AiMonitoringEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,9 @@ public ReportedConfiguration(IConfiguration configuration)
[JsonProperty("agent.instrumentation.ignore")]
public IEnumerable<IDictionary<string, string>> IgnoredInstrumentation => _configuration.IgnoredInstrumentation;

[JsonProperty("agent.disable_file_system_watcher")]
public bool DisableFileSystemWatcher => _configuration.DisableFileSystemWatcher;

[JsonProperty("agent.ai_monitoring.enabled")]
public bool AiMonitoringEnabled => _configuration.AiMonitoringEnabled;

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,18 +17,26 @@ public class InstrumentationWatcher : IDisposable

private readonly IInstrumentationService _instrumentationService;
private readonly IWrapperService _wrapperService;
private readonly IConfigurationService _configurationService;

private List<FileSystemWatcher> _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.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public interface IConfiguration
bool LoggingEnabled { get; }
string LoggingLevel { get; }
IEnumerable<IDictionary<string, string>> IgnoredInstrumentation { get; }

bool DisableFileSystemWatcher { get; }
bool AiMonitoringEnabled { get; }
bool AiMonitoringStreamingEnabled { get; }
bool AiMonitoringRecordContentEnabled { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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", "service" }, "disableFileSystemWatcher", enabled.ToString().ToLower());
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ public abstract class RejitAddFileBase<TFixture> : NewRelicIntegrationTest<TFixt
where TFixture:AspNetCoreReJitMvcApplicationFixture
{
private readonly AspNetCoreReJitMvcApplicationFixture _fixture;
private readonly bool _disableFileSystemWatcher;

protected RejitAddFileBase(TFixture fixture, ITestOutputHelper output) : base(fixture)
protected RejitAddFileBase(TFixture fixture, ITestOutputHelper output, bool disableFileSystemWatcher) : base(fixture)
{
_fixture = fixture;
_disableFileSystemWatcher = disableFileSystemWatcher;

_fixture.TestLogger = output;
_fixture.Actions(
Expand All @@ -37,6 +39,7 @@ protected RejitAddFileBase(TFixture fixture, ITestOutputHelper output) : base(fi
var configModifier = new NewRelicConfigModifier(_fixture.DestinationNewRelicConfigFilePath);
configModifier.SetLogLevel("finest");
configModifier.AutoInstrumentBrowserMonitoring(false);
configModifier.SetDisableFileSystemWatcher(disableFileSystemWatcher);
},
exerciseApplication: () =>
{
Expand All @@ -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());

Expand All @@ -84,18 +96,26 @@ public void Test()
}
}

public class RejitAddFile : RejitAddFileBase<AspNetCoreReJitMvcApplicationFixture>
public class RejitAddFileWithFileWatcherEnabled : RejitAddFileBase<AspNetCoreReJitMvcApplicationFixture>
{
public RejitAddFileWithFileWatcherEnabled(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output)
: base(fixture, output, false)
{
}
}

public class RejitAddFileWithFileWatcherDisabled : RejitAddFileBase<AspNetCoreReJitMvcApplicationFixture>
{
public RejitAddFile(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output)
: base(fixture, output)
public RejitAddFileWithFileWatcherDisabled(AspNetCoreReJitMvcApplicationFixture fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}

public class RejitAddFileWithTieredCompilation : RejitAddFileBase<AspNetCoreReJitMvcApplicationFixtureWithTieredCompilation>
{
public RejitAddFileWithTieredCompilation(AspNetCoreReJitMvcApplicationFixtureWithTieredCompilation fixture, ITestOutputHelper output)
: base(fixture, output)
: base(fixture, output, false)
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,25 @@ namespace NewRelic.Agent.IntegrationTests.ReJit.NetFramework
/// Files: Integration.Testing.AddXmlFileTest.xml
/// </summary>
[NetFrameworkTest]
public class RejitAddFile : NewRelicIntegrationTest<AspNetFrameworkReJitMvcApplicationFixture>
public abstract class RejitAddFileBase<TFixture> : NewRelicIntegrationTest<TFixture>
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(
setupConfiguration: () =>
{
var configModifier = new NewRelicConfigModifier(_fixture.DestinationNewRelicConfigFilePath);
configModifier.AutoInstrumentBrowserMonitoring(false);
configModifier.SetDisableFileSystemWatcher(disableFileSystemWatcher);
},
exerciseApplication: () =>
{
Expand All @@ -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());

Expand All @@ -81,4 +94,20 @@ public void Test()
);
}
}

public class RejitAddFileWithFileWatcherEnabled : RejitAddFileBase<AspNetFrameworkReJitMvcApplicationFixture>
{
public RejitAddFileWithFileWatcherEnabled(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutputHelper output)
: base(fixture, output, false)
{
}
}

public class RejitAddFileWithFileWatcherDisabled : RejitAddFileBase<AspNetFrameworkReJitMvcApplicationFixture>
{
public RejitAddFileWithFileWatcherDisabled(AspNetFrameworkReJitMvcApplicationFixture fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4023,6 +4023,39 @@ public void LoggingLevelValueIsCached()

#endregion Agent Logs

[TestCase(false, null, true, false, ExpectedResult = false)] // default
[TestCase(true, null, true, false, ExpectedResult = true)]
[TestCase(false, true, true, false, ExpectedResult = true)]
[TestCase(true, false, true, false, ExpectedResult = false)]
[TestCase(false, null, false, false, ExpectedResult = true)]
[TestCase(false, null, false, true, ExpectedResult = true)]
[TestCase(false, null, true, true, ExpectedResult = true)]
[TestCase(true, null, false, false, ExpectedResult = true)]
[TestCase(true, null, false, true, ExpectedResult = true)]
[TestCase(true, null, true, true, ExpectedResult = true)]
[TestCase(false, true, false, false, ExpectedResult = true)]
[TestCase(false, true, false, true, ExpectedResult = true)]
[TestCase(false, true, true, true, ExpectedResult = true)]
[TestCase(true, false, false, false, ExpectedResult = true)]
[TestCase(true, false, false, true, ExpectedResult = true)]
[TestCase(true, false, true, true, ExpectedResult = true)]
public bool ValidateDisableFileSystemWatcher(bool localWatcherDisabled, bool? envWatcherDisabled, bool loggingEnabled, bool serverlessMode)
{
// Setup config values
_localConfig.service.disableFileSystemWatcher = localWatcherDisabled;

if (envWatcherDisabled.HasValue)
{
Mock.Arrange(() => _environment.GetEnvironmentVariable("NEW_RELIC_DISABLE_FILE_SYSTEM_WATCHER")).Returns(envWatcherDisabled.ToString().ToLower());
}

_localConfig.log.enabled = loggingEnabled;
Mock.Arrange(() => _bootstrapConfiguration.ServerlessModeEnabled).Returns(serverlessMode);

// test
return _defaultConfig.DisableFileSystemWatcher;
}

private void CreateDefaultConfiguration()
{
_defaultConfig = new TestableDefaultConfiguration(_environment, _localConfig, _serverConfig, _runTimeConfig, _securityPoliciesConfiguration, _bootstrapConfiguration, _processStatic, _httpRuntimeStatic, _configurationManagerStatic, _dnsStatic);
Expand Down
Loading

0 comments on commit 27d0ed0

Please sign in to comment.