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): 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..02d39a206a 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/SettingsTests.cs @@ -1,7 +1,9 @@ // 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; using Xunit; @@ -393,6 +395,48 @@ internal void OtlpExportProtocol_DependsOnCorrespondingEnvVariable(string? otlpP Assert.Equal(expectedOtlpExportProtocol, settings.OtlpSettings.Protocol); } + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Traces() + { + 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); + + return Settings.FromDefaultSources(false).OtlpSettings; + }); + } + + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Metrics() + { + 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); + + return Settings.FromDefaultSources(false).OtlpSettings; + }); + } + + [Fact] + internal void OtlpExportProtocol_CheckPriorityEnvIsSet_Logs() + { + 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); + + return Settings.FromDefaultSources(false).OtlpSettings; + }); + } + [Theory] [InlineData("true", true)] [InlineData("false", false)] @@ -477,5 +521,39 @@ 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 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); } }