RuleEngineTracker to control rule engine execution (#2407)

This commit is contained in:
Rajkumar Rangaraj 2023-04-06 01:23:21 -07:00 committed by GitHub
parent 765a89c46d
commit 44a24c4922
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 0 deletions

View File

@ -16,6 +16,10 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
the entry assembly name instead, only falling back to the process name
in case of an error. If the application uses .NET Framework and is hosted
on IIS, the service name is determined using `SiteName/ApplicationVirtualPath`.
- Added a rule engine to validate potential conflicts and unsupported scenarios,
ensuring back off instead of crashing, improving overall stability.
- The environment variable `OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED` could be
used to enable or disable the rule engine.
### Changed

View File

@ -185,6 +185,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.SelfContain
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.EntityFrameworkCore.Pomelo.MySql", "test\test-applications\integrations\TestApplication.EntityFrameworkCore.Pomelo.MySql\TestApplication.EntityFrameworkCore.Pomelo.MySql.csproj", "{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.AutoInstrumentation.StartupHook.Tests", "test\OpenTelemetry.AutoInstrumentation.StartupHook.Tests\OpenTelemetry.AutoInstrumentation.StartupHook.Tests.csproj", "{0077F121-DC2B-425E-A2F4-DEAC2A566E14}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -849,6 +851,18 @@ Global
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2}.Release|x64.Build.0 = Release|x64
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2}.Release|x86.ActiveCfg = Release|x86
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2}.Release|x86.Build.0 = Release|x86
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|x64.ActiveCfg = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|x64.Build.0 = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|x86.ActiveCfg = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Debug|x86.Build.0 = Debug|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|Any CPU.Build.0 = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x64.ActiveCfg = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x64.Build.0 = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.ActiveCfg = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -914,6 +928,7 @@ Global
{2EF2F7CE-E56F-4B81-A5A5-277693529D43} = {91A299AD-6C09-4B7F-BD8B-A705D9BFC672}
{25ED93D0-A70C-4A07-84D9-EF94115259C9} = {2EF2F7CE-E56F-4B81-A5A5-277693529D43}
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{0077F121-DC2B-425E-A2F4-DEAC2A566E14} = {5C915382-C886-457D-8641-9E766D8E5A17}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}

View File

@ -325,6 +325,22 @@ Important environment variables include:
| `OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES` | Comma-separated list of additional `System.Diagnostics.Metrics.Meter` names to be added to the meter at the startup. Use it to capture manually instrumented spans. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `OTEL_DOTNET_AUTO_PLUGINS` | Colon-separated list of OTel SDK instrumentation plugin types, specified with the [assembly-qualified name](https://docs.microsoft.com/en-us/dotnet/api/system.type.assemblyqualifiedname?view=net-6.0#system-type-assemblyqualifiedname). _Note: This list must be colon-separated because the type names may include commas._ See more info on how to write plugins at [plugins.md](plugins.md). | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
## RuleEngine
RuleEngine is a feature that validates OpenTelemetry API, SDK,
Instrumentation, and Exporter assemblies for unsupported scenarios,
ensuring that OpenTelemetry automatic instrumentation is more
stable by backing of instead of crashing. It works on .NET 6 and higher.
Enable RuleEngine only during the first run of the application,
or when the deployment changes or the Automatic Instrumentation
library is upgraded. Once validated, there's no need to revalidate
the rules when the application restarts.
| Environment variable | Description | Default value | Status |
|--------------------------------------- |---------------------|----------------|-----------------------------------------------------------------------------------------------------------------------------------|
| `OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED` | Enables RuleEngine. | `true` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
## .NET CLR Profiler
The CLR uses the following

View File

@ -0,0 +1,19 @@
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("OpenTelemetry.AutoInstrumentation.StartupHook.Tests")]

View File

@ -29,10 +29,26 @@ internal class RuleEngine
new InstrumentationAssemblyRule()
};
internal RuleEngine()
{
}
// This constructor is used for test purpose.
internal RuleEngine(List<Rule> rules)
{
_rules = rules;
}
internal bool Validate()
{
var result = true;
if (bool.TryParse(Environment.GetEnvironmentVariable("OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED"), out var shouldTrack) && !shouldTrack)
{
Logger.Information($"OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED is set to false, skipping rule engine validation.");
return result;
}
foreach (var rule in _rules)
{
try

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenTelemetry.AutoInstrumentation.StartupHook\OpenTelemetry.AutoInstrumentation.StartupHook.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,123 @@
// <copyright file="RuleEngineTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using OpenTelemetry.AutoInstrumentation.RulesEngine;
using Xunit;
namespace OpenTelemetry.AutoInstrumentation.StartupHook.Tests;
public class RuleEngineTests : IDisposable
{
public void Dispose()
{
Environment.SetEnvironmentVariable("OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED", null);
}
[Fact]
public void RuleEngineValidation_WhenShouldTrackIsTrue()
{
// Arrange
SetShouldTrackEnvironmentVariable(true);
var testRule = new TestRule();
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
// Act
var result = ruleEngine.Validate();
// Assert
Assert.True(result);
Assert.True(testRule.IsEvaluated);
}
[Fact]
public void RuleEngineValidation_WhenShouldTrackIsFalse()
{
// Arrange
SetShouldTrackEnvironmentVariable(false);
var testRule = new TestRule();
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
// Act
var result = ruleEngine.Validate();
// Assert
Assert.True(result);
Assert.False(testRule.IsEvaluated);
}
[Fact]
public void RuleEngineValidation_WhenShouldTrackIsNull()
{
// Arrange
SetShouldTrackEnvironmentVariable(null);
var testRule = new TestRule();
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
// Act
var result = ruleEngine.Validate();
// Assert
Assert.True(result);
Assert.True(testRule.IsEvaluated);
}
[Fact]
public void RuleEngineValidation_WhenShouldTrackIsNotSet()
{
// Arrange
var testRule = new TestRule();
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
// Act
var result = ruleEngine.Validate();
// Assert
Assert.True(result);
Assert.True(testRule.IsEvaluated);
}
[Fact]
public void RuleEngineValidation_WhenShouldTrackHasInvalidValue()
{
// Arrange
Environment.SetEnvironmentVariable("OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED", "Invalid");
var testRule = new TestRule();
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
// Act
var result = ruleEngine.Validate();
// Assert
Assert.True(result);
Assert.True(testRule.IsEvaluated);
}
private void SetShouldTrackEnvironmentVariable(bool? value)
{
Environment.SetEnvironmentVariable("OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED", value?.ToString());
}
private class TestRule : Rule
{
internal bool IsEvaluated { get; private set; }
internal override bool Evaluate()
{
IsEvaluated = true;
return true;
}
}
}