Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OTLP exporter add User-Agent header #4120

Merged
merged 9 commits into from
Feb 1, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Include User-Agent header.
alanwest marked this conversation as resolved.
Show resolved Hide resolved
([#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 @@ -15,6 +15,7 @@
// </copyright>

using System.Diagnostics;
using System.Reflection;
#if NETFRAMEWORK
using System.Net.Http;
#endif
Expand All @@ -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<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";
cijothomas marked this conversation as resolved.
Show resolved Hide resolved

private static readonly AssemblyName AssemblyName = typeof(OtlpExporterOptions).Assembly.GetName();
alanwest marked this conversation as resolved.
Show resolved Hide resolved
private static readonly Version UserAgentProductVersion = AssemblyName.Version;

private Uri endpoint;

Expand Down
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