diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs index 9667c4328f5..0d315778c9d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs @@ -20,7 +20,7 @@ internal static int WriteResource(byte[] buffer, int writePosition, Resource? re WritePosition = writePosition, }; - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.ResourceSpans_Resource, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.ResourceSpans_Resource, ProtobufWireType.LEN); int resourceLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; @@ -67,7 +67,7 @@ internal static int WriteResource(byte[] buffer, int writePosition, Resource? re private static ProtobufOtlpTagWriter.OtlpTagWriterState ProcessResourceAttribute(ref ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState, KeyValuePair attribute) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Resource_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Resource_Attributes, ProtobufWireType.LEN); int resourceAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs index 2cc0fb4fb81..fdfbcf1af8e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs @@ -17,60 +17,60 @@ private ProtobufOtlpTagWriter() protected override void WriteIntegralTag(ref OtlpTagWriterState state, string key, long value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1; // ComputeVarint64Size(ulong) + TagSize - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Int_Value, (ulong)value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Int_Value, (ulong)value); } protected override void WriteFloatingPointTag(ref OtlpTagWriterState state, string key, double value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 8 + TagSize - state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Double_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 8 + TagSize + state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Double_Value, value); } protected override void WriteBooleanTag(ref OtlpTagWriterState state, string key, bool value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 1 + TagSize - state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Bool_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 1 + TagSize + state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Bool_Value, value); } protected override void WriteStringTag(ref OtlpTagWriterState state, string key, ReadOnlySpan value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(value); var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString); // length = numberOfUtf8CharsInString + tagSize + length field size. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); } protected override void WriteArrayTag(ref OtlpTagWriterState state, string key, ref OtlpTagWriterArrayState value) { // TODO: Expand OtlpTagWriterArrayState.Buffer on IndexOutOfRangeException. // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag and length var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)value.WritePosition); - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition + 1 + serializedLengthSize, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // Array content length + Array tag size + length field size + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // Array content length + Array tag size + length field size // Write Array tag and length - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Array_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Array_Value, ProtobufWireType.LEN); Buffer.BlockCopy(value.Buffer, 0, state.Buffer, state.WritePosition, value.WritePosition); state.WritePosition += value.WritePosition; } @@ -111,26 +111,26 @@ public override OtlpTagWriterArrayState BeginWriteArray() public override void WriteNullValue(ref OtlpTagWriterArrayState state) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); } public override void WriteIntegralValue(ref OtlpTagWriterArrayState state, long value) { var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1; - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Int_Value, (ulong)value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Int_Value, (ulong)value); } public override void WriteFloatingPointValue(ref OtlpTagWriterArrayState state, double value) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Double_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Double_Value, value); } public override void WriteBooleanValue(ref OtlpTagWriterArrayState state, bool value) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Bool_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Bool_Value, value); } public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnlySpan value) @@ -140,8 +140,8 @@ public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnl var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString); // length = numberOfUtf8CharsInString + tagSize + length field size. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); } public override void EndWriteArray(ref OtlpTagWriterArrayState state) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs similarity index 94% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpFieldNumberConstants.cs rename to src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs index 7c4be7d8e86..c7d0198a99f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpFieldNumberConstants.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs @@ -3,7 +3,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; -internal static class ProtobufOtlpFieldNumberConstants +internal static class ProtobufOtlpTraceFieldNumberConstants { // Resource spans #pragma warning disable SA1310 // Field names should not contain underscore @@ -70,6 +70,8 @@ internal static class ProtobufOtlpFieldNumberConstants // InstrumentationScope internal const int InstrumentationScope_Name = 1; internal const int InstrumentationScope_Version = 2; + internal const int InstrumentationScope_Attributes = 3; + internal const int InstrumentationScope_Dropped_Attributes_Count = 4; // KeyValue internal const int KeyValue_Key = 1; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs index 813b691e67f..a250d85096b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs @@ -15,34 +15,188 @@ internal static class ProtobufOtlpTraceSerializer private const int TraceIdSize = 16; private const int SpanIdSize = 8; + private static readonly Stack> ActivityListPool = []; + private static readonly Dictionary> ScopeTracesList = []; + + internal static int WriteTraceData(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource, Batch batch) + { + foreach (var activity in batch) + { + var sourceName = activity.Source.Name; + if (!ScopeTracesList.TryGetValue(sourceName, out var activities)) + { + activities = ActivityListPool.Count > 0 ? ActivityListPool.Pop() : new List(); + ScopeTracesList[sourceName] = activities; + } + + activities.Add(activity); + } + + writePosition = WriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource, ScopeTracesList); + ReturnActivityListToPool(); + + return writePosition; + } + + internal static void ReturnActivityListToPool() + { + if (ScopeTracesList.Count != 0) + { + foreach (var entry in ScopeTracesList) + { + entry.Value.Clear(); + ActivityListPool.Push(entry.Value); + } + + ScopeTracesList.Clear(); + } + } + + internal static int WriteResourceSpans(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource, Dictionary> scopeTraces) + { + int maxAttributeValueLength = sdkLimitOptions.AttributeValueLengthLimit ?? int.MaxValue; + + writePosition = ProtobufOtlpResourceSerializer.WriteResource(buffer, writePosition, resource); + writePosition = WriteScopeSpans(buffer, writePosition, sdkLimitOptions, scopeTraces); + + return writePosition; + } + + internal static int WriteScopeSpans(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Dictionary> scopeTraces) + { + if (scopeTraces != null) + { + foreach (KeyValuePair> entry in scopeTraces) + { + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.ResourceSpans_Scope_Spans, ProtobufWireType.LEN); + int resourceSpansScopeSpansLengthPosition = writePosition; + writePosition += ReserveSizeForLength; + + writePosition = WriteScopeSpan(buffer, writePosition, sdkLimitOptions, entry.Value[0].Source, entry.Value); + ProtobufSerializer.WriteReservedLength(buffer, resourceSpansScopeSpansLengthPosition, writePosition - (resourceSpansScopeSpansLengthPosition + ReserveSizeForLength)); + } + } + + return writePosition; + } + + internal static int WriteScopeSpan(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ActivitySource activitySource, List activities) + { + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.ScopeSpans_Scope, ProtobufWireType.LEN); + int instrumentationScopeLengthPosition = writePosition; + writePosition += ReserveSizeForLength; + + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Name, activitySource.Name); + if (activitySource.Version != null) + { + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Version, activitySource.Version); + } + + if (activitySource.Tags != null) + { + var maxAttributeCount = sdkLimitOptions.SpanAttributeCountLimit ?? int.MaxValue; + var maxAttributeValueLength = sdkLimitOptions.AttributeValueLengthLimit ?? int.MaxValue; + var attributeCount = 0; + var droppedAttributeCount = 0; + + ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState + { + Buffer = buffer, + WritePosition = writePosition, + }; + + if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) + { + for (int i = 0; i < activitySourceTagsList.Count; i++) + { + if (attributeCount < maxAttributeCount) + { + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); + int instrumentationScopeAttributesLengthPosition = otlpTagWriterState.WritePosition; + otlpTagWriterState.WritePosition += ReserveSizeForLength; + + ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, activitySourceTagsList[i].Key, activitySourceTagsList[i].Value, maxAttributeValueLength); + + var instrumentationScopeAttributesLength = otlpTagWriterState.WritePosition - (instrumentationScopeAttributesLengthPosition + ReserveSizeForLength); + ProtobufSerializer.WriteReservedLength(otlpTagWriterState.Buffer, instrumentationScopeAttributesLengthPosition, instrumentationScopeAttributesLength); + attributeCount++; + } + else + { + droppedAttributeCount++; + } + } + } + else + { + foreach (var tag in activitySource.Tags) + { + if (attributeCount < maxAttributeCount) + { + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); + int instrumentationScopeAttributesLengthPosition = otlpTagWriterState.WritePosition; + otlpTagWriterState.WritePosition += ReserveSizeForLength; + + ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag.Key, tag.Value, maxAttributeValueLength); + + var instrumentationScopeAttributesLength = otlpTagWriterState.WritePosition - (instrumentationScopeAttributesLengthPosition + ReserveSizeForLength); + ProtobufSerializer.WriteReservedLength(otlpTagWriterState.Buffer, instrumentationScopeAttributesLengthPosition, instrumentationScopeAttributesLength); + attributeCount++; + } + else + { + droppedAttributeCount++; + } + } + } + + if (droppedAttributeCount > 0) + { + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Dropped_Attributes_Count, ProtobufWireType.VARINT); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteVarInt32(buffer, otlpTagWriterState.WritePosition, (uint)droppedAttributeCount); + } + + writePosition = otlpTagWriterState.WritePosition; + } + + ProtobufSerializer.WriteReservedLength(buffer, instrumentationScopeLengthPosition, writePosition - (instrumentationScopeLengthPosition + ReserveSizeForLength)); + + for (int i = 0; i < activities.Count; i++) + { + writePosition = WriteSpan(buffer, writePosition, sdkLimitOptions, activities[i]); + } + + return writePosition; + } + internal static int WriteSpan(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Activity activity) { - writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.ScopeSpans_Span, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.ScopeSpans_Span, ProtobufWireType.LEN); int spanLengthPosition = writePosition; writePosition += ReserveSizeForLength; - writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, TraceIdSize, ProtobufOtlpFieldNumberConstants.Span_Trace_Id, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, TraceIdSize, ProtobufOtlpTraceFieldNumberConstants.Span_Trace_Id, ProtobufWireType.LEN); writePosition = WriteTraceId(buffer, writePosition, activity.TraceId); - writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpFieldNumberConstants.Span_Span_Id, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpTraceFieldNumberConstants.Span_Span_Id, ProtobufWireType.LEN); writePosition = WriteSpanId(buffer, writePosition, activity.SpanId); if (activity.TraceStateString != null) { - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Trace_State, activity.TraceStateString); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Trace_State, activity.TraceStateString); } if (activity.ParentSpanId != default) { - writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpFieldNumberConstants.Span_Parent_Span_Id, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpTraceFieldNumberConstants.Span_Parent_Span_Id, ProtobufWireType.LEN); writePosition = WriteSpanId(buffer, writePosition, activity.ParentSpanId); } - writePosition = WriteTraceFlags(buffer, writePosition, activity.ActivityTraceFlags, activity.HasRemoteParent, ProtobufOtlpFieldNumberConstants.Span_Flags); - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Name, activity.DisplayName); - writePosition = ProtobufSerializer.WriteEnumWithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Kind, (int)activity.Kind + 1); - writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Start_Time_Unix_Nano, (ulong)activity.StartTimeUtc.ToUnixTimeNanoseconds()); - writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_End_Time_Unix_Nano, (ulong)(activity.StartTimeUtc.ToUnixTimeNanoseconds() + activity.Duration.ToNanoseconds())); + writePosition = WriteTraceFlags(buffer, writePosition, activity.ActivityTraceFlags, activity.HasRemoteParent, ProtobufOtlpTraceFieldNumberConstants.Span_Flags); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Name, activity.DisplayName); + writePosition = ProtobufSerializer.WriteEnumWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Kind, (int)activity.Kind + 1); + writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Start_Time_Unix_Nano, (ulong)activity.StartTimeUtc.ToUnixTimeNanoseconds()); + writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_End_Time_Unix_Nano, (ulong)(activity.StartTimeUtc.ToUnixTimeNanoseconds() + activity.Duration.ToNanoseconds())); (writePosition, StatusCode? statusCode, string? statusMessage) = WriteActivityTags(buffer, writePosition, sdkLimitOptions, activity); writePosition = WriteSpanEvents(buffer, writePosition, sdkLimitOptions, activity); @@ -123,7 +277,7 @@ not null when OkStatusCodeTagValue.Equals(tag.Value as string, StringComparison. if (attributeCount < maxAttributeCount) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Span_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Attributes, ProtobufWireType.LEN); int spanAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; @@ -140,7 +294,7 @@ not null when OkStatusCodeTagValue.Equals(tag.Value as string, StringComparison. if (droppedAttributeCount > 0) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Span_Dropped_Attributes_Count, ProtobufWireType.VARINT); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Dropped_Attributes_Count, ProtobufWireType.VARINT); otlpTagWriterState.WritePosition = ProtobufSerializer.WriteVarInt32(buffer, otlpTagWriterState.WritePosition, (uint)droppedAttributeCount); } @@ -156,12 +310,12 @@ internal static int WriteSpanEvents(byte[] buffer, int writePosition, SdkLimitOp { if (eventCount < maxEventCountLimit) { - writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Events, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Events, ProtobufWireType.LEN); int spanEventsLengthPosition = writePosition; writePosition += ReserveSizeForLength; // Reserve 4 bytes for length - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Event_Name, evnt.Name); - writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Event_Time_Unix_Nano, (ulong)evnt.Timestamp.ToUnixTimeNanoseconds()); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Event_Name, evnt.Name); + writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Event_Time_Unix_Nano, (ulong)evnt.Timestamp.ToUnixTimeNanoseconds()); writePosition = WriteEventAttributes(ref buffer, writePosition, sdkLimitOptions, evnt); ProtobufSerializer.WriteReservedLength(buffer, spanEventsLengthPosition, writePosition - (spanEventsLengthPosition + ReserveSizeForLength)); @@ -175,7 +329,7 @@ internal static int WriteSpanEvents(byte[] buffer, int writePosition, SdkLimitOp if (droppedEventCount > 0) { - writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Dropped_Events_Count, ProtobufWireType.VARINT); + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Dropped_Events_Count, ProtobufWireType.VARINT); writePosition = ProtobufSerializer.WriteVarInt32(buffer, writePosition, (uint)droppedEventCount); } @@ -199,7 +353,7 @@ internal static int WriteEventAttributes(ref byte[] buffer, int writePosition, S { if (attributeCount < maxAttributeCount) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Event_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Event_Attributes, ProtobufWireType.LEN); int eventAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag.Key, tag.Value, maxAttributeValueLength); @@ -214,7 +368,7 @@ internal static int WriteEventAttributes(ref byte[] buffer, int writePosition, S if (droppedAttributeCount > 0) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Event_Dropped_Attributes_Count, ProtobufWireType.VARINT); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Event_Dropped_Attributes_Count, ProtobufWireType.VARINT); otlpTagWriterState.WritePosition = ProtobufSerializer.WriteVarInt32(buffer, otlpTagWriterState.WritePosition, (uint)droppedAttributeCount); } @@ -231,21 +385,21 @@ internal static int WriteSpanLinks(byte[] buffer, int writePosition, SdkLimitOpt { if (linkCount < maxLinksCount) { - writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Links, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Links, ProtobufWireType.LEN); int spanLinksLengthPosition = writePosition; writePosition += ReserveSizeForLength; // Reserve 4 bytes for length - writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, TraceIdSize, ProtobufOtlpFieldNumberConstants.Link_Trace_Id, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, TraceIdSize, ProtobufOtlpTraceFieldNumberConstants.Link_Trace_Id, ProtobufWireType.LEN); writePosition = WriteTraceId(buffer, writePosition, link.Context.TraceId); - writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpFieldNumberConstants.Link_Span_Id, ProtobufWireType.LEN); + writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, SpanIdSize, ProtobufOtlpTraceFieldNumberConstants.Link_Span_Id, ProtobufWireType.LEN); writePosition = WriteSpanId(buffer, writePosition, link.Context.SpanId); if (link.Context.TraceState != null) { - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Trace_State, link.Context.TraceState); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Trace_State, link.Context.TraceState); } writePosition = WriteLinkAttributes(buffer, writePosition, sdkLimitOptions, link); - writePosition = WriteTraceFlags(buffer, writePosition, link.Context.TraceFlags, link.Context.IsRemote, ProtobufOtlpFieldNumberConstants.Link_Flags); + writePosition = WriteTraceFlags(buffer, writePosition, link.Context.TraceFlags, link.Context.IsRemote, ProtobufOtlpTraceFieldNumberConstants.Link_Flags); ProtobufSerializer.WriteReservedLength(buffer, spanLinksLengthPosition, writePosition - (spanLinksLengthPosition + ReserveSizeForLength)); linkCount++; @@ -258,7 +412,7 @@ internal static int WriteSpanLinks(byte[] buffer, int writePosition, SdkLimitOpt if (droppedLinkCount > 0) { - writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpFieldNumberConstants.Span_Dropped_Links_Count, ProtobufWireType.VARINT); + writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.Span_Dropped_Links_Count, ProtobufWireType.VARINT); writePosition = ProtobufSerializer.WriteVarInt32(buffer, writePosition, (uint)droppedLinkCount); } @@ -282,7 +436,7 @@ internal static int WriteLinkAttributes(byte[] buffer, int writePosition, SdkLim { if (attributeCount < maxAttributeCount) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Link_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Link_Attributes, ProtobufWireType.LEN); int linkAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag.Key, tag.Value, maxAttributeValueLength); @@ -297,7 +451,7 @@ internal static int WriteLinkAttributes(byte[] buffer, int writePosition, SdkLim if (droppedAttributeCount > 0) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Link_Dropped_Attributes_Count, ProtobufWireType.VARINT); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.Link_Dropped_Attributes_Count, ProtobufWireType.VARINT); otlpTagWriterState.WritePosition = ProtobufSerializer.WriteVarInt32(buffer, otlpTagWriterState.WritePosition, (uint)droppedAttributeCount); } @@ -319,16 +473,16 @@ internal static int WriteSpanStatus(byte[] buffer, int position, Activity activi { var descriptionSpan = description.AsSpan(); var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(descriptionSpan); - position = ProtobufSerializer.WriteTagAndLength(buffer, position, numberOfUtf8CharsInString + 4, ProtobufOtlpFieldNumberConstants.Span_Status, ProtobufWireType.LEN); - position = ProtobufSerializer.WriteStringWithTag(buffer, position, ProtobufOtlpFieldNumberConstants.Status_Message, numberOfUtf8CharsInString, descriptionSpan); + position = ProtobufSerializer.WriteTagAndLength(buffer, position, numberOfUtf8CharsInString + 4, ProtobufOtlpTraceFieldNumberConstants.Span_Status, ProtobufWireType.LEN); + position = ProtobufSerializer.WriteStringWithTag(buffer, position, ProtobufOtlpTraceFieldNumberConstants.Status_Message, numberOfUtf8CharsInString, descriptionSpan); } else { - position = ProtobufSerializer.WriteTagAndLength(buffer, position, 2, ProtobufOtlpFieldNumberConstants.Span_Status, ProtobufWireType.LEN); + position = ProtobufSerializer.WriteTagAndLength(buffer, position, 2, ProtobufOtlpTraceFieldNumberConstants.Span_Status, ProtobufWireType.LEN); } var finalStatusCode = useActivity ? (int)activity.Status : (statusCode != null && statusCode != StatusCode.Unset) ? (int)statusCode! : (int)StatusCode.Unset; - position = ProtobufSerializer.WriteEnumWithTag(buffer, position, ProtobufOtlpFieldNumberConstants.Status_Code, finalStatusCode); + position = ProtobufSerializer.WriteEnumWithTag(buffer, position, ProtobufOtlpTraceFieldNumberConstants.Status_Code, finalStatusCode); return position; } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 97d62b75345..5b149bb0293 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -128,9 +128,11 @@ public void ServiceProviderHttpClientFactoryInvoked() } [Theory] - [InlineData(true)] - [InlineData(false)] - public void ToOtlpResourceSpansTest(bool includeServiceNameInResource) + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public void ToOtlpResourceSpansTest(bool includeServiceNameInResource, bool useCustomSerializer) { var evenTags = new[] { new KeyValuePair("k0", "v0") }; var oddTags = new[] { new KeyValuePair("k1", "v1") }; @@ -175,7 +177,14 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) { var request = new OtlpCollector.ExportTraceServiceRequest(); - request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + if (useCustomSerializer) + { + request = CreateTraceExportRequest(sdkOptions, batch, resourceBuilder.Build()); + } + else + { + request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + } Assert.Single(request.ResourceSpans); var otlpResource = request.ResourceSpans.First().Resource; @@ -222,8 +231,10 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) } } - [Fact] - public void ScopeAttributesRemainConsistentAcrossMultipleBatches() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ScopeAttributesRemainConsistentAcrossMultipleBatches(bool useCustomSerializer) { var activitySourceTags = new TagList { @@ -266,7 +277,14 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch, ActivitySource a { var request = new OtlpCollector.ExportTraceServiceRequest(); - request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + if (useCustomSerializer) + { + request = CreateTraceExportRequest(sdkOptions, batch, resourceBuilder.Build()); + } + else + { + request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + } var resourceSpans = request.ResourceSpans.First(); Assert.NotNull(request.ResourceSpans.First()); @@ -308,8 +326,10 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch, ActivitySource a } } - [Fact] - public void ScopeAttributesLimitsTest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ScopeAttributesLimitsTest(bool useCustomSerializer) { var sdkOptions = new SdkLimitOptions() { @@ -349,7 +369,14 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) { var request = new OtlpCollector.ExportTraceServiceRequest(); - request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + if (useCustomSerializer) + { + request = CreateTraceExportRequest(sdkOptions, batch, resourceBuilder.Build()); + } + else + { + request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + } var resourceSpans = request.ResourceSpans.First(); Assert.NotNull(request.ResourceSpans.First()); @@ -367,7 +394,10 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); // Return and re-add batch to simulate reuse - request.Return(); + if (!useCustomSerializer) + { + request.Return(); + } } } @@ -1019,6 +1049,17 @@ public void SpanLinkFlagsTest(bool isRecorded, bool isRemote, bool useCustomSeri return scopeSpans.Spans.FirstOrDefault(); } + private static OtlpCollector.ExportTraceServiceRequest CreateTraceExportRequest(SdkLimitOptions sdkOptions, Batch batch, Resource resource) + { + var buffer = new byte[4096]; + var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(buffer, 0, sdkOptions, resource, batch); + using var stream = new MemoryStream(buffer, 0, writePosition); + var tracesData = OtlpTrace.ResourceSpans.Parser.ParseFrom(stream); + var request = new OtlpCollector.ExportTraceServiceRequest(); + request.ResourceSpans.Add(tracesData); + return request; + } + private void ArrayValueAsserts(RepeatedField values) { var expectedStringArray = new string?[] { "1234", "1234", string.Empty, null };