StartupHook: Move all checks to mandatory rules (#2432)
* InstrumentationAssemblyRule * Test changes. * Update src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/InstrumentationAssemblyRule.cs Co-authored-by: Paulo Janotti <pjanotti@splunk.com> * Move ApplicationInExcludeList to Rule * New changes * PR feedback. --------- Co-authored-by: Paulo Janotti <pjanotti@splunk.com>
This commit is contained in:
parent
0f15c72088
commit
3e5ce4ab11
|
|
@ -0,0 +1,97 @@
|
|||
// <copyright file="ApplicationInExcludeListRule.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.Logging;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.RulesEngine;
|
||||
|
||||
internal class ApplicationInExcludeListRule : Rule
|
||||
{
|
||||
private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook");
|
||||
|
||||
public ApplicationInExcludeListRule()
|
||||
{
|
||||
Name = "Application is in exclude list validator";
|
||||
Description = "This rule checks if the application is included in the exclusion list specified by the OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES environment variable. If the application is in the exclusion list, the rule skips initialization.";
|
||||
}
|
||||
|
||||
internal override bool Evaluate()
|
||||
{
|
||||
var applicationName = GetApplicationName();
|
||||
|
||||
if (IsApplicationInExcludeList(applicationName))
|
||||
{
|
||||
Logger.Information($"Rule Engine: {applicationName} is in the exclusion list. Skipping initialization.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.Debug($"Rule Engine: {applicationName} is not in the exclusion list. ApplicationInExcludeListRule evaluation success.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string GetApplicationName()
|
||||
{
|
||||
try
|
||||
{
|
||||
return AppDomain.CurrentDomain.FriendlyName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error getting AppDomain.CurrentDomain.FriendlyName: {ex}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsApplicationInExcludeList(string applicationName)
|
||||
{
|
||||
return GetExcludedApplicationNames().Contains(applicationName);
|
||||
}
|
||||
|
||||
private static List<string> GetExcludedApplicationNames()
|
||||
{
|
||||
var excludedProcesses = new List<string>();
|
||||
|
||||
var environmentValue = GetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES");
|
||||
|
||||
if (environmentValue == null)
|
||||
{
|
||||
return excludedProcesses;
|
||||
}
|
||||
|
||||
foreach (var processName in environmentValue.Split(Constants.ConfigurationValues.Separator))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(processName))
|
||||
{
|
||||
excludedProcesses.Add(processName.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
return excludedProcesses;
|
||||
}
|
||||
|
||||
private static string? GetEnvironmentVariable(string variableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(variableName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error getting environment variable {variableName}: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// <copyright file="MinSupportedFrameworkRule.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.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using OpenTelemetry.AutoInstrumentation.Logging;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.RulesEngine;
|
||||
|
||||
internal class MinSupportedFrameworkRule : Rule
|
||||
{
|
||||
private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook");
|
||||
|
||||
public MinSupportedFrameworkRule()
|
||||
{
|
||||
Name = "Minimum Supported Framework Version Validator";
|
||||
Description = "Verifies that the application is running on a supported version of the .NET runtime.";
|
||||
}
|
||||
|
||||
internal override bool Evaluate()
|
||||
{
|
||||
var minSupportedFramework = new FrameworkName(".NETCoreApp,Version=v6.0");
|
||||
var appTargetFramework = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
|
||||
// This is the best way to identify application's target framework.
|
||||
// If entry assembly framework is null, StartupHook should continue its execution.
|
||||
if (appTargetFramework != null)
|
||||
{
|
||||
var appTargetFrameworkName = new FrameworkName(appTargetFramework);
|
||||
var appTargetFrameworkVersion = appTargetFrameworkName.Version;
|
||||
|
||||
if (appTargetFrameworkVersion < minSupportedFramework.Version)
|
||||
{
|
||||
Logger.Information($"Rule Engine: Error in StartupHook initialization: {appTargetFramework} is not supported");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Information("Rule Engine: MinSupportedFrameworkRule evaluation success.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,13 @@ internal class RuleEngine
|
|||
{
|
||||
private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook");
|
||||
|
||||
private readonly List<Rule> _rules = new()
|
||||
private readonly List<Rule> _mandatoryRules = new()
|
||||
{
|
||||
new ApplicationInExcludeListRule(),
|
||||
new MinSupportedFrameworkRule()
|
||||
};
|
||||
|
||||
private readonly List<Rule> _otherRules = new()
|
||||
{
|
||||
new OpenTelemetrySdkMinimumVersionRule(),
|
||||
new DiagnosticSourceRule(),
|
||||
|
|
@ -36,36 +42,55 @@ internal class RuleEngine
|
|||
// This constructor is used for test purpose.
|
||||
internal RuleEngine(List<Rule> rules)
|
||||
{
|
||||
_rules = rules;
|
||||
_otherRules = rules;
|
||||
}
|
||||
|
||||
internal bool Validate()
|
||||
internal bool ValidateRules()
|
||||
{
|
||||
var result = true;
|
||||
|
||||
// Single rule failure will stop the execution.
|
||||
foreach (var rule in _mandatoryRules)
|
||||
{
|
||||
if (!EvaluateRule(rule))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
// All the rules are validated here.
|
||||
foreach (var rule in _otherRules)
|
||||
{
|
||||
try
|
||||
if (!EvaluateRule(rule))
|
||||
{
|
||||
if (!rule.Evaluate())
|
||||
{
|
||||
Logger.Error($"Rule '{rule.Name}' failed: {rule.Description}");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error evaluating rule '{rule.Name}': {ex.Message}");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool EvaluateRule(Rule rule)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!rule.Evaluate())
|
||||
{
|
||||
Logger.Error($"Rule '{rule.Name}' failed: {rule.Description}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"Error evaluating rule '{rule.Name}': {ex.Message}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,8 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using OpenTelemetry.AutoInstrumentation;
|
||||
using OpenTelemetry.AutoInstrumentation.Logging;
|
||||
using OpenTelemetry.AutoInstrumentation.RulesEngine;
|
||||
|
||||
|
|
@ -37,44 +35,19 @@ internal class StartupHook
|
|||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
var minSupportedFramework = new FrameworkName(".NETCoreApp,Version=v6.0");
|
||||
var appTargetFramework = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
|
||||
// This is the best way to identify application's target framework.
|
||||
// If entry assembly framework is null, StartupHook should continue its execution.
|
||||
if (appTargetFramework != null)
|
||||
{
|
||||
var appTargetFrameworkName = new FrameworkName(appTargetFramework);
|
||||
var appTargetFrameworkVersion = appTargetFrameworkName.Version;
|
||||
|
||||
if (appTargetFrameworkVersion < minSupportedFramework.Version)
|
||||
{
|
||||
Logger.Information($"Error in StartupHook initialization: {appTargetFramework} is not supported");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var applicationName = GetApplicationName();
|
||||
Logger.Information($"StartupHook loaded for application with name {applicationName}.");
|
||||
|
||||
if (IsApplicationInExcludeList(applicationName))
|
||||
{
|
||||
Logger.Information("Application is in the exclusion list. Skipping initialization.");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Information("Attempting initialization.");
|
||||
|
||||
LoaderAssemblyLocation = GetLoaderAssemblyLocation();
|
||||
|
||||
try
|
||||
{
|
||||
LoaderAssemblyLocation = GetLoaderAssemblyLocation();
|
||||
|
||||
var ruleEngine = new RuleEngine();
|
||||
if (!ruleEngine.Validate())
|
||||
if (!ruleEngine.ValidateRules())
|
||||
{
|
||||
Logger.Error("Rule Engine Failure: One or more rules failed validation. Auto-Instrumentation won't be loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Information("Initialization.");
|
||||
|
||||
// Creating an instance of OpenTelemetry.AutoInstrumentation.Loader.Startup
|
||||
// will initialize Instrumentation through its static constructor.
|
||||
string loaderFilePath = Path.Combine(LoaderAssemblyLocation, "OpenTelemetry.AutoInstrumentation.Loader.dll");
|
||||
|
|
@ -118,57 +91,4 @@ internal class StartupHook
|
|||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetApplicationName()
|
||||
{
|
||||
try
|
||||
{
|
||||
return AppDomain.CurrentDomain.FriendlyName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error getting AppDomain.CurrentDomain.FriendlyName: {ex}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsApplicationInExcludeList(string applicationName)
|
||||
{
|
||||
return GetExcludedApplicationNames().Contains(applicationName);
|
||||
}
|
||||
|
||||
private static List<string> GetExcludedApplicationNames()
|
||||
{
|
||||
var excludedProcesses = new List<string>();
|
||||
|
||||
var environmentValue = GetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES");
|
||||
|
||||
if (environmentValue == null)
|
||||
{
|
||||
return excludedProcesses;
|
||||
}
|
||||
|
||||
foreach (var processName in environmentValue.Split(Constants.ConfigurationValues.Separator))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(processName))
|
||||
{
|
||||
excludedProcesses.Add(processName.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
return excludedProcesses;
|
||||
}
|
||||
|
||||
private static string? GetEnvironmentVariable(string variableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(variableName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error getting environment variable {variableName}: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class RuleEngineTests : IDisposable
|
|||
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
|
||||
|
||||
// Act
|
||||
var result = ruleEngine.Validate();
|
||||
var result = ruleEngine.ValidateRules();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -51,7 +51,7 @@ public class RuleEngineTests : IDisposable
|
|||
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
|
||||
|
||||
// Act
|
||||
var result = ruleEngine.Validate();
|
||||
var result = ruleEngine.ValidateRules();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -67,7 +67,7 @@ public class RuleEngineTests : IDisposable
|
|||
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
|
||||
|
||||
// Act
|
||||
var result = ruleEngine.Validate();
|
||||
var result = ruleEngine.ValidateRules();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -82,7 +82,7 @@ public class RuleEngineTests : IDisposable
|
|||
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
|
||||
|
||||
// Act
|
||||
var result = ruleEngine.Validate();
|
||||
var result = ruleEngine.ValidateRules();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -98,7 +98,7 @@ public class RuleEngineTests : IDisposable
|
|||
var ruleEngine = new RuleEngine(new List<Rule> { testRule });
|
||||
|
||||
// Act
|
||||
var result = ruleEngine.Validate();
|
||||
var result = ruleEngine.ValidateRules();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
|
|||
Loading…
Reference in New Issue