diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs index e28fcda6a60..8038492a0be 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinSpan.cs @@ -153,7 +153,9 @@ public void Write(Utf8JsonWriter writer) writer.WritePropertyName(ZipkinSpanJsonHelper.TagsPropertyName); writer.WriteStartObject(); - // this will be used when we convert int, double, int[], double[] to string + // 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; @@ -161,18 +163,12 @@ public void Write(Utf8JsonWriter writer) { foreach (var tag in this.LocalEndpoint.Tags ?? Enumerable.Empty>()) { - if (ZipkinTagTransformer.Instance.TryTransformTag(tag, out var result)) - { - writer.WriteString(tag.Key, result); - } + ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); } foreach (var tag in this.Tags) { - if (ZipkinTagTransformer.Instance.TryTransformTag(tag, out var result)) - { - writer.WriteString(tag.Key, result); - } + ZipkinTagWriter.Instance.TryWriteTag(ref writer, tag); } } finally diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagTransformer.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagTransformer.cs deleted file mode 100644 index ac8b686e5dc..00000000000 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagTransformer.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Exporter.Zipkin.Implementation; - -internal sealed class ZipkinTagTransformer : TagTransformer -{ - private ZipkinTagTransformer() - { - } - - public static ZipkinTagTransformer Instance { get; } = new(); - - protected override string TransformIntegralTag(string key, long value) => value.ToString(); - - protected override string TransformFloatingPointTag(string key, double value) => value.ToString(); - - protected override string TransformBooleanTag(string key, bool value) => value ? "true" : "false"; - - protected override string TransformStringTag(string key, string value) => value; - - protected override string TransformArrayTag(string key, Array array) - => this.TransformStringTag(key, TagTransformerJsonHelper.JsonSerializeArrayTag(array)); - - protected override void OnUnsupportedTagDropped( - string tagKey, - string tagValueTypeFullName) - { - ZipkinExporterEventSource.Log.UnsupportedAttributeType( - tagValueTypeFullName, - tagKey); - } -} diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs new file mode 100644 index 00000000000..c83b1f92003 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs @@ -0,0 +1,69 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Buffers.Text; +using System.Globalization; +using System.Text.Json; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Exporter.Zipkin.Implementation; + +internal sealed class ZipkinTagWriter : JsonStringArrayTagWriter +{ + public const int StackallocByteThreshold = 256; + + private ZipkinTagWriter() + { + } + + public static ZipkinTagWriter Instance { get; } = new(); + + protected override void WriteIntegralTag(ref Utf8JsonWriter writer, string key, long 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) + { + 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.WriteString(key, value ? "true" : "false"); + + protected override void WriteStringTag(ref Utf8JsonWriter writer, string key, string value) + => writer.WriteString(key, value); + + protected override void WriteArrayTag(ref Utf8JsonWriter writer, string key, ArraySegment arrayUtf8JsonBytes) + { + writer.WritePropertyName(key); + writer.WriteStringValue(arrayUtf8JsonBytes); + } + + protected override void OnUnsupportedTagDropped( + string tagKey, + string tagValueTypeFullName) + { + ZipkinExporterEventSource.Log.UnsupportedAttributeType( + tagValueTypeFullName, + tagKey); + } +} diff --git a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj index d3418763073..3ca65ced4d0 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj +++ b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj @@ -23,8 +23,7 @@ - - +