Skip to content

Commit

Permalink
fix: Update Profiler to check whether Azure function mode support is …
Browse files Browse the repository at this point in the history
…enabled (#2822)
  • Loading branch information
tippmar-nr authored Oct 10, 2024
1 parent 7c241f8 commit 9669641
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 11 deletions.
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.29.0.66"/>
<PackageReference Include="NewRelic.Agent.Internal.Profiler" Version="10.31.0.15"/>
</ItemGroup>

</Project>
29 changes: 22 additions & 7 deletions src/Agent/NewRelic/Profiler/Configuration/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,13 +605,17 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
return false;
}

if (IsAzureFunction()) {
auto retVal = ShouldInstrumentAzureFunction(processPath, appPoolId, commandLine);
if (retVal == 0) {
return false;
}
if (retVal == 1) {
return true;
if (IsAzureFunction())
{
if (IsAzureFunctionModeEnabled()) // if not explicitly enabled, fall back to "legacy" behavior
{
auto retVal = ShouldInstrumentAzureFunction(processPath, appPoolId, commandLine);
if (retVal == 0) {
return false;
}
if (retVal == 1) {
return true;
}
}
}

Expand All @@ -622,6 +626,17 @@ namespace NewRelic { namespace Profiler { namespace Configuration {
return true;
}

bool IsAzureFunctionModeEnabled() const
{
auto azureFunctionModeEnabled = _systemCalls->TryGetEnvironmentVariable(_X("NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"));

if (azureFunctionModeEnabled == nullptr || azureFunctionModeEnabled->length() == 0) {
return false;
}

return Strings::AreEqualCaseInsensitive(*azureFunctionModeEnabled, _X("true")) || Strings::AreEqualCaseInsensitive(*azureFunctionModeEnabled, _X("1"));
}

bool IsAzureFunction() const
{
// Azure Functions sets the FUNCTIONS_WORKER_RUNTIME environment variable to "dotnet-isolated" when running in the .NET worker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,47 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te
Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", L"", false));
}

// tests to verify that "legacy" behavior (before azure function support) is retained.
// If NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED environment variable is not set or is set to false,
// we should behave as if no azure function support has been added.
TEST_METHOD(azure_function_should_behave_as_legacy_if_azure_function_mode_not_specified)
{
std::wstring configurationXml(L"\
<?xml version=\"1.0\"?>\
<configuration>\
<log level=\"deBug\"/>\
</configuration>\
");

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Assert::IsTrue(configuration.ShouldInstrument(L"functionsnethost.exe", L"", L"", L"blah blah blah FooBarBaz blah blah blah", true));
}

// tests to verify that "legacy" behavior (before azure function support) is retained.
// If NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED environment variable is not set or is set to false,
// we should behave as if no azure function support has been added.
TEST_METHOD(azure_function_should_behave_as_legacy_if_azure_function_mode_disabled)
{
std::wstring configurationXml(L"\
<?xml version=\"1.0\"?>\
<configuration>\
<log level=\"deBug\"/>\
</configuration>\
");

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";
systemCalls->environmentVariables[L"NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"] = L"0";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Assert::IsTrue(configuration.ShouldInstrument(L"functionsnethost.exe", L"", L"", L"blah blah blah FooBarBaz blah blah blah", true));
}

TEST_METHOD(should_not_instrument_azure_function_app_pool_id_in_commandline)
{
std::wstring configurationXml(L"\
Expand All @@ -84,6 +125,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";
systemCalls->environmentVariables[L"NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"] = L"true";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Expand All @@ -101,6 +143,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";
systemCalls->environmentVariables[L"NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"] = L"true";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Expand All @@ -118,6 +161,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";
systemCalls->environmentVariables[L"NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"] = L"true";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Expand All @@ -135,6 +179,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te

auto systemCalls = std::make_shared<NewRelic::Profiler::Logger::Test::SystemCalls>();
systemCalls->environmentVariables[L"FUNCTIONS_WORKER_RUNTIME"] = L"dotnet-isolated";
systemCalls->environmentVariables[L"NEW_RELIC_AZURE_FUNCTION_MODE_ENABLED"] = L"true";

Configuration configuration(configurationXml, _missingConfig, L"", systemCalls);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,26 @@ protected AzureFunctionHttpTriggerTestsBase(TFixture fixture, ITestOutputHelper
_fixture.AddActions(
setupConfiguration: () =>
{
new NewRelicConfigModifier(fixture.DestinationNewRelicConfigFilePath)
var configModifier = new NewRelicConfigModifier(fixture.DestinationNewRelicConfigFilePath);
configModifier
.ForceTransactionTraces()
.ConfigureFasterTransactionTracesHarvestCycle(20)
.ConfigureFasterMetricsHarvestCycle(15)
.ConfigureFasterSpanEventsHarvestCycle(15)
.SetLogLevel("finest");

// This is a bit of a kludge. When azure function instrumentation is disabled,
// the agent instruments *two* processes: the azure function host (func.exe) and the actual function app.
// Both processes use the same config files, so explicitly setting the log file name forces both
// processes to log to the same file, which makes it easier to verify that the
// actual function app is not being instrumented when the Invoke() method gets hit.
//
// Ideally, we'd prefer to look for the specific log file for the azure function app, but that's less trivial
// and not worth the effort for this one test.
if (!_fixture.AzureFunctionModeEnabled)
{
configModifier.SetLogFileName("azure_function_instrumentation_disabled.log");
}
},
exerciseApplication: () =>
{
Expand Down Expand Up @@ -138,8 +152,8 @@ public void Test_SimpleInvocationMode()

if (!_fixture.AzureFunctionModeEnabled) // look for a specific log line that indicates azure function mode is disabled
{
var disabledLogLine = _fixture.AgentLog.TryGetLogLine(AgentLogBase.AzureFunctionModeDisabledLogLineRegex);
Assert.NotNull(disabledLogLine);
var disabledLogLines = _fixture.AgentLog.TryGetLogLines(AgentLogBase.AzureFunctionModeDisabledLogLineRegex);
Assert.Single(disabledLogLines);
}
}

Expand Down

0 comments on commit 9669641

Please sign in to comment.