Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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)
{
case "grpc":
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we support mixed case like "gRPC"?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, I think we should keep it as it is (case sensitive), since all our enum values are currently case sensitive. However, in the latest release of the specification, this behavior was changed, and we should start making all enum values as case insensitive.

It should be a separate issue.

open-telemetry/opentelemetry-specification#4576

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Thanks for the reference!

#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;
}
Comment on lines +177 to +180
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can stop relying on the Otel .NET SDK to handle this and manage the protocol ourselves instead. That way, there will be less confusion about when it’s handled by us and when it’s handled by the SDK.

#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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic above should also be applied to this? (DefaultProtocolEnvVarName is OTEL_EXPORTER_OTLP_PROTOCOL.)


if (string.IsNullOrEmpty(exporterOtlpProtocol))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<TracerSettings>(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<MetricSettings>(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<LogSettings>(false).OtlpSettings;
});
}

[Theory]
[InlineData("true", true)]
[InlineData("false", false)]
Expand Down Expand Up @@ -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<string> GetAllOtlpEnvVarNames()
{
return typeof(AutoOtlpDefinitions)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.FieldType == typeof(string))
.Select(f => (string)f.GetValue(null)!)
.ToList() ?? Enumerable.Empty<string>();
}

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;
Copy link
Copy Markdown
Contributor

@xiang17 xiang17 Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about other values like "grpc", "invalidProtocol" or "" (empty string)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*Applies to the line above.

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);
}
}
Loading