Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

* Include User-Agent header
[per the specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#user-agent).
([#4120](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4120))

## 1.4.0-rc.2

Released 2023-Jan-09
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,19 @@ public class OtlpExporterOptions
internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT";
internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL";

internal static readonly KeyValuePair<string, string>[] StandardHeaders = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("User-Agent", UserAgentProductVersion != null ? $"{UserAgentProduct}/{UserAgentProductVersion}" : UserAgentProduct),
};

internal readonly Func<HttpClient> DefaultHttpClientFactory;

private const string DefaultGrpcEndpoint = "http://localhost:4317";
private const string DefaultHttpEndpoint = "http://localhost:4318";
private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc;
private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet";

private static readonly Version UserAgentProductVersion = GetAssemblyVersion();

private Uri endpoint;

Expand Down Expand Up @@ -195,5 +203,18 @@ internal static void RegisterOtlpExporterOptionsFactory(IServiceCollection servi
configuration,
sp.GetRequiredService<IOptionsMonitor<BatchExportActivityProcessorOptions>>().Get(name)));
}

private static Version GetAssemblyVersion()
{
try
{
var assemblyName = typeof(OtlpExporterOptions).Assembly.GetName();
return assemblyName.Version;
Comment on lines +211 to +212
Copy link
Member

Choose a reason for hiding this comment

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

IMHO it would be better to use either the assembly file version (blue) or even better the assembly informational version (green) instead of the assembly version (red) as it's much more granular:

image

I'd be happy to submit a PR to do that if people agree.

Copy link
Member

Choose a reason for hiding this comment

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

+1 👍

Copy link
Member

Choose a reason for hiding this comment

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

Opened #4143.

}
catch (Exception)
{
return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ public static THeaders GetHeaders<THeaders>(this OtlpExporterOptions options, Ac
});
}

foreach (var header in OtlpExporterOptions.StandardHeaders)
{
addHeader(headers, header.Key, header.Value);
}

return headers;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,14 @@ public void NewOtlpHttpTraceExportClient_OtlpExporterOptions_ExporterHasCorrectP

Assert.NotNull(client.HttpClient);

Assert.Equal(2, client.Headers.Count);
Assert.Equal(2 + OtlpExporterOptions.StandardHeaders.Length, client.Headers.Count);
Assert.Contains(client.Headers, kvp => kvp.Key == header1.Name && kvp.Value == header1.Value);
Assert.Contains(client.Headers, kvp => kvp.Key == header2.Name && kvp.Value == header2.Value);

for (int i = 0; i < OtlpExporterOptions.StandardHeaders.Length; i++)
{
Assert.Contains(client.Headers, entry => entry.Key == OtlpExporterOptions.StandardHeaders[i].Key && entry.Value == OtlpExporterOptions.StandardHeaders[i].Value);
}
}

[Theory]
Expand Down Expand Up @@ -179,10 +184,15 @@ void RunTest(Batch<Activity> batch)
Assert.NotNull(httpRequest);
Assert.Equal(HttpMethod.Post, httpRequest.Method);
Assert.Equal("http://localhost:4317/", httpRequest.RequestUri.AbsoluteUri);
Assert.Equal(2, httpRequest.Headers.Count());
Assert.Equal(OtlpExporterOptions.StandardHeaders.Length + 2, httpRequest.Headers.Count());
Assert.Contains(httpRequest.Headers, h => h.Key == header1.Name && h.Value.First() == header1.Value);
Assert.Contains(httpRequest.Headers, h => h.Key == header2.Name && h.Value.First() == header2.Value);

for (int i = 0; i < OtlpExporterOptions.StandardHeaders.Length; i++)
{
Assert.Contains(httpRequest.Headers, entry => entry.Key == OtlpExporterOptions.StandardHeaders[i].Key && entry.Value.First() == OtlpExporterOptions.StandardHeaders[i].Value);
}

Assert.NotNull(httpRequest.Content);
Assert.IsType<OtlpHttpTraceExportClient.ExportRequestContent>(httpRequest.Content);
Assert.Contains(httpRequest.Content.Headers, h => h.Key == "Content-Type" && h.Value.First() == OtlpHttpTraceExportClient.MediaContentType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

using Grpc.Core;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
using Xunit;
using Xunit.Sdk;
Expand All @@ -37,12 +38,19 @@ public void GetMetadataFromHeadersWorksCorrectFormat(string headers, string[] ke
};
var metadata = options.GetMetadataFromHeaders();

Assert.Equal(keys.Length, metadata.Count);
Assert.Equal(OtlpExporterOptions.StandardHeaders.Length + keys.Length, metadata.Count);

for (int i = 0; i < keys.Length; i++)
{
Assert.Contains(metadata, entry => entry.Key == keys[i] && entry.Value == values[i]);
}

for (int i = 0; i < OtlpExporterOptions.StandardHeaders.Length; i++)
{
// Metadata key is always converted to lowercase.
// See: https://cloud.google.com/dotnet/docs/reference/Grpc.Core/latest/Grpc.Core.Metadata.Entry#Grpc_Core_Metadata_Entry__ctor_System_String_System_String_
Assert.Contains(metadata, entry => entry.Key == OtlpExporterOptions.StandardHeaders[i].Key.ToLower() && entry.Value == OtlpExporterOptions.StandardHeaders[i].Value);
}
}

[Theory]
Expand Down Expand Up @@ -71,7 +79,7 @@ public void GetMetadataFromHeadersThrowsExceptionOnInvalidFormat(string headers)
[Theory]
[InlineData("")]
[InlineData(null)]
public void GetHeaders_NoOptionHeaders_ReturnsEmptyHeaders(string optionHeaders)
public void GetHeaders_NoOptionHeaders_ReturnsStandardHeaders(string optionHeaders)
{
var options = new OtlpExporterOptions
{
Expand All @@ -80,7 +88,12 @@ public void GetHeaders_NoOptionHeaders_ReturnsEmptyHeaders(string optionHeaders)

var headers = options.GetHeaders<Dictionary<string, string>>((d, k, v) => d.Add(k, v));

Assert.Empty(headers);
Assert.Equal(OtlpExporterOptions.StandardHeaders.Length, headers.Count);

for (int i = 0; i < OtlpExporterOptions.StandardHeaders.Length; i++)
{
Assert.Contains(headers, entry => entry.Key == OtlpExporterOptions.StandardHeaders[i].Key && entry.Value == OtlpExporterOptions.StandardHeaders[i].Value);
}
}

[Theory]
Expand Down