From a46a2a9fd1b52829e9906909f7913935d5002637 Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 13 Nov 2025 14:24:00 +0000 Subject: [PATCH 01/13] Fix otlp priority env settings --- .../Configurations/Otlp/OtlpSettings.cs | 40 ++++++++++++++- .../Configurations/SettingsTests.cs | 49 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs index 9d5be2854a..58da8f2f9e 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs @@ -158,8 +158,44 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy { // the default in SDK is grpc. http/protobuf should be default for our purposes var priorityVar = OtlpSpecConfigDefinitions.GetProtocolEnvVar(signalType); - var exporterOtlpProtocol = configuration.GetString(priorityVar) ?? - configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); + var exporterOtlpProtocol = configuration.GetString(priorityVar); + + // SDK handles only general environment variables. + // In case priority env is set, the value must be maually passed. + if (!string.IsNullOrEmpty(exporterOtlpProtocol)) + { + switch (exporterOtlpProtocol) + { + case "grpc": +#if NETFRAMEWORK + if (configuration.FailFast) + { + throw new ArgumentException( + $"OTLP protocol 'grpc' is not supported on .NET Framework in environment variable '{priorityVar}'. Use 'http/protobuf' instead."); + } + else + { + // null value here means that it will be handled by OTEL .NET SDK + return null; + } +#else + return OtlpExportProtocol.Grpc; +#endif + case "http/protobuf": + return OtlpExportProtocol.HttpProtobuf; + default: + if (configuration.FailFast) + { + throw new ArgumentException( + $"Invalid OTLP protocol value '{exporterOtlpProtocol}' in environment variable '{priorityVar}'. Supported values are 'grpc' and 'http/protobuf'."); + } + + // null value here means that it will be handled by OTEL .NET SDK + return null; + } + } + + exporterOtlpProtocol = configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); if (string.IsNullOrEmpty(exporterOtlpProtocol)) { diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index e9f2e2f330..563bbc77b0 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using OpenTelemetry.AutoInstrumentation.Configurations; +using OpenTelemetry.AutoInstrumentation.Configurations.Otlp; using OpenTelemetry.Exporter; using Xunit; @@ -393,6 +394,45 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP Assert.Equal(expectedOtlpExportProtocol, settings.OtlpSettings.Protocol); } + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, "http://example.org/traces/v1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, "http/protobuf"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, "1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, "key1=value1,key2=value2"); + + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + VerifyOtlpPrioritySettings(settings); + } + + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, "http://example.org/traces/v1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, "http/protobuf"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, "1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, "key1=value1,key2=value2"); + + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + VerifyOtlpPrioritySettings(settings); + } + + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, "http://example.org/traces/v1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, "http/protobuf"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, "1"); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, "key1=value1,key2=value2"); + + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + VerifyOtlpPrioritySettings(settings); + } + [Theory] [InlineData("true", true)] [InlineData("false", false)] @@ -478,4 +518,13 @@ private static void ClearEnvVars() Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, null); Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.OracleMdaSetDbStatementForText, null); } + + private void VerifyOtlpPrioritySettings(OtlpSettings? settings) + { + Assert.NotNull(settings); + Assert.Equal(new Uri("http://example.org/traces/v1"), settings.Endpoint); + Assert.Equal(OtlpExportProtocol.HttpProtobuf, settings.Protocol); + Assert.Equal(1, settings.TimeoutMilliseconds); + Assert.Equal("key1=value1,key2=value2", settings.Headers); + } } From 9a27a5a061c858100821368921a064eab1982231 Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 13 Nov 2025 14:51:13 +0000 Subject: [PATCH 02/13] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d276e214..e6dd45cffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h ### Changed +- Fixed `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` + `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` handling. See [#4593](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4593). + #### Dependency updates - Updated [Core components](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/VERSIONING.md#core-components): From eb2e7c0b09332a4645799ff28209e67ddc05e69b Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 13 Nov 2025 15:07:46 +0000 Subject: [PATCH 03/13] fix tests --- .../Configurations/SettingsTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 563bbc77b0..dcad7d728c 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -405,6 +405,7 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); + ClearEnvVars(); } [Fact] @@ -418,6 +419,7 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); + ClearEnvVars(); } [Fact] @@ -431,6 +433,7 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); + ClearEnvVars(); } [Theory] From 1ef993fc25cbcdcc98ec00e958c8d5511690405f Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 13 Nov 2025 15:23:01 +0000 Subject: [PATCH 04/13] fix tests --- .../Configurations/SettingsTests.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index dcad7d728c..4dbe98998b 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Reflection; using OpenTelemetry.AutoInstrumentation.Configurations; using OpenTelemetry.AutoInstrumentation.Configurations.Otlp; using OpenTelemetry.Exporter; @@ -405,7 +406,6 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); - ClearEnvVars(); } [Fact] @@ -419,7 +419,6 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); - ClearEnvVars(); } [Fact] @@ -433,7 +432,6 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() var settings = Settings.FromDefaultSources(false).OtlpSettings; VerifyOtlpPrioritySettings(settings); - ClearEnvVars(); } [Theory] @@ -520,6 +518,21 @@ private static void ClearEnvVars() Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders, null); Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, null); Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.OracleMdaSetDbStatementForText, null); + + // Cleanup OTLP env vars + foreach (var envVar in GetAllOtlpEnvVarNames()) + { + Environment.SetEnvironmentVariable(envVar, null); + } + } + + private static IEnumerable GetAllOtlpEnvVarNames() + { + return typeof(AutoOtlpDefinitions) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(f => f.FieldType == typeof(string)) + .Select(f => (string)f.GetValue(null)!) + .ToList() ?? Enumerable.Empty(); } private void VerifyOtlpPrioritySettings(OtlpSettings? settings) From a60d87cb894e9c09ab003c88d1c8b82890c785c1 Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 13 Nov 2025 15:48:46 +0000 Subject: [PATCH 05/13] refactor tests --- .../Configurations/SettingsTests.cs | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 4dbe98998b..02d39a206a 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -398,40 +398,43 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP [Fact] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, "http://example.org/traces/v1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, "http/protobuf"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, "1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, "key1=value1,key2=value2"); - - var settings = Settings.FromDefaultSources(false).OtlpSettings; + VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, values.Endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, values.Protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, values.Timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, values.Headers); - VerifyOtlpPrioritySettings(settings); + return Settings.FromDefaultSources(false).OtlpSettings; + }); } [Fact] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, "http://example.org/traces/v1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, "http/protobuf"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, "1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, "key1=value1,key2=value2"); - - var settings = Settings.FromDefaultSources(false).OtlpSettings; + VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, values.Endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, values.Protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, values.Timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, values.Headers); - VerifyOtlpPrioritySettings(settings); + return Settings.FromDefaultSources(false).OtlpSettings; + }); } [Fact] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, "http://example.org/traces/v1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, "http/protobuf"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, "1"); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, "key1=value1,key2=value2"); - - var settings = Settings.FromDefaultSources(false).OtlpSettings; + VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => + { + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, values.Endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, values.Protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, values.Timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, values.Headers); - VerifyOtlpPrioritySettings(settings); + return Settings.FromDefaultSources(false).OtlpSettings; + }); } [Theory] @@ -535,12 +538,22 @@ private static IEnumerable GetAllOtlpEnvVarNames() .ToList() ?? Enumerable.Empty(); } - private void VerifyOtlpPrioritySettings(OtlpSettings? settings) + private static void VerifyOtlpPrioritySettings(Func<(string Endpoint, string Protocol, string Timeout, string Headers), OtlpSettings?> setup) { + var endpoint = "http://example.org/traces/v1"; + var endpointValue = new Uri(endpoint); + var protocol = "http/protobuf"; + var protocolValue = OtlpExportProtocol.HttpProtobuf; + var timeout = "1"; + var timeoutValue = 1; + var headers = "key1=value1,key2=value2"; + + var settings = setup((endpoint, protocol, timeout, headers)); + Assert.NotNull(settings); - Assert.Equal(new Uri("http://example.org/traces/v1"), settings.Endpoint); - Assert.Equal(OtlpExportProtocol.HttpProtobuf, settings.Protocol); - Assert.Equal(1, settings.TimeoutMilliseconds); - Assert.Equal("key1=value1,key2=value2", settings.Headers); + Assert.Equal(endpointValue, settings.Endpoint); + Assert.Equal(protocolValue, settings.Protocol); + Assert.Equal(timeoutValue, settings.TimeoutMilliseconds); + Assert.Equal(headers, settings.Headers); } } From 647ff0292f5c52cb855e810ef5ddb479144275f2 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Wed, 19 Nov 2025 17:27:07 -0800 Subject: [PATCH 06/13] Apply parsing for default protocol env var as well --- .../Configurations/Otlp/OtlpSettings.cs | 17 +-- .../Configurations/SettingsTests.cs | 117 ++++++++++-------- 2 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs index 58da8f2f9e..ffad982ab6 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs @@ -158,10 +158,11 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy { // the default in SDK is grpc. http/protobuf should be default for our purposes var priorityVar = OtlpSpecConfigDefinitions.GetProtocolEnvVar(signalType); - var exporterOtlpProtocol = configuration.GetString(priorityVar); + var exporterOtlpProtocol = configuration.GetString(priorityVar) ?? + configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); // SDK handles only general environment variables. - // In case priority env is set, the value must be maually passed. + // In case priority env is set, the value must be manually passed. if (!string.IsNullOrEmpty(exporterOtlpProtocol)) { switch (exporterOtlpProtocol) @@ -195,15 +196,7 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy } } - exporterOtlpProtocol = configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); - - if (string.IsNullOrEmpty(exporterOtlpProtocol)) - { - // override settings only for http/protobuf - return OtlpExportProtocol.HttpProtobuf; - } - - // null value here means that it will be handled by OTEL .NET SDK - return null; + // In case of absent value, it will fall back to default value "otlp" + return OtlpExportProtocol.HttpProtobuf; } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 02d39a206a..3e6db6ed2e 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -3,7 +3,6 @@ using System.Reflection; using OpenTelemetry.AutoInstrumentation.Configurations; -using OpenTelemetry.AutoInstrumentation.Configurations.Otlp; using OpenTelemetry.Exporter; using Xunit; @@ -381,8 +380,12 @@ internal void IncludeFormattedMessage_DependsOnCorrespondingEnvVariable(string i [Theory] [InlineData("", OtlpExportProtocol.HttpProtobuf)] [InlineData(null, OtlpExportProtocol.HttpProtobuf)] - [InlineData("http/protobuf", null)] + [InlineData("http/protobuf", OtlpExportProtocol.HttpProtobuf)] +#if NETFRAMEWORK [InlineData("grpc", null)] +#else + [InlineData("grpc", OtlpExportProtocol.Grpc)] +#endif [InlineData("nonExistingProtocol", null)] internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpProtocol, OtlpExportProtocol? expectedOtlpExportProtocol) { @@ -395,46 +398,77 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP Assert.Equal(expectedOtlpExportProtocol, settings.OtlpSettings.Protocol); } - [Fact] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() + [Theory] + // endpoint, protocol, timeout, headers, expectedProtocol, expectedTimeout + [InlineData("http://example.org/traces/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] +#if NETFRAMEWORK + [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", null, 15000)] +#else + [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", OtlpExportProtocol.Grpc, 15000)] +#endif + [InlineData("http://example.org/traces/v1", "invalid", "42", "x=y", null, 42)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { - VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => - { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, values.Endpoint); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, values.Protocol); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, values.Timeout); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, values.Headers); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, headers); - return Settings.FromDefaultSources(false).OtlpSettings; - }); + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + Assert.NotNull(settings); + Assert.Equal(new Uri(endpoint), settings.Endpoint); + Assert.Equal(expectedProtocol, settings.Protocol); + Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); + Assert.Equal(headers, settings.Headers); } - [Fact] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() + [Theory] + [InlineData("http://example.org/metrics/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] +#if NETFRAMEWORK + [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", null, 25000)] +#else + [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", OtlpExportProtocol.Grpc, 25000)] +#endif + [InlineData("http://example.org/metrics/v1", "invalid", "100", "a=b", null, 100)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { - VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => - { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, values.Endpoint); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, values.Protocol); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, values.Timeout); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, values.Headers); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, headers); - return Settings.FromDefaultSources(false).OtlpSettings; - }); + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + Assert.NotNull(settings); + Assert.Equal(new Uri(endpoint), settings.Endpoint); + Assert.Equal(expectedProtocol, settings.Protocol); + Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); + Assert.Equal(headers, settings.Headers); } - [Fact] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() + [Theory] + [InlineData("http://example.org/logs/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] +#if NETFRAMEWORK + [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", null, 5000)] +#else + [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", OtlpExportProtocol.Grpc, 5000)] +#endif + [InlineData("http://example.org/logs/v1", "invalid", "77", "z=zz", null, 77)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { - VerifyOtlpPrioritySettings(((string Endpoint, string Protocol, string Timeout, string Headers) values) => - { - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, values.Endpoint); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, values.Protocol); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, values.Timeout); - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, values.Headers); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, endpoint); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, protocol); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, timeout); + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, headers); - return Settings.FromDefaultSources(false).OtlpSettings; - }); + var settings = Settings.FromDefaultSources(false).OtlpSettings; + + Assert.NotNull(settings); + Assert.Equal(new Uri(endpoint), settings.Endpoint); + Assert.Equal(expectedProtocol, settings.Protocol); + Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); + Assert.Equal(headers, settings.Headers); } [Theory] @@ -537,23 +571,4 @@ private static IEnumerable GetAllOtlpEnvVarNames() .Select(f => (string)f.GetValue(null)!) .ToList() ?? Enumerable.Empty(); } - - private static void VerifyOtlpPrioritySettings(Func<(string Endpoint, string Protocol, string Timeout, string Headers), OtlpSettings?> setup) - { - var endpoint = "http://example.org/traces/v1"; - var endpointValue = new Uri(endpoint); - var protocol = "http/protobuf"; - var protocolValue = OtlpExportProtocol.HttpProtobuf; - var timeout = "1"; - var timeoutValue = 1; - var headers = "key1=value1,key2=value2"; - - var settings = setup((endpoint, protocol, timeout, headers)); - - Assert.NotNull(settings); - Assert.Equal(endpointValue, settings.Endpoint); - Assert.Equal(protocolValue, settings.Protocol); - Assert.Equal(timeoutValue, settings.TimeoutMilliseconds); - Assert.Equal(headers, settings.Headers); - } } From 5a8f336dc832ff166df2335d6eac749dbede4b14 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Wed, 19 Nov 2025 19:37:15 -0800 Subject: [PATCH 07/13] update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6dd45cffa..9c64613e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,8 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h ### Changed -- Fixed `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` - `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` handling. See [#4593](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4593). +- Support `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` + `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` for all signals. See [#4627](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4627). #### Dependency updates From fa1b0474a72460ce00efce378585af4e0fdcab01 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Thu, 20 Nov 2025 17:24:14 -0800 Subject: [PATCH 08/13] Set to default protocol for invalid protocol and log a warning --- .../AutoInstrumentationEventSource.cs | 2 +- .../Configurations/Otlp/OtlpSettings.cs | 31 ++++++------------- .../Configurations/SettingsTests.cs | 16 +++++----- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation/AutoInstrumentationEventSource.cs b/src/OpenTelemetry.AutoInstrumentation/AutoInstrumentationEventSource.cs index 63e2c135e4..4268d4933e 100644 --- a/src/OpenTelemetry.AutoInstrumentation/AutoInstrumentationEventSource.cs +++ b/src/OpenTelemetry.AutoInstrumentation/AutoInstrumentationEventSource.cs @@ -40,7 +40,7 @@ public void Information(string message) WriteEvent(4, message); } - /// Logs as Warning level message. + /// Logs as Verbose level message. /// Message to log. [Event(5, Message = "{0}", Level = EventLevel.Verbose)] public void Verbose(string message) diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs index ffad982ab6..a0561b59e4 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; +using OpenTelemetry.AutoInstrumentation.Logging; using OpenTelemetry.Exporter; namespace OpenTelemetry.AutoInstrumentation.Configurations.Otlp; @@ -12,6 +13,8 @@ namespace OpenTelemetry.AutoInstrumentation.Configurations.Otlp; /// internal class OtlpSettings { + private static readonly IOtelLogger Logger = OtelLogging.GetLogger(); + public OtlpSettings(OtlpSignalType signalType, Configuration configuration) { Protocol = GetExporterOtlpProtocol(signalType, configuration); @@ -156,47 +159,31 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy private static OtlpExportProtocol? GetExporterOtlpProtocol(OtlpSignalType signalType, Configuration configuration) { - // the default in SDK is grpc. http/protobuf should be default for our purposes + // http/protobuf should be default for our purposes. Always set a value to avoid relying on SDK, because the default in SDK is grpc. var priorityVar = OtlpSpecConfigDefinitions.GetProtocolEnvVar(signalType); var exporterOtlpProtocol = configuration.GetString(priorityVar) ?? configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); - // SDK handles only general environment variables. - // In case priority env is set, the value must be manually passed. if (!string.IsNullOrEmpty(exporterOtlpProtocol)) { switch (exporterOtlpProtocol) { case "grpc": #if NETFRAMEWORK - if (configuration.FailFast) - { - throw new ArgumentException( - $"OTLP protocol 'grpc' is not supported on .NET Framework in environment variable '{priorityVar}'. Use 'http/protobuf' instead."); - } - else - { - // null value here means that it will be handled by OTEL .NET SDK - return null; - } + Logger.Warning($"OTLP protocol 'grpc' is not supported on .NET Framework in environment variable '{priorityVar}'. Changing to 'http/protobuf' instead."); + return OtlpExportProtocol.HttpProtobuf; #else return OtlpExportProtocol.Grpc; #endif case "http/protobuf": return OtlpExportProtocol.HttpProtobuf; default: - if (configuration.FailFast) - { - throw new ArgumentException( - $"Invalid OTLP protocol value '{exporterOtlpProtocol}' in environment variable '{priorityVar}'. Supported values are 'grpc' and 'http/protobuf'."); - } - - // null value here means that it will be handled by OTEL .NET SDK - return null; + Logger.Warning($"Invalid OTLP protocol value '{exporterOtlpProtocol}' in environment variable '{priorityVar}'. Supported values are 'grpc' and 'http/protobuf'. Defaulting to 'http/protobuf'."); + return OtlpExportProtocol.HttpProtobuf; } } - // In case of absent value, it will fall back to default value "otlp" + // In case of absent value, it will fall back to default value return OtlpExportProtocol.HttpProtobuf; } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 3e6db6ed2e..c3d408ce81 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -382,11 +382,11 @@ internal void IncludeFormattedMessage_DependsOnCorrespondingEnvVariable(string i [InlineData(null, OtlpExportProtocol.HttpProtobuf)] [InlineData("http/protobuf", OtlpExportProtocol.HttpProtobuf)] #if NETFRAMEWORK - [InlineData("grpc", null)] + [InlineData("grpc", OtlpExportProtocol.HttpProtobuf)] #else [InlineData("grpc", OtlpExportProtocol.Grpc)] #endif - [InlineData("nonExistingProtocol", null)] + [InlineData("nonExistingProtocol", OtlpExportProtocol.HttpProtobuf)] internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpProtocol, OtlpExportProtocol? expectedOtlpExportProtocol) { Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, otlpProtocol); @@ -402,11 +402,11 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP // endpoint, protocol, timeout, headers, expectedProtocol, expectedTimeout [InlineData("http://example.org/traces/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", null, 15000)] + [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", OtlpExportProtocol.HttpProtobuf, 15000)] #else [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", OtlpExportProtocol.Grpc, 15000)] #endif - [InlineData("http://example.org/traces/v1", "invalid", "42", "x=y", null, 42)] + [InlineData("http://example.org/traces/v1", "invalid", "42", "x=y", OtlpExportProtocol.HttpProtobuf, 42)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, endpoint); @@ -426,11 +426,11 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, s [Theory] [InlineData("http://example.org/metrics/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", null, 25000)] + [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", OtlpExportProtocol.HttpProtobuf, 25000)] #else [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", OtlpExportProtocol.Grpc, 25000)] #endif - [InlineData("http://example.org/metrics/v1", "invalid", "100", "a=b", null, 100)] + [InlineData("http://example.org/metrics/v1", "invalid", "100", "a=b", OtlpExportProtocol.HttpProtobuf, 100)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, endpoint); @@ -450,11 +450,11 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, [Theory] [InlineData("http://example.org/logs/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", null, 5000)] + [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", OtlpExportProtocol.HttpProtobuf, 5000)] #else [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", OtlpExportProtocol.Grpc, 5000)] #endif - [InlineData("http://example.org/logs/v1", "invalid", "77", "z=zz", null, 77)] + [InlineData("http://example.org/logs/v1", "invalid", "77", "z=zz", OtlpExportProtocol.HttpProtobuf, 77)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, endpoint); From c1da4374f77100def8edd092d9a2769b0063c0e8 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Thu, 20 Nov 2025 18:10:01 -0800 Subject: [PATCH 09/13] Set a wrong protocol for distraction --- .../Configurations/SettingsTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index c3d408ce81..06420ea8a9 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -409,6 +409,8 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP [InlineData("http://example.org/traces/v1", "invalid", "42", "x=y", OtlpExportProtocol.HttpProtobuf, 42)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { + var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, timeout); @@ -433,6 +435,8 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, s [InlineData("http://example.org/metrics/v1", "invalid", "100", "a=b", OtlpExportProtocol.HttpProtobuf, 100)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { + var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, timeout); @@ -457,6 +461,8 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, [InlineData("http://example.org/logs/v1", "invalid", "77", "z=zz", OtlpExportProtocol.HttpProtobuf, 77)] internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { + var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; + Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, timeout); From 846384b4013a04bc11d43c4adca7f84728eee2ea Mon Sep 17 00:00:00 2001 From: Sean Li Date: Thu, 20 Nov 2025 18:26:11 -0800 Subject: [PATCH 10/13] remove unused endpoint env var --- .../Configurations/SettingsTests.cs | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs index 06420ea8a9..f226d3213f 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -399,19 +399,17 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP } [Theory] - // endpoint, protocol, timeout, headers, expectedProtocol, expectedTimeout - [InlineData("http://example.org/traces/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] + [InlineData("http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", OtlpExportProtocol.HttpProtobuf, 15000)] + [InlineData("grpc", "15000", "a=b,c=d", OtlpExportProtocol.HttpProtobuf, 15000)] #else - [InlineData("http://example.org/traces/v1", "grpc", "15000", "a=b,c=d", OtlpExportProtocol.Grpc, 15000)] + [InlineData("grpc", "15000", "a=b,c=d", OtlpExportProtocol.Grpc, 15000)] #endif - [InlineData("http://example.org/traces/v1", "invalid", "42", "x=y", OtlpExportProtocol.HttpProtobuf, 42)] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) + [InlineData("invalid", "42", "x=y", OtlpExportProtocol.HttpProtobuf, 42)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string? protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesTimeoutEnvVarName, timeout); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.TracesHeadersEnvVarName, headers); @@ -419,25 +417,23 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces(string endpoint, s var settings = Settings.FromDefaultSources(false).OtlpSettings; Assert.NotNull(settings); - Assert.Equal(new Uri(endpoint), settings.Endpoint); Assert.Equal(expectedProtocol, settings.Protocol); Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); Assert.Equal(headers, settings.Headers); } [Theory] - [InlineData("http://example.org/metrics/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] + [InlineData("http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", OtlpExportProtocol.HttpProtobuf, 25000)] + [InlineData("grpc", "25000", "m=n", OtlpExportProtocol.HttpProtobuf, 25000)] #else - [InlineData("http://example.org/metrics/v1", "grpc", "25000", "m=n", OtlpExportProtocol.Grpc, 25000)] + [InlineData("grpc", "25000", "m=n", OtlpExportProtocol.Grpc, 25000)] #endif - [InlineData("http://example.org/metrics/v1", "invalid", "100", "a=b", OtlpExportProtocol.HttpProtobuf, 100)] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) + [InlineData("invalid", "100", "a=b", OtlpExportProtocol.HttpProtobuf, 100)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string? protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsTimeoutEnvVarName, timeout); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.MetricsHeadersEnvVarName, headers); @@ -445,25 +441,23 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics(string endpoint, var settings = Settings.FromDefaultSources(false).OtlpSettings; Assert.NotNull(settings); - Assert.Equal(new Uri(endpoint), settings.Endpoint); Assert.Equal(expectedProtocol, settings.Protocol); Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); Assert.Equal(headers, settings.Headers); } [Theory] - [InlineData("http://example.org/logs/v1", "http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] + [InlineData("http/protobuf", "1", "key1=value1,key2=value2", OtlpExportProtocol.HttpProtobuf, 1)] #if NETFRAMEWORK - [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", OtlpExportProtocol.HttpProtobuf, 5000)] + [InlineData("grpc", "5000", "l=m", OtlpExportProtocol.HttpProtobuf, 5000)] #else - [InlineData("http://example.org/logs/v1", "grpc", "5000", "l=m", OtlpExportProtocol.Grpc, 5000)] + [InlineData("grpc", "5000", "l=m", OtlpExportProtocol.Grpc, 5000)] #endif - [InlineData("http://example.org/logs/v1", "invalid", "77", "z=zz", OtlpExportProtocol.HttpProtobuf, 77)] - internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string endpoint, string protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) + [InlineData("invalid", "77", "z=zz", OtlpExportProtocol.HttpProtobuf, 77)] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string? protocol, string timeout, string headers, OtlpExportProtocol? expectedProtocol, int expectedTimeout) { var unexpectedProtocolForDistraction = expectedProtocol == OtlpExportProtocol.HttpProtobuf ? "grpc" : "http/protobuf"; Environment.SetEnvironmentVariable(AutoOtlpDefinitions.DefaultProtocolEnvVarName, unexpectedProtocolForDistraction); // set a different default to verify priority - Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsEndpointEnvVarName, endpoint); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsProtocolEnvVarName, protocol); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsTimeoutEnvVarName, timeout); Environment.SetEnvironmentVariable(AutoOtlpDefinitions.LogsHeadersEnvVarName, headers); @@ -471,7 +465,6 @@ internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs(string endpoint, str var settings = Settings.FromDefaultSources(false).OtlpSettings; Assert.NotNull(settings); - Assert.Equal(new Uri(endpoint), settings.Endpoint); Assert.Equal(expectedProtocol, settings.Protocol); Assert.Equal(expectedTimeout, settings.TimeoutMilliseconds); Assert.Equal(headers, settings.Headers); From f9ea3dbec62d55b1b1f7b357c2faafe4a57273df Mon Sep 17 00:00:00 2001 From: Sean Li Date: Thu, 20 Nov 2025 22:31:25 -0800 Subject: [PATCH 11/13] reword changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 666d63fcdd..37c1b7c23c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,9 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h - Setting `DOTNET_STARTUP_HOOKS` is now optional if the .NET CLR profiler is configured. If it is not set, the profiler looks for the startup hooks assembly and injects the configuration into the application. -- Support `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` - `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` for all signals. See [#4627](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4627). +- Fix bug in signal specific OTLP exporter variables: `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, + `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` and `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL`. + See [#4627](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4627). #### Dependency updates From 20e2d7ddb136bf18abe3441ce2ec7962fb14fcaa Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 21 Nov 2025 13:54:04 -0800 Subject: [PATCH 12/13] Introduce a `usedEnvVarName` to record which env var is used --- .../Configurations/Otlp/OtlpSettings.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs index a0561b59e4..1ed8dd216a 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/Otlp/OtlpSettings.cs @@ -161,8 +161,14 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy { // http/protobuf should be default for our purposes. Always set a value to avoid relying on SDK, because the default in SDK is grpc. var priorityVar = OtlpSpecConfigDefinitions.GetProtocolEnvVar(signalType); - var exporterOtlpProtocol = configuration.GetString(priorityVar) ?? - configuration.GetString(OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); + var defaultVar = OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName; + string? usedEnvVarName = priorityVar; + var exporterOtlpProtocol = configuration.GetString(priorityVar); + if (exporterOtlpProtocol == null) + { + exporterOtlpProtocol = configuration.GetString(defaultVar); + usedEnvVarName = defaultVar; + } if (!string.IsNullOrEmpty(exporterOtlpProtocol)) { @@ -170,7 +176,7 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy { case "grpc": #if NETFRAMEWORK - Logger.Warning($"OTLP protocol 'grpc' is not supported on .NET Framework in environment variable '{priorityVar}'. Changing to 'http/protobuf' instead."); + Logger.Warning($"OTLP protocol 'grpc' is not supported on .NET Framework in environment variable '{usedEnvVarName}'. Changing to 'http/protobuf' instead."); return OtlpExportProtocol.HttpProtobuf; #else return OtlpExportProtocol.Grpc; @@ -178,7 +184,7 @@ private static Uri GetOtlpHttpEndpoint(string? endpoint, OtlpSignalType signalTy case "http/protobuf": return OtlpExportProtocol.HttpProtobuf; default: - Logger.Warning($"Invalid OTLP protocol value '{exporterOtlpProtocol}' in environment variable '{priorityVar}'. Supported values are 'grpc' and 'http/protobuf'. Defaulting to 'http/protobuf'."); + Logger.Warning($"Invalid OTLP protocol value '{exporterOtlpProtocol}' in environment variable '{usedEnvVarName}'. Supported values are 'grpc' and 'http/protobuf'. Defaulting to 'http/protobuf'."); return OtlpExportProtocol.HttpProtobuf; } } From dbbca6149a31f4bebdf5451b013c3c843c496dd4 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Fri, 21 Nov 2025 13:59:18 -0800 Subject: [PATCH 13/13] fix CHANGELOG entry --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c1b7c23c..2b49d7baa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,6 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h - Setting `DOTNET_STARTUP_HOOKS` is now optional if the .NET CLR profiler is configured. If it is not set, the profiler looks for the startup hooks assembly and injects the configuration into the application. -- Fix bug in signal specific OTLP exporter variables: `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, - `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` and `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL`. - See [#4627](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4627). #### Dependency updates @@ -105,6 +102,10 @@ and runtime, then loads the correct version of dependency assemblies. See [#4269](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4269) for details. - Fixed rule engine check for .NET 9 to reflect longer support for [STS channel](https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/). +- Fix bug in signal specific OTLP exporter variables: `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, + `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` and `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL`. + See [#4593](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/4593) + for details. ## [1.13.0-beta.1](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v1.13.0-beta.1)