From 68794ad3b190ca13c70c4b03310f0297bfd63187 Mon Sep 17 00:00:00 2001 From: unknown <3676547+alanwest@users.noreply.github.com> Date: Mon, 30 Jan 2023 16:43:29 -0800 Subject: [PATCH 1/5] OTLP exporter add User-Agent header --- .../OtlpExporterOptions.cs | 10 ++++++++++ .../OtlpExporterOptionsExtensions.cs | 5 +++++ .../OtlpExporterOptionsExtensionsTests.cs | 19 ++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 289c0fec8b3..5eaacf9547d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -15,6 +15,7 @@ // using System.Diagnostics; +using System.Reflection; #if NETFRAMEWORK using System.Net.Http; #endif @@ -39,11 +40,20 @@ public class OtlpExporterOptions internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; + internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] + { + new KeyValuePair("User-Agent", UserAgentProductVersion != null ? $"{UserAgentProduct}/{UserAgentProductVersion}" : UserAgentProduct), + }; + internal readonly Func 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 AssemblyName AssemblyName = typeof(OtlpExporterOptions).Assembly.GetName(); + private static readonly Version UserAgentProductVersion = AssemblyName.Version; private Uri endpoint; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs index 2275bdcca93..c4aa08f23d5 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs @@ -89,6 +89,11 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac }); } + foreach (var header in OtlpExporterOptions.StandardHeaders) + { + addHeader(headers, header.Key, header.Value); + } + return headers; } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs index acb49cf7185..74bae7c6ddb 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs @@ -14,6 +14,7 @@ // limitations under the License. // +using Grpc.Core; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; using Xunit; using Xunit.Sdk; @@ -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] @@ -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 { @@ -80,7 +88,12 @@ public void GetHeaders_NoOptionHeaders_ReturnsEmptyHeaders(string optionHeaders) var headers = options.GetHeaders>((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] From 5f1b380e3831f70a5988d76f2e45333a4fbebe90 Mon Sep 17 00:00:00 2001 From: unknown <3676547+alanwest@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:02:38 -0800 Subject: [PATCH 2/5] Fix tests --- .../ExportClient/OtlpHttpTraceExportClientTests.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs index bd21628cd03..76998e52340 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs @@ -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] @@ -179,10 +184,15 @@ void RunTest(Batch 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(httpRequest.Content); Assert.Contains(httpRequest.Content.Headers, h => h.Key == "Content-Type" && h.Value.First() == OtlpHttpTraceExportClient.MediaContentType); From 9d0ded74a2041df03afa13249e77db45b6eda31e Mon Sep 17 00:00:00 2001 From: unknown <3676547+alanwest@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:03:00 -0800 Subject: [PATCH 3/5] Update changelog --- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 3b84183b3ae..e752f1ba822 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Include User-Agent header. + ([#4120](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4120)) + ## 1.4.0-rc.2 Released 2023-Jan-09 From e706e7d2b03fee0bc71e611cc5492309f08191af Mon Sep 17 00:00:00 2001 From: unknown <3676547+alanwest@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:00:25 -0800 Subject: [PATCH 4/5] Update changelog --- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index e752f1ba822..2c18840f57d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,7 +2,8 @@ ## Unreleased -* Include User-Agent header. +* 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 From 168cef67816d941943ebe8a263a3b960a25a22ca Mon Sep 17 00:00:00 2001 From: unknown <3676547+alanwest@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:36:17 -0800 Subject: [PATCH 5/5] Handle exceptions retrieving assembly version --- .../OtlpExporterOptions.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 5eaacf9547d..18efd9de04b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -15,7 +15,6 @@ // using System.Diagnostics; -using System.Reflection; #if NETFRAMEWORK using System.Net.Http; #endif @@ -52,8 +51,7 @@ public class OtlpExporterOptions private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; - private static readonly AssemblyName AssemblyName = typeof(OtlpExporterOptions).Assembly.GetName(); - private static readonly Version UserAgentProductVersion = AssemblyName.Version; + private static readonly Version UserAgentProductVersion = GetAssemblyVersion(); private Uri endpoint; @@ -205,5 +203,18 @@ internal static void RegisterOtlpExporterOptionsFactory(IServiceCollection servi configuration, sp.GetRequiredService>().Get(name))); } + + private static Version GetAssemblyVersion() + { + try + { + var assemblyName = typeof(OtlpExporterOptions).Assembly.GetName(); + return assemblyName.Version; + } + catch (Exception) + { + return null; + } + } } }