diff --git a/.cspell/other.txt b/.cspell/other.txt
index 7115c6cc14..777e68cf67 100644
--- a/.cspell/other.txt
+++ b/.cspell/other.txt
@@ -44,6 +44,7 @@ NETRUNTIME
Npgsql
NSERVICEBUS
omnisharp
+OPAMP
OPENTRACING
OPERATINGSYSTEM
ORACLEMDA
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28e0f81e94..c8bbf0ac41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
available for [SqlClient instrumentation](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4343).
This is disabled by default and can be enabled via the
`OTEL_DOTNET_AUTO_SQLCLIENT_NETFX_ILREWRITE_ENABLED` environment variable.
+- Experimental support for OpAMP (by default the client is disabled).
### Changed
diff --git a/docs/config.md b/docs/config.md
index be42d4a8af..d91d8f6b9d 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -508,3 +508,11 @@ instead.
| `OTEL_LOG_LEVEL` | SDK log level. (supported values: `none`,`error`,`warn`,`info`,`debug`) | `info` | [Stable](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `OTEL_DOTNET_AUTO_LOGGER` | AutoInstrumentation diagnostic logs sink. (supported values: `none`,`file`,`console`) | `file` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `OTEL_DOTNET_AUTO_LOG_FILE_SIZE` | Maximum size (in bytes) of a single log file created by the Auto Instrumentation | 10 485 760 (10 MB) | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
+
+## OpAMP Client
+
+| Environment variable | Description | Default value | Status |
+|------------------------------------------|--------------------------------------------|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
+| `OTEL_DOTNET_AUTO_OPAMP_ENABLED` | Enables OpAMP client. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
+| `OTEL_DOTNET_AUTO_OPAMP_SERVER_URL` | OpAMP server url. | `https://localhost:4318/v1/opamp` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
+| `OTEL_DOTNET_AUTO_OPAMP_CONNECTION_TYPE` | OpAMP connection type (http or websocket). | `http` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
\ No newline at end of file
diff --git a/docs/file-based-configuration.md b/docs/file-based-configuration.md
index 773db8f9e3..1687a42bcf 100644
--- a/docs/file-based-configuration.md
+++ b/docs/file-based-configuration.md
@@ -260,6 +260,23 @@ instrumentation/development:
bridge_enabled: true
```
+### OpAMP
+
+``` yaml
+opamp:
+ # Configure if OpAMP client is enabled.
+ # If omitted or null, the client is disabled.
+ enabled: false
+ # Configure the server endpoint. If not explicitly set, a default
+ # URL is returned based on the connection_type.
+ # connection_type:
+ # • connection_type: http -> https://localhost:4318/v1/opamp
+ # • connection_type: websocket -> wss://localhost:4318/v1/opamp
+ server_url: https://localhost:4318/v1/opamp
+ # Configure the type of connection used to communicate with the server (for example, http or websocket).
+ connection_type: http
+```
+
### Configuration based instrumentation
Documentation for configuration based instrumentation can be found in [nocode-instrumentation.md](nocode-instrumentation.md).
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 9db56f4647..aac80fe3b4 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -26,6 +26,7 @@
+
diff --git a/src/OpenTelemetry.AutoInstrumentation.Assemblies.NetFramework/Directory.Packages.props b/src/OpenTelemetry.AutoInstrumentation.Assemblies.NetFramework/Directory.Packages.props
index 63bf8b92dc..2adb9327ab 100644
--- a/src/OpenTelemetry.AutoInstrumentation.Assemblies.NetFramework/Directory.Packages.props
+++ b/src/OpenTelemetry.AutoInstrumentation.Assemblies.NetFramework/Directory.Packages.props
@@ -32,11 +32,49 @@
-
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/netfx_assembly_redirection.h b/src/OpenTelemetry.AutoInstrumentation.Native/netfx_assembly_redirection.h
index f65418f83d..437e2637bc 100644
--- a/src/OpenTelemetry.AutoInstrumentation.Native/netfx_assembly_redirection.h
+++ b/src/OpenTelemetry.AutoInstrumentation.Native/netfx_assembly_redirection.h
@@ -19,6 +19,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
assembly_version_redirect_map_.insert({
{ 462, {
+ { L"Google.Protobuf", {3, 31, 1, 0} },
{ L"Microsoft.Bcl.AsyncInterfaces", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration.Abstractions", {9, 0, 0, 10} },
@@ -51,6 +52,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Instrumentation.Runtime", {1, 13, 0, 708} },
{ L"OpenTelemetry.Instrumentation.SqlClient", {1, 13, 0, 709} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 13, 0, 698} },
+ { L"OpenTelemetry.OpAmp.Client", {0, 1, 0, 664} },
{ L"OpenTelemetry.Resources.Azure", {1, 13, 0, 711} },
{ L"OpenTelemetry.Resources.Host", {1, 13, 0, 683} },
{ L"OpenTelemetry.Resources.OperatingSystem", {1, 13, 0, 713} },
@@ -62,6 +64,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"System.Buffers", {4, 0, 5, 0} },
{ L"System.Collections", {4, 0, 11, 0} },
{ L"System.Collections.Concurrent", {4, 0, 11, 0} },
+ { L"System.Collections.Immutable", {8, 0, 0, 0} },
{ L"System.Collections.NonGeneric", {4, 0, 3, 0} },
{ L"System.Collections.Specialized", {4, 0, 3, 0} },
{ L"System.ComponentModel", {4, 0, 1, 0} },
@@ -163,6 +166,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"System.Xml.XPath.XDocument", {4, 1, 0, 0} },
}},
{ 470, {
+ { L"Google.Protobuf", {3, 31, 1, 0} },
{ L"Microsoft.Bcl.AsyncInterfaces", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration.Abstractions", {9, 0, 0, 10} },
@@ -195,6 +199,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Instrumentation.Runtime", {1, 13, 0, 708} },
{ L"OpenTelemetry.Instrumentation.SqlClient", {1, 13, 0, 709} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 13, 0, 698} },
+ { L"OpenTelemetry.OpAmp.Client", {0, 1, 0, 664} },
{ L"OpenTelemetry.Resources.Azure", {1, 13, 0, 711} },
{ L"OpenTelemetry.Resources.Host", {1, 13, 0, 683} },
{ L"OpenTelemetry.Resources.OperatingSystem", {1, 13, 0, 713} },
@@ -206,6 +211,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"System.Buffers", {4, 0, 5, 0} },
{ L"System.Collections", {4, 0, 11, 0} },
{ L"System.Collections.Concurrent", {4, 0, 11, 0} },
+ { L"System.Collections.Immutable", {8, 0, 0, 0} },
{ L"System.Collections.NonGeneric", {4, 0, 3, 0} },
{ L"System.Collections.Specialized", {4, 0, 3, 0} },
{ L"System.ComponentModel", {4, 0, 1, 0} },
@@ -307,6 +313,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"System.Xml.XPath.XDocument", {4, 1, 0, 0} },
}},
{ 471, {
+ { L"Google.Protobuf", {3, 31, 1, 0} },
{ L"Microsoft.Bcl.AsyncInterfaces", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration.Abstractions", {9, 0, 0, 10} },
@@ -338,6 +345,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Instrumentation.Runtime", {1, 13, 0, 708} },
{ L"OpenTelemetry.Instrumentation.SqlClient", {1, 13, 0, 709} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 13, 0, 698} },
+ { L"OpenTelemetry.OpAmp.Client", {0, 1, 0, 664} },
{ L"OpenTelemetry.Resources.Azure", {1, 13, 0, 711} },
{ L"OpenTelemetry.Resources.Host", {1, 13, 0, 683} },
{ L"OpenTelemetry.Resources.OperatingSystem", {1, 13, 0, 713} },
@@ -346,6 +354,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Shims.OpenTracing", {1, 0, 0, 0} },
{ L"OpenTracing", {0, 12, 1, 0} },
{ L"System.Buffers", {4, 0, 5, 0} },
+ { L"System.Collections.Immutable", {8, 0, 0, 0} },
{ L"System.Data.Common", {4, 2, 0, 0} },
{ L"System.Diagnostics.DiagnosticSource", {9, 0, 0, 10} },
{ L"System.Diagnostics.StackTrace", {4, 1, 0, 0} },
@@ -368,6 +377,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"System.Xml.XPath.XDocument", {4, 1, 0, 0} },
}},
{ 472, {
+ { L"Google.Protobuf", {3, 31, 1, 0} },
{ L"Microsoft.Bcl.AsyncInterfaces", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration", {9, 0, 0, 10} },
{ L"Microsoft.Extensions.Configuration.Abstractions", {9, 0, 0, 10} },
@@ -399,6 +409,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Instrumentation.Runtime", {1, 13, 0, 708} },
{ L"OpenTelemetry.Instrumentation.SqlClient", {1, 13, 0, 709} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 13, 0, 698} },
+ { L"OpenTelemetry.OpAmp.Client", {0, 1, 0, 664} },
{ L"OpenTelemetry.Resources.Azure", {1, 13, 0, 711} },
{ L"OpenTelemetry.Resources.Host", {1, 13, 0, 683} },
{ L"OpenTelemetry.Resources.OperatingSystem", {1, 13, 0, 713} },
@@ -407,6 +418,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Shims.OpenTracing", {1, 0, 0, 0} },
{ L"OpenTracing", {0, 12, 1, 0} },
{ L"System.Buffers", {4, 0, 5, 0} },
+ { L"System.Collections.Immutable", {8, 0, 0, 0} },
{ L"System.Diagnostics.DiagnosticSource", {9, 0, 0, 10} },
{ L"System.IO.Pipelines", {9, 0, 0, 10} },
{ L"System.Memory", {4, 0, 5, 0} },
diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs
index c6420d49a9..39bb0eafa8 100644
--- a/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs
+++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/ConfigurationKeys.cs
@@ -36,6 +36,21 @@ internal partial class ConfigurationKeys
///
public const string SetupSdk = "OTEL_DOTNET_AUTO_SETUP_SDK";
+ ///
+ /// Configuration key for enabling OpAmp client.
+ ///
+ public const string OpAmpEnabled = "OTEL_DOTNET_AUTO_OPAMP_ENABLED";
+
+ ///
+ /// Configuration key for OpAmp server url.
+ ///
+ public const string OpAmpServerUrl = "OTEL_DOTNET_AUTO_OPAMP_SERVER_URL";
+
+ ///
+ /// Configuration key for OpAmp server connection type.
+ ///
+ public const string OpAmpConnectionType = "OTEL_DOTNET_AUTO_OPAMP_CONNECTION_TYPE";
+
///
/// Configuration key for enabling all instrumentations.
///
diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/OpAmpConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/OpAmpConfiguration.cs
new file mode 100644
index 0000000000..bb2bd305cb
--- /dev/null
+++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/OpAmpConfiguration.cs
@@ -0,0 +1,27 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Vendors.YamlDotNet.Serialization;
+
+namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
+
+internal class OpAmpConfiguration
+{
+ ///
+ /// Gets or sets a value indicating whether the OpAmp client is enabled.
+ ///
+ [YamlMember(Alias = "enabled")]
+ public bool? Enabled { get; set; }
+
+ ///
+ /// Gets or sets the URL of the server to which the application connects.
+ ///
+ [YamlMember(Alias = "server_url")]
+ public string? ServerUrl { get; set; }
+
+ ///
+ /// Gets or sets the type of connection used for communication.
+ ///
+ [YamlMember(Alias = "connection_type")]
+ public string? ConnectionType { get; set; }
+}
diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs
index 269a9d6a1c..a92aa9bfcc 100644
--- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs
+++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs
@@ -74,4 +74,10 @@ internal class YamlConfiguration
///
[YamlMember(Alias = "no_code/development")]
public NoCodeConfiguration? NoCode { get; set; }
+
+ ///
+ /// Gets or sets the OpAMP settings.
+ ///
+ [YamlMember(Alias = "opamp")]
+ public OpAmpConfiguration? OpAmp { get; set; }
}
diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/OpAmpSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/OpAmpSettings.cs
new file mode 100644
index 0000000000..b2a8d287cb
--- /dev/null
+++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/OpAmpSettings.cs
@@ -0,0 +1,92 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
+using OpenTelemetry.AutoInstrumentation.Logging;
+
+namespace OpenTelemetry.AutoInstrumentation.Configurations;
+
+internal class OpAmpSettings : Settings
+{
+ private static readonly IOtelLogger Logger = OtelLogging.GetLogger();
+
+ ///
+ /// Gets a value indicating whether the OpAmp client is enabled.
+ ///
+ public bool OpAmpClientEnabled { get; private set; }
+
+ ///
+ /// Gets the URL of the server to which the application connects.
+ ///
+ public Uri? ServerUrl { get; private set; }
+
+ ///
+ /// Gets the type of connection used for communication.
+ ///
+ public string? ConnectionType { get; private set; }
+
+ protected override void OnLoadEnvVar(Configuration configuration)
+ {
+ OpAmpClientEnabled = configuration.GetBool(ConfigurationKeys.OpAmpEnabled) ?? false;
+ ServerUrl = GetServerUrl(configuration.GetString(ConfigurationKeys.OpAmpServerUrl), configuration.FailFast);
+ ConnectionType = GetConnectionType(configuration.GetString(ConfigurationKeys.OpAmpConnectionType), configuration.FailFast);
+ }
+
+ protected override void OnLoadFile(YamlConfiguration configuration)
+ {
+ OpAmpClientEnabled = configuration.OpAmp?.Enabled ?? false;
+ ServerUrl = GetServerUrl(configuration.OpAmp?.ServerUrl, configuration.FailFast);
+ ConnectionType = GetConnectionType(configuration.OpAmp?.ConnectionType, configuration.FailFast);
+ }
+
+ private static Uri? GetServerUrl(string? configurationValue, bool failFast)
+ {
+ if (string.IsNullOrWhiteSpace(configurationValue))
+ {
+ // indicates that the default value should be used
+ return null;
+ }
+
+ try
+ {
+ return new Uri(configurationValue);
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = $"OpAMP server URL configuration has an invalid value: '{configurationValue}'.";
+ Logger.Error(ex, errorMessage);
+
+ if (failFast)
+ {
+ throw new InvalidOperationException(errorMessage, ex);
+ }
+
+ return null;
+ }
+ }
+
+ private static string? GetConnectionType(string? configurationValue, bool failFast)
+ {
+ if (string.IsNullOrWhiteSpace(configurationValue))
+ {
+ // indicates that the default value should be used
+ return null;
+ }
+
+ var isValid = configurationValue!.ToLower() is "http" or "websocket";
+ if (isValid)
+ {
+ return configurationValue!.ToLower();
+ }
+
+ var unsupportedMessage = $"OpAMP connection type configuration has an invalid value: '{configurationValue}'.";
+ Logger.Error(unsupportedMessage);
+
+ if (failFast)
+ {
+ throw new NotSupportedException(unsupportedMessage);
+ }
+
+ return null;
+ }
+}
diff --git a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs
index d781ec406a..b79e5973f4 100644
--- a/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs
+++ b/src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs
@@ -14,6 +14,7 @@
using OpenTelemetry.AutoInstrumentation.Loading;
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.AutoInstrumentation.Plugins;
+using OpenTelemetry.AutoInstrumentation.Util;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@@ -69,6 +70,8 @@ internal static LoggerProvider? LoggerProvider
internal static Lazy NoCodeSettings { get; } = new(() => Settings.FromDefaultSources(FailFastSettings.Value.FailFast));
+ internal static Lazy OpAmpSettings { get; } = new(() => Settings.FromDefaultSources(FailFastSettings.Value.FailFast));
+
///
/// Initialize the OpenTelemetry SDK with a pre-defined set of exporters, shims, and
/// instrumentations.
@@ -215,6 +218,13 @@ public static void Initialize()
{
OpenTracingHelper.EnableOpenTracing(_tracerProvider);
}
+
+ if (OpAmpSettings.Value.OpAmpClientEnabled)
+ {
+ var resources = ResourceHelper.AggregateResources(_tracerProvider, _meterProvider, LoggerProvider);
+
+ OpAmpHelper.EnableOpAmpClient(resources, OpAmpSettings.Value);
+ }
}
#if NET
@@ -533,6 +543,8 @@ private static void OnExit(object? sender, EventArgs e)
try
{
+ OpAmpHelper.StopOpAmpClientIfRunning();
+
#if NET
LazyInstrumentationLoader?.Dispose();
_sampleExporter?.Dispose();
diff --git a/src/OpenTelemetry.AutoInstrumentation/OpAmpHelper.cs b/src/OpenTelemetry.AutoInstrumentation/OpAmpHelper.cs
new file mode 100644
index 0000000000..b366971fb1
--- /dev/null
+++ b/src/OpenTelemetry.AutoInstrumentation/OpAmpHelper.cs
@@ -0,0 +1,140 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Reflection;
+using OpenTelemetry.AutoInstrumentation.Configurations;
+using OpenTelemetry.AutoInstrumentation.Logging;
+using OpenTelemetry.OpAmp.Client;
+using OpenTelemetry.OpAmp.Client.Settings;
+using OpenTelemetry.Resources;
+
+namespace OpenTelemetry.AutoInstrumentation;
+
+internal static class OpAmpHelper
+{
+ private static readonly CancellationTokenSource _cts = new();
+ private static readonly IOtelLogger Logger = OtelLogging.GetLogger();
+
+ private static OpAmpClient? _client;
+ private static Task? _clientRunningTask;
+
+ public static bool IsRunning { get; private set; }
+
+ public static void EnableOpAmpClient(Resource resources, OpAmpSettings opAmpSettins)
+ {
+ try
+ {
+ _client = new OpAmpClient(settings => ConfigureClient(settings, opAmpSettins, resources));
+
+ _clientRunningTask = Task.Run(async () =>
+ {
+ try
+ {
+ await _client.StartAsync(_cts.Token);
+
+ IsRunning = true;
+ }
+ catch (Exception ex)
+ {
+ Logger.Warning(ex, "OpAmp client stopped unexpectedly.");
+
+ IsRunning = false;
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "An error occurred while initializing the OpAmp client.");
+ }
+ }
+
+ public static void StopOpAmpClientIfRunning()
+ {
+ if (!IsRunning)
+ {
+ return;
+ }
+
+ try
+ {
+ _client?.StopAsync().GetAwaiter().GetResult();
+
+ if (_clientRunningTask != null)
+ {
+ _cts.Cancel();
+ _clientRunningTask.GetAwaiter().GetResult();
+ }
+
+ _client?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "An error occurred while stopping the OpAmp client.");
+ }
+ finally
+ {
+ IsRunning = false;
+ }
+ }
+
+ private static void ConfigureClient(OpAmpClientSettings settings, OpAmpSettings opAmpSettings, Resource resources)
+ {
+ // Configure connection type.
+ // Late parse ensures that OpenTelemetry.OpAmp.Client.dll is loaded only when OpAmp is enabled.
+ var connectionType = ParseConnectionType(opAmpSettings.ConnectionType);
+ if (connectionType.HasValue)
+ {
+ settings.ConnectionType = connectionType.Value;
+ }
+
+ // Configure server URL.
+ var serverUrl = opAmpSettings.ServerUrl;
+ if (serverUrl != null)
+ {
+ settings.ServerUrl = serverUrl;
+ }
+
+ // Configure resource attributes for identification.
+ foreach (var resourceAttribute in resources.Attributes)
+ {
+ if (resourceAttribute.Value == null)
+ {
+ continue;
+ }
+
+ var value = resourceAttribute.Value.ToString();
+ if (!string.IsNullOrWhiteSpace(value))
+ {
+ if (resourceAttribute.Key == "service.name")
+ {
+ settings.Identification.AddIdentifyingAttribute(resourceAttribute.Key, value);
+ }
+ else
+ {
+ settings.Identification.AddNonIdentifyingAttribute(resourceAttribute.Key, value);
+ }
+ }
+ }
+
+ settings.Identification.AddNonIdentifyingAttribute("opamp.version", GetOpAmpVersion());
+ }
+
+ private static ConnectionType? ParseConnectionType(string? connectionType)
+ {
+ return connectionType switch
+ {
+ "http" => ConnectionType.Http,
+ "websocket" => ConnectionType.WebSocket,
+ _ => null,
+ };
+ }
+
+ private static string GetOpAmpVersion()
+ {
+ var assembly = typeof(OpAmpClient).Assembly;
+
+ return assembly
+ .GetCustomAttribute()?
+ .InformationalVersion?.Split(['+'], 2)[0] ?? "unknown";
+ }
+}
diff --git a/src/OpenTelemetry.AutoInstrumentation/OpenTelemetry.AutoInstrumentation.csproj b/src/OpenTelemetry.AutoInstrumentation/OpenTelemetry.AutoInstrumentation.csproj
index 79ad3521ce..e31ad87fc6 100644
--- a/src/OpenTelemetry.AutoInstrumentation/OpenTelemetry.AutoInstrumentation.csproj
+++ b/src/OpenTelemetry.AutoInstrumentation/OpenTelemetry.AutoInstrumentation.csproj
@@ -37,6 +37,7 @@
+
diff --git a/src/OpenTelemetry.AutoInstrumentation/Util/ResourceHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Util/ResourceHelper.cs
new file mode 100644
index 0000000000..5238b22973
--- /dev/null
+++ b/src/OpenTelemetry.AutoInstrumentation/Util/ResourceHelper.cs
@@ -0,0 +1,37 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using OpenTelemetry.Resources;
+
+namespace OpenTelemetry.AutoInstrumentation.Util;
+
+internal static class ResourceHelper
+{
+ public static Resource AggregateResources(params BaseProvider?[] providers)
+ {
+ var resource = Resource.Empty;
+
+ foreach (var provider in providers)
+ {
+ if (provider == null)
+ {
+ continue;
+ }
+
+ try
+ {
+ var providerResource = provider.GetResource();
+ if (providerResource != null)
+ {
+ resource = resource.Merge(providerResource);
+ }
+ }
+ catch (Exception)
+ {
+ // intentionally empty
+ }
+ }
+
+ return resource;
+ }
+}
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-arm64.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-arm64.verified.txt
index 7c7c26e312..6f2a5e5c4f 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-arm64.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-arm64.verified.txt
@@ -4,6 +4,7 @@
/LICENSE,
/instrument.sh,
/linux-musl-arm64/OpenTelemetry.AutoInstrumentation.Native.so,
+ /net/Google.Protobuf.dll,
/net/Microsoft.Extensions.Diagnostics.Abstractions.dll,
/net/OpenTelemetry.Api.ProviderBuilderExtensions.dll,
/net/OpenTelemetry.Api.dll,
@@ -30,6 +31,7 @@
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
+ /net/OpenTelemetry.OpAmp.Client.dll,
/net/OpenTelemetry.Resources.Azure.dll,
/net/OpenTelemetry.Resources.Container.dll,
/net/OpenTelemetry.Resources.Host.dll,
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-x64.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-x64.verified.txt
index db81591ff2..5d9e727ad6 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-x64.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_alpine-linux-x64.verified.txt
@@ -4,6 +4,7 @@
/LICENSE,
/instrument.sh,
/linux-musl-x64/OpenTelemetry.AutoInstrumentation.Native.so,
+ /net/Google.Protobuf.dll,
/net/Microsoft.Extensions.Diagnostics.Abstractions.dll,
/net/OpenTelemetry.Api.ProviderBuilderExtensions.dll,
/net/OpenTelemetry.Api.dll,
@@ -30,6 +31,7 @@
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
+ /net/OpenTelemetry.OpAmp.Client.dll,
/net/OpenTelemetry.Resources.Azure.dll,
/net/OpenTelemetry.Resources.Container.dll,
/net/OpenTelemetry.Resources.Host.dll,
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_linux-arm64.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_linux-arm64.verified.txt
index 93d33bbaaa..f6e5df6aaa 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_linux-arm64.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_linux-arm64.verified.txt
@@ -4,6 +4,7 @@
/LICENSE,
/instrument.sh,
/linux-arm64/OpenTelemetry.AutoInstrumentation.Native.so,
+ /net/Google.Protobuf.dll,
/net/Microsoft.Extensions.Diagnostics.Abstractions.dll,
/net/OpenTelemetry.Api.ProviderBuilderExtensions.dll,
/net/OpenTelemetry.Api.dll,
@@ -30,6 +31,7 @@
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
+ /net/OpenTelemetry.OpAmp.Client.dll,
/net/OpenTelemetry.Resources.Azure.dll,
/net/OpenTelemetry.Resources.Container.dll,
/net/OpenTelemetry.Resources.Host.dll,
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_linux-x64.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_linux-x64.verified.txt
index 4a6ef4451c..c1ef51a9d7 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_linux-x64.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_linux-x64.verified.txt
@@ -4,6 +4,7 @@
/LICENSE,
/instrument.sh,
/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so,
+ /net/Google.Protobuf.dll,
/net/Microsoft.Extensions.Diagnostics.Abstractions.dll,
/net/OpenTelemetry.Api.ProviderBuilderExtensions.dll,
/net/OpenTelemetry.Api.dll,
@@ -30,6 +31,7 @@
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
+ /net/OpenTelemetry.OpAmp.Client.dll,
/net/OpenTelemetry.Resources.Azure.dll,
/net/OpenTelemetry.Resources.Container.dll,
/net/OpenTelemetry.Resources.Host.dll,
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_osx.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_osx.verified.txt
index 822c59b11c..b3d67288d6 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_osx.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_osx.verified.txt
@@ -3,6 +3,7 @@
/AdditionalDeps/shared/Microsoft.NETCore.App/9.0.0/OpenTelemetry.AutoInstrumentation.AdditionalDeps.deps.json,
/LICENSE,
/instrument.sh,
+ /net/Google.Protobuf.dll,
/net/Microsoft.Extensions.Diagnostics.Abstractions.dll,
/net/OpenTelemetry.Api.ProviderBuilderExtensions.dll,
/net/OpenTelemetry.Api.dll,
@@ -29,6 +30,7 @@
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
+ /net/OpenTelemetry.OpAmp.Client.dll,
/net/OpenTelemetry.Resources.Azure.dll,
/net/OpenTelemetry.Resources.Container.dll,
/net/OpenTelemetry.Resources.Host.dll,
diff --git a/test/IntegrationTests/BuildTests.DistributionStructure_windows.verified.txt b/test/IntegrationTests/BuildTests.DistributionStructure_windows.verified.txt
index b44dd7222f..950af321d5 100644
--- a/test/IntegrationTests/BuildTests.DistributionStructure_windows.verified.txt
+++ b/test/IntegrationTests/BuildTests.DistributionStructure_windows.verified.txt
@@ -3,6 +3,7 @@
\AdditionalDeps\shared\Microsoft.NETCore.App\9.0.0\OpenTelemetry.AutoInstrumentation.AdditionalDeps.deps.json,
\LICENSE,
\instrument.sh,
+ \net\Google.Protobuf.dll,
\net\Microsoft.Extensions.Diagnostics.Abstractions.dll,
\net\OpenTelemetry.Api.ProviderBuilderExtensions.dll,
\net\OpenTelemetry.Api.dll,
@@ -29,6 +30,7 @@
\net\OpenTelemetry.Instrumentation.SqlClient.dll,
\net\OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
\net\OpenTelemetry.Instrumentation.Wcf.dll,
+ \net\OpenTelemetry.OpAmp.Client.dll,
\net\OpenTelemetry.Resources.Azure.dll,
\net\OpenTelemetry.Resources.Container.dll,
\net\OpenTelemetry.Resources.Host.dll,
@@ -43,6 +45,7 @@
\net\System.ServiceModel.Primitives.dll,
\net\System.ServiceModel.dll,
\net\ruleEngine.json,
+ \netfx\Google.Protobuf.dll,
\netfx\Microsoft.Bcl.AsyncInterfaces.dll,
\netfx\Microsoft.Extensions.Configuration.Abstractions.dll,
\netfx\Microsoft.Extensions.Configuration.Binder.dll,
@@ -74,6 +77,7 @@
\netfx\OpenTelemetry.Instrumentation.Runtime.dll,
\netfx\OpenTelemetry.Instrumentation.SqlClient.dll,
\netfx\OpenTelemetry.Instrumentation.Wcf.dll,
+ \netfx\OpenTelemetry.OpAmp.Client.dll,
\netfx\OpenTelemetry.Resources.Azure.dll,
\netfx\OpenTelemetry.Resources.Host.dll,
\netfx\OpenTelemetry.Resources.OperatingSystem.dll,
@@ -83,6 +87,7 @@
\netfx\OpenTelemetry.dll,
\netfx\OpenTracing.dll,
\netfx\System.Buffers.dll,
+ \netfx\System.Collections.Immutable.dll,
\netfx\System.Diagnostics.DiagnosticSource.dll,
\netfx\System.IO.Pipelines.dll,
\netfx\System.Memory.dll,
diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Util/ResourceHelperTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Util/ResourceHelperTests.cs
new file mode 100644
index 0000000000..88250ae050
--- /dev/null
+++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Util/ResourceHelperTests.cs
@@ -0,0 +1,67 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using OpenTelemetry.AutoInstrumentation.Util;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using Xunit;
+
+namespace OpenTelemetry.AutoInstrumentation.Tests.Util;
+
+public class ResourceHelperTests
+{
+ [Fact]
+ public void AggregateResources_AllProvidersNull_ReturnsEmptyResource()
+ {
+ var attributes1 = new Dictionary
+ {
+ { "service.name", "my-service" },
+ { "service.version", "1.0.0" }
+ };
+
+ var attributes2 = new Dictionary
+ {
+ { "service.name", "my-service-2" },
+ { "service.namespace", "my-namespace" }
+ };
+
+ var tracerProvider = Sdk
+ .CreateTracerProviderBuilder()
+ .ConfigureResource(resource =>
+ {
+ resource.Clear();
+ resource.AddAttributes(attributes1);
+ })
+ .Build();
+
+ var meterProvider = Sdk
+ .CreateMeterProviderBuilder()
+ .ConfigureResource(resource =>
+ {
+ resource.Clear();
+ resource.AddAttributes(attributes2);
+ })
+ .Build();
+
+ var resource = ResourceHelper.AggregateResources(tracerProvider, meterProvider);
+
+ Assert.Collection(
+ resource.Attributes,
+ attribute =>
+ {
+ Assert.Equal("service.name", attribute.Key);
+ Assert.Equal("my-service-2", attribute.Value);
+ },
+ attribute =>
+ {
+ Assert.Equal("service.namespace", attribute.Key);
+ Assert.Equal("my-namespace", attribute.Value);
+ },
+ attribute =>
+ {
+ Assert.Equal("service.version", attribute.Key);
+ Assert.Equal("1.0.0", attribute.Value);
+ });
+ }
+}