diff --git a/docs/file-based-configuration.md b/docs/file-based-configuration.md index e779ba1e85..1c662dc2c6 100644 --- a/docs/file-based-configuration.md +++ b/docs/file-based-configuration.md @@ -81,4 +81,21 @@ resource: operatingsystem: # Detects OS-level attributes (os.*) process: # Detects process-level attributes (process.*) processruntime: # Detects process runtime attributes (process.runtime.*) -``` +``` + +### Plugins Configuration + +For more details and updates about Plugins Configuration, see: +[Plugins documentation](plugins.md) + +``` yaml +plugins/development: + # Configure plugins. Entries have higher priority than entries from .plugins_list. + # List of plugins to load. Each entry is the full type name, followed by the assembly name. + # For example: MyNamespace.MyPlugin, MyAssembly, Version=1.0.0, Culture=neutral, PublicKeyToken=null + plugins: + - Test1.Plugins.Plugin, Test1.Plugins, Version=1.0.0, Culture=neutral, PublicKeyToken=null + - Test2.Plugins.Plugin, Test2.Plugins + # Alternatively, configure via a colon-separated list (same format as OTEL_DOTNET_AUTO_PLUGINS). + plugins_list: ${OTEL_DOTNET_AUTO_PLUGINS} +``` diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PluginsConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PluginsConfiguration.cs new file mode 100644 index 0000000000..e3b9bd2eda --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PluginsConfiguration.cs @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Vendors.YamlDotNet.Serialization; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +internal class PluginsConfiguration +{ + /// + /// Gets or sets the list of plugins. + /// + [YamlMember(Alias = "plugins")] + public List? Plugins { get; set; } + + /// + /// Gets or sets the plugins list. + /// + [YamlMember(Alias = "plugins_list")] + public string? PluginsList { get; set; } + + public List ParsePlugins() + { + var uniquePlugins = new HashSet(); + + if (Plugins != null) + { + foreach (var plugin in Plugins) + { + if (!string.IsNullOrWhiteSpace(plugin)) + { + uniquePlugins.Add(plugin.Trim()); + } + } + } + + if (!string.IsNullOrWhiteSpace(PluginsList)) + { + foreach (var plugin in PluginsList!.Split(Constants.ConfigurationValues.DotNetQualifiedNameSeparator)) + { + uniquePlugins.Add(plugin.Trim()); + } + } + + return uniquePlugins.ToList(); + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs index 62a3b94821..bde75b5426 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs @@ -49,4 +49,10 @@ internal class YamlConfiguration /// [YamlMember(Alias = "no_code/development")] public NoCodeConfiguration? NoCode { get; set; } + + /// + /// Gets or sets the plugins configuration. + /// + [YamlMember(Alias = "plugins/development")] + public PluginsConfiguration? Plugins { get; set; } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs index 5af9978bca..09ecaeb41e 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/GeneralSettings.cs @@ -8,11 +8,6 @@ namespace OpenTelemetry.AutoInstrumentation.Configurations; internal class GeneralSettings : Settings { - /// - /// Gets the list of plugins represented by . - /// - public IList Plugins { get; } = []; - /// /// Gets a value indicating whether the event should trigger /// the flushing of telemetry data. @@ -32,15 +27,6 @@ internal class GeneralSettings : Settings protected override void OnLoadEnvVar(Configuration configuration) { - var providerPlugins = configuration.GetString(ConfigurationKeys.ProviderPlugins); - if (providerPlugins != null) - { - foreach (var pluginAssemblyQualifiedName in providerPlugins.Split(Constants.ConfigurationValues.DotNetQualifiedNameSeparator)) - { - Plugins.Add(pluginAssemblyQualifiedName); - } - } - FlushOnUnhandledException = configuration.GetBool(ConfigurationKeys.FlushOnUnhandledException) ?? false; SetupSdk = configuration.GetBool(ConfigurationKeys.SetupSdk) ?? true; diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/PluginsSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/PluginsSettings.cs new file mode 100644 index 0000000000..f9ce823e3f --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/PluginsSettings.cs @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +namespace OpenTelemetry.AutoInstrumentation.Configurations; + +internal class PluginsSettings : Settings +{ + /// + /// Gets the list of plugins represented by . + /// + public IList Plugins { get; private set; } = []; + + protected override void OnLoadEnvVar(Configuration configuration) + { + var providerPlugins = configuration.GetString(ConfigurationKeys.ProviderPlugins); + if (providerPlugins != null) + { + foreach (var pluginAssemblyQualifiedName in providerPlugins.Split(Constants.ConfigurationValues.DotNetQualifiedNameSeparator)) + { + Plugins.Add(pluginAssemblyQualifiedName); + } + } + } + + protected override void OnLoadFile(YamlConfiguration configuration) + { + Plugins = configuration.Plugins?.ParsePlugins() ?? []; + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs index d781ec406a..374a6c0e37 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs @@ -69,6 +69,8 @@ internal static LoggerProvider? LoggerProvider internal static Lazy NoCodeSettings { get; } = new(() => Settings.FromDefaultSources(FailFastSettings.Value.FailFast)); + internal static Lazy PluginsSettings { get; } = new(() => Settings.FromDefaultSources(FailFastSettings.Value.FailFast)); + /// /// Initialize the OpenTelemetry SDK with a pre-defined set of exporters, shims, and /// instrumentations. @@ -102,7 +104,7 @@ public static void Initialize() // Initialize SdkSelfDiagnosticsEventListener to create an EventListener for the OpenTelemetry SDK _sdkEventListener = new(Logger); - _pluginManager = new PluginManager(GeneralSettings.Value); + _pluginManager = new PluginManager(PluginsSettings.Value); _pluginManager.Initializing(); // Register to shutdown events diff --git a/src/OpenTelemetry.AutoInstrumentation/Plugins/PluginManager.cs b/src/OpenTelemetry.AutoInstrumentation/Plugins/PluginManager.cs index 9327dccf75..0ce6304c3b 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Plugins/PluginManager.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Plugins/PluginManager.cs @@ -13,7 +13,7 @@ internal partial class PluginManager { private readonly IReadOnlyList<(Type Type, object Instance)> _plugins; - public PluginManager(GeneralSettings settings) + public PluginManager(PluginsSettings settings) { var plugins = new List<(Type, object)>(); @@ -131,10 +131,7 @@ private void CallPlugins(string methodName) foreach (var plugin in _plugins) { var mi = plugin.Type.GetMethod(methodName, Type.EmptyTypes); - if (mi is not null) - { - mi.Invoke(plugin.Instance, null); - } + mi?.Invoke(plugin.Instance, null); } } @@ -142,11 +139,8 @@ private void CallPlugins(string methodName, (Type Type, object Value) arg) { foreach (var plugin in _plugins) { - var mi = plugin.Type.GetMethod(methodName, new[] { arg.Type }); - if (mi is not null) - { - mi.Invoke(plugin.Instance, new object[] { arg.Value }); - } + var mi = plugin.Type.GetMethod(methodName, [arg.Type]); + mi?.Invoke(plugin.Instance, [arg.Value]); } } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedPluginsSettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedPluginsSettingsTests.cs new file mode 100644 index 0000000000..b5a85c407b --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedPluginsSettingsTests.cs @@ -0,0 +1,70 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations; +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; +using Xunit; + +namespace OpenTelemetry.AutoInstrumentation.Tests.Configurations.FileBased; + +public class FilebasedPluginsSettingsTests +{ + [Fact] + public void LoadFile_PluginsSettings() + { + var conf = new YamlConfiguration + { + Plugins = new PluginsConfiguration + { + Plugins = ["Test.Plugins.Plugin, Test.Plugins"] + } + }; + + var settings = new PluginsSettings(); + + settings.LoadFile(conf); + + Assert.Single(settings.Plugins); + Assert.Contains("Test.Plugins.Plugin, Test.Plugins", settings.Plugins); + } + + [Fact] + public void LoadFile_PluginsListSettings() + { + var conf = new YamlConfiguration + { + Plugins = new PluginsConfiguration + { + PluginsList = "Test.Plugins.Plugin, Test.Plugins" + } + }; + + var settings = new PluginsSettings(); + + settings.LoadFile(conf); + Assert.Single(settings.Plugins); + Assert.Contains("Test.Plugins.Plugin, Test.Plugins", settings.Plugins); + } + + [Fact] + public void LoadFile_MergePluginsSettings() + { + var conf = new YamlConfiguration + { + Plugins = new PluginsConfiguration + { + Plugins = ["Test1.Plugins.Plugin, Test1.Plugins", "Test2.Plugins.Plugin, Test2.Plugins"], + PluginsList = "Test2.Plugins.Plugin, Test2.Plugins:Test3.Plugins.Plugin, Test3.Plugins" + } + }; + + var settings = new PluginsSettings(); + + settings.LoadFile(conf); + + Assert.Equal(3, settings.Plugins.Count); + Assert.Contains("Test1.Plugins.Plugin, Test1.Plugins", settings.Plugins); + Assert.Contains("Test2.Plugins.Plugin, Test2.Plugins", settings.Plugins); + Assert.Contains("Test3.Plugins.Plugin, Test3.Plugins", settings.Plugins); + } +} diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFile.yaml b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFile.yaml new file mode 100644 index 0000000000..24cd86b41a --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFile.yaml @@ -0,0 +1,5 @@ +file_format: "1.0-rc.1" +plugins/development: + plugins: + - Test1.Plugins.Plugin, Test1.Plugins + - Test2.Plugins.Plugin, Test2.Plugins diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFileEnvVars.yaml b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFileEnvVars.yaml new file mode 100644 index 0000000000..340b0be923 --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPluginsFileEnvVars.yaml @@ -0,0 +1,4 @@ +file_format: "1.0-rc.1" +plugins/development: + plugins_list: ${OTEL_DOTNET_AUTO_PLUGINS} + diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPluginsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPluginsTests.cs new file mode 100644 index 0000000000..72ca77e1a6 --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPluginsTests.cs @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Xunit; +using YamlParser = OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser.Parser; + +namespace OpenTelemetry.AutoInstrumentation.Tests.Configurations.FileBased.Parser; + +[Collection("Non-Parallel Collection")] +public class ParserPluginsTests +{ + [Fact] + public void Parse_FullConfigYaml_ShouldPopulateModelCorrectly() + { + var config = YamlParser.ParseYaml("Configurations/FileBased/Files/TestPluginsFile.yaml"); + + Assert.NotNull(config); + + Assert.NotNull(config.Plugins); + Assert.NotNull(config.Plugins.Plugins); + Assert.Null(config.Plugins.PluginsList); + Assert.Equal(2, config.Plugins.Plugins.Count); + Assert.Equal("Test1.Plugins.Plugin, Test1.Plugins", config.Plugins.Plugins[0]); + Assert.Equal("Test2.Plugins.Plugin, Test2.Plugins", config.Plugins.Plugins[1]); + } + + [Fact] + public void Parse_EnvVarYaml_ShouldPopulateModelCompletely() + { + Environment.SetEnvironmentVariable("OTEL_DOTNET_AUTO_PLUGINS", "Test.Plugins.Plugin, Test.Plugins"); + + var config = YamlParser.ParseYaml("Configurations/FileBased/Files/TestPluginsFileEnvVars.yaml"); + + Assert.NotNull(config); + + Assert.NotNull(config.Plugins); + Assert.Null(config.Plugins.Plugins); + Assert.NotNull(config.Plugins.PluginsList); + + Assert.Equal("Test.Plugins.Plugin, Test.Plugins", config.Plugins.PluginsList); + } +} diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/PluginManagerTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/PluginManagerTests.cs index 2c1317b8c7..a9b7085898 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/PluginManagerTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/PluginManagerTests.cs @@ -150,14 +150,14 @@ public void ConfigureResourceSuccess() Assert.Equal("value", resource.Attributes.First().Value); } - private static GeneralSettings GetSettings(string assemblyQualifiedName) + private static PluginsSettings GetSettings(string assemblyQualifiedName) { var config = new Configuration(false, new NameValueConfigurationSource(false, new NameValueCollection() { { ConfigurationKeys.ProviderPlugins, assemblyQualifiedName } })); - var settings = new GeneralSettings(); + var settings = new PluginsSettings(); settings.LoadEnvVar(config); return settings; } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 1b5ebd8fab..41d65a0381 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -30,10 +30,17 @@ internal void GeneralSettings_DefaultValues() { var settings = Settings.FromDefaultSources(false); - Assert.Empty(settings.Plugins); Assert.False(settings.FlushOnUnhandledException); } + [Fact] + internal void PluginsSettings_DefaultValues() + { + var settings = Settings.FromDefaultSources(false); + + Assert.Empty(settings.Plugins); + } + [Fact] internal void ResourceSettings_DefaultValues() { diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj index e34a9eb301..da6639f509 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -15,9 +15,15 @@ PreserveNewest + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest