diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md index 3898965cdf3..cf72bf0c33f 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md @@ -2,11 +2,6 @@ ## Unreleased -* Zipkin will now write numeric, boolean, and floating-point tag values as - primitive types in its emitted JSON instead of converting the values to - strings. - ([#5590](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5590)) - ## 1.8.1 Released 2024-Apr-17 diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs index 1e11eba503b..8038492a0be 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Globalization; using System.Text.Json; using OpenTelemetry.Internal; @@ -152,14 +153,27 @@ public void Write(Utf8JsonWriter writer) writer.WritePropertyName(ZipkinSpanJsonHelper.TagsPropertyName); writer.WriteStartObject(); - foreach (var tag in this.LocalEndpoint.Tags ?? Enumerable.Empty>()) + // Note: The spec says "Primitive types MUST be converted to string using en-US culture settings" + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#attribute + + var originalUICulture = Thread.CurrentThread.CurrentUICulture; + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + + try { - ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); + foreach (var tag in this.LocalEndpoint.Tags ?? Enumerable.Empty>()) + { + ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); + } + + foreach (var tag in this.Tags) + { + ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); + } } - - foreach (var tag in this.Tags) + finally { - ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); + Thread.CurrentThread.CurrentUICulture = originalUICulture; } writer.WriteEndObject(); diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs index 98dc3e0f246..c83b1f92003 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs @@ -3,6 +3,8 @@ #nullable enable +using System.Buffers.Text; +using System.Globalization; using System.Text.Json; using OpenTelemetry.Internal; @@ -10,6 +12,8 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation; internal sealed class ZipkinTagWriter : JsonStringArrayTagWriter { + public const int StackallocByteThreshold = 256; + private ZipkinTagWriter() { } @@ -17,13 +21,33 @@ private ZipkinTagWriter() public static ZipkinTagWriter Instance { get; } = new(); protected override void WriteIntegralTag(ref Utf8JsonWriter writer, string key, long value) - => writer.WriteNumber(key, value); + { + Span destination = stackalloc byte[StackallocByteThreshold]; + if (Utf8Formatter.TryFormat(value, destination, out int bytesWritten)) + { + writer.WriteString(key, destination.Slice(0, bytesWritten)); + } + else + { + writer.WriteString(key, value.ToString(CultureInfo.InvariantCulture)); + } + } protected override void WriteFloatingPointTag(ref Utf8JsonWriter writer, string key, double value) - => writer.WriteNumber(key, value); + { + Span destination = stackalloc byte[StackallocByteThreshold]; + if (Utf8Formatter.TryFormat(value, destination, out int bytesWritten)) + { + writer.WriteString(key, destination.Slice(0, bytesWritten)); + } + else + { + writer.WriteString(key, value.ToString(CultureInfo.InvariantCulture)); + } + } protected override void WriteBooleanTag(ref Utf8JsonWriter writer, string key, bool value) - => writer.WriteBoolean(key, value); + => writer.WriteString(key, value ? "true" : "false"); protected override void WriteStringTag(ref Utf8JsonWriter writer, string key, string value) => writer.WriteString(key, value);