diff --git a/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs b/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs index fcaf9cdb43c..1fdd4d70030 100644 --- a/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs +++ b/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs @@ -73,6 +73,13 @@ protected override void OnUnsupportedTagDropped( this.onUnsupportedTagDropped(tagKey, tagValueTypeFullName); } + protected override bool TryWriteEmptyTag(ref ConsoleTag consoleTag, string key, object? value) + { + consoleTag.Key = key; + consoleTag.Value = null; + return true; + } + internal struct ConsoleTag { public string? Key; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 9360a87a1a7..4e1cddc40a0 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -16,6 +16,11 @@ Notes](../../RELEASENOTES.md). `Activity.StatusDescription` exceeds 127 bytes. ([#6119](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6119)) +* Fixed incorrect log serialization of attributes with null values, causing + some backends to reject logs. + some backends to reject logs when using OTLP exporter to output protobuf. + ([#6149](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6149)) + ## 1.11.1 Released 2025-Jan-22 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs index 1002f90e7c7..ee23a19b421 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs @@ -81,6 +81,13 @@ protected override void OnUnsupportedTagDropped( tagValueTypeFullName, tagKey); + protected override bool TryWriteEmptyTag(ref OtlpTagWriterState state, string key, object? value) + { + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); + return true; + } + internal struct OtlpTagWriterState { public byte[] Buffer; diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs index 30e1eb112ee..3e1c42c69ec 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs @@ -64,4 +64,6 @@ protected override void OnUnsupportedTagDropped( tagValueTypeFullName, tagKey); } + + protected override bool TryWriteEmptyTag(ref Utf8JsonWriter state, string key, object? value) => false; } diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs index f0d17019473..749f2d6822d 100644 --- a/src/Shared/TagWriter/TagWriter.cs +++ b/src/Shared/TagWriter/TagWriter.cs @@ -36,7 +36,7 @@ public bool TryWriteTag( { if (value == null) { - return false; + return this.TryWriteEmptyTag(ref state, key, value); } switch (value) @@ -117,6 +117,8 @@ public bool TryWriteTag( return true; } + protected abstract bool TryWriteEmptyTag(ref TTagState state, string key, object? value); + protected abstract void WriteIntegralTag(ref TTagState state, string key, long value); protected abstract void WriteFloatingPointTag(ref TTagState state, string key, double value); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs index 05ffe584eb0..3991621936b 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs @@ -14,7 +14,13 @@ public class OtlpAttributeTests public void NullValueAttribute() { var kvp = new KeyValuePair("key", null); - Assert.False(TryTransformTag(kvp, out _)); + Assert.True(TryTransformTag(kvp, out var attribute)); + Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.None, attribute.Value.ValueCase); + Assert.False(attribute.Value.HasBoolValue); + Assert.False(attribute.Value.HasBytesValue); + Assert.False(attribute.Value.HasDoubleValue); + Assert.False(attribute.Value.HasIntValue); + Assert.False(attribute.Value.HasStringValue); } [Fact] diff --git a/test/OpenTelemetry.Tests/Internal/JsonStringArrayTagWriterTests.cs b/test/OpenTelemetry.Tests/Internal/JsonStringArrayTagWriterTests.cs index fbdc3da9cf7..2639b6f6366 100644 --- a/test/OpenTelemetry.Tests/Internal/JsonStringArrayTagWriterTests.cs +++ b/test/OpenTelemetry.Tests/Internal/JsonStringArrayTagWriterTests.cs @@ -193,6 +193,11 @@ protected override void OnUnsupportedTagDropped(string tagKey, string tagValueTy { } + protected override bool TryWriteEmptyTag(ref Tag state, string key, object? value) + { + throw new NotImplementedException(); + } + public struct Tag { public string? Key;