Skip to content

Commit 5efc683

Browse files
[otlp] OTLP Exporter Custom serializer Part 2 - Resources (#5916)
Co-authored-by: Mikel Blanchard <[email protected]>
1 parent 5dff99f commit 5efc683

File tree

6 files changed

+496
-5
lines changed

6 files changed

+496
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer;
5+
6+
internal static class ProtobufOtlpFieldNumberConstants
7+
{
8+
// Resource spans
9+
#pragma warning disable SA1310 // Field names should not contain underscore
10+
internal const int ResourceSpans_Resource = 1;
11+
internal const int ResourceSpans_Scope_Spans = 2;
12+
internal const int ResourceSpans_Schema_Url = 3;
13+
14+
// Resource
15+
internal const int Resource_Attributes = 1;
16+
17+
// ScopeSpans
18+
internal const int ScopeSpans_Scope = 1;
19+
internal const int ScopeSpans_Span = 2;
20+
internal const int ScopeSpans_Schema_Url = 3;
21+
22+
// Span
23+
internal const int Span_Trace_Id = 1;
24+
internal const int Span_Span_Id = 2;
25+
internal const int Span_Trace_State = 3;
26+
internal const int Span_Parent_Span_Id = 4;
27+
internal const int Span_Name = 5;
28+
internal const int Span_Kind = 6;
29+
internal const int Span_Start_Time_Unix_Nano = 7;
30+
internal const int Span_End_Time_Unix_Nano = 8;
31+
internal const int Span_Attributes = 9;
32+
internal const int Span_Dropped_Attributes_Count = 10;
33+
internal const int Span_Events = 11;
34+
internal const int Span_Dropped_Events_Count = 12;
35+
internal const int Span_Links = 13;
36+
internal const int Span_Dropped_Links_Count = 14;
37+
internal const int Span_Status = 15;
38+
internal const int Span_Flags = 16;
39+
40+
// SpanKind
41+
internal const int SpanKind_Internal = 2;
42+
internal const int SpanKind_Server = 3;
43+
internal const int SpanKind_Client = 4;
44+
internal const int SpanKind_Producer = 5;
45+
internal const int SpanKind_Consumer = 6;
46+
47+
// Events
48+
internal const int Event_Time_Unix_Nano = 1;
49+
internal const int Event_Name = 2;
50+
internal const int Event_Attributes = 3;
51+
internal const int Event_Dropped_Attributes_Count = 4;
52+
53+
// Links
54+
internal const int Link_Trace_Id = 1;
55+
internal const int Link_Span_Id = 2;
56+
internal const int Link_Trace_State = 3;
57+
internal const int Link_Attributes = 4;
58+
internal const int Link_Dropped_Attributes_Count = 5;
59+
internal const int Link_Flags = 6;
60+
61+
// Status
62+
internal const int Status_Message = 2;
63+
internal const int Status_Code = 3;
64+
65+
// StatusCode
66+
internal const int StatusCode_Unset = 0;
67+
internal const int StatusCode_Ok = 1;
68+
internal const int StatusCode_Error = 2;
69+
70+
// InstrumentationScope
71+
internal const int InstrumentationScope_Name = 1;
72+
internal const int InstrumentationScope_Version = 2;
73+
74+
// KeyValue
75+
internal const int KeyValue_Key = 1;
76+
internal const int KeyValue_Value = 2;
77+
78+
// AnyValue
79+
internal const int AnyValue_String_Value = 1;
80+
internal const int AnyValue_Bool_Value = 2;
81+
internal const int AnyValue_Int_Value = 3;
82+
internal const int AnyValue_Double_Value = 4;
83+
internal const int AnyValue_Array_Value = 5;
84+
internal const int AnyValue_Kvlist_Value = 6;
85+
internal const int AnyValue_Bytes_Value = 7;
86+
87+
internal const int ArrayValue_Value = 1;
88+
#pragma warning restore SA1310 // Field names should not contain underscore
89+
}
90+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.Resources;
5+
6+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer;
7+
8+
internal static class ProtobufOtlpResourceSerializer
9+
{
10+
private const int ReserveSizeForLength = 4;
11+
12+
private static readonly string DefaultServiceName = ResourceBuilder.CreateDefault().Build().Attributes.FirstOrDefault(
13+
kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName).Value as string ?? "unknown_service";
14+
15+
internal static int WriteResource(byte[] buffer, int writePosition, Resource? resource)
16+
{
17+
ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState
18+
{
19+
Buffer = buffer,
20+
WritePosition = writePosition,
21+
};
22+
23+
otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.ResourceSpans_Resource, ProtobufWireType.LEN);
24+
int resourceLengthPosition = otlpTagWriterState.WritePosition;
25+
otlpTagWriterState.WritePosition += ReserveSizeForLength;
26+
27+
bool isServiceNamePresent = false;
28+
if (resource != null && resource != Resource.Empty)
29+
{
30+
if (resource.Attributes is IReadOnlyList<KeyValuePair<string, object>> resourceAttributesList)
31+
{
32+
for (int i = 0; i < resourceAttributesList.Count; i++)
33+
{
34+
var attribute = resourceAttributesList[i];
35+
if (attribute.Key == ResourceSemanticConventions.AttributeServiceName)
36+
{
37+
isServiceNamePresent = true;
38+
}
39+
40+
otlpTagWriterState = ProcessResourceAttribute(ref otlpTagWriterState, attribute);
41+
}
42+
}
43+
else
44+
{
45+
foreach (var attribute in resource.Attributes)
46+
{
47+
if (attribute.Key == ResourceSemanticConventions.AttributeServiceName)
48+
{
49+
isServiceNamePresent = true;
50+
}
51+
52+
otlpTagWriterState = ProcessResourceAttribute(ref otlpTagWriterState, attribute);
53+
}
54+
}
55+
}
56+
57+
if (!isServiceNamePresent)
58+
{
59+
otlpTagWriterState = ProcessResourceAttribute(ref otlpTagWriterState, new KeyValuePair<string, object>(ResourceSemanticConventions.AttributeServiceName, DefaultServiceName));
60+
}
61+
62+
var resourceLength = otlpTagWriterState.WritePosition - (resourceLengthPosition + ReserveSizeForLength);
63+
ProtobufSerializer.WriteReservedLength(otlpTagWriterState.Buffer, resourceLengthPosition, resourceLength);
64+
65+
return otlpTagWriterState.WritePosition;
66+
}
67+
68+
private static ProtobufOtlpTagWriter.OtlpTagWriterState ProcessResourceAttribute(ref ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState, KeyValuePair<string, object> attribute)
69+
{
70+
otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpFieldNumberConstants.Resource_Attributes, ProtobufWireType.LEN);
71+
int resourceAttributesLengthPosition = otlpTagWriterState.WritePosition;
72+
otlpTagWriterState.WritePosition += ReserveSizeForLength;
73+
74+
ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, attribute.Key, attribute.Value);
75+
76+
var resourceAttributesLength = otlpTagWriterState.WritePosition - (resourceAttributesLengthPosition + ReserveSizeForLength);
77+
ProtobufSerializer.WriteReservedLength(otlpTagWriterState.Buffer, resourceAttributesLengthPosition, resourceAttributesLength);
78+
return otlpTagWriterState;
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.Internal;
5+
6+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer;
7+
8+
internal sealed class ProtobufOtlpTagWriter : TagWriter<ProtobufOtlpTagWriter.OtlpTagWriterState, ProtobufOtlpTagWriter.OtlpTagWriterArrayState>
9+
{
10+
private ProtobufOtlpTagWriter()
11+
: base(new OtlpArrayTagWriter())
12+
{
13+
}
14+
15+
public static ProtobufOtlpTagWriter Instance { get; } = new();
16+
17+
protected override void WriteIntegralTag(ref OtlpTagWriterState state, string key, long value)
18+
{
19+
// Write KeyValue tag
20+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key);
21+
22+
// Write KeyValue.Value tag, length and value.
23+
var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1; // ComputeVarint64Size(ulong) + TagSize
24+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN);
25+
state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Int_Value, (ulong)value);
26+
}
27+
28+
protected override void WriteFloatingPointTag(ref OtlpTagWriterState state, string key, double value)
29+
{
30+
// Write KeyValue tag
31+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key);
32+
33+
// Write KeyValue.Value tag, length and value.
34+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 8 + TagSize
35+
state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Double_Value, value);
36+
}
37+
38+
protected override void WriteBooleanTag(ref OtlpTagWriterState state, string key, bool value)
39+
{
40+
// Write KeyValue tag
41+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key);
42+
43+
// Write KeyValue.Value tag, length and value.
44+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 1 + TagSize
45+
state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Bool_Value, value);
46+
}
47+
48+
protected override void WriteStringTag(ref OtlpTagWriterState state, string key, ReadOnlySpan<char> value)
49+
{
50+
// Write KeyValue tag
51+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key);
52+
53+
// Write KeyValue.Value tag, length and value.
54+
var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(value);
55+
var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString);
56+
57+
// length = numberOfUtf8CharsInString + tagSize + length field size.
58+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN);
59+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value);
60+
}
61+
62+
protected override void WriteArrayTag(ref OtlpTagWriterState state, string key, ref OtlpTagWriterArrayState value)
63+
{
64+
// TODO: Expand OtlpTagWriterArrayState.Buffer on IndexOutOfRangeException.
65+
// Write KeyValue tag
66+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.KeyValue_Key, key);
67+
68+
// Write KeyValue.Value tag and length
69+
var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)value.WritePosition);
70+
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
71+
72+
// Write Array tag and length
73+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Array_Value, ProtobufWireType.LEN);
74+
Buffer.BlockCopy(value.Buffer, 0, state.Buffer, state.WritePosition, value.WritePosition);
75+
state.WritePosition += value.WritePosition;
76+
}
77+
78+
protected override void OnUnsupportedTagDropped(
79+
string tagKey,
80+
string tagValueTypeFullName) => OpenTelemetryProtocolExporterEventSource.Log.UnsupportedAttributeType(
81+
tagValueTypeFullName,
82+
tagKey);
83+
84+
internal struct OtlpTagWriterState
85+
{
86+
public byte[] Buffer;
87+
public int WritePosition;
88+
}
89+
90+
internal struct OtlpTagWriterArrayState
91+
{
92+
public byte[] Buffer;
93+
public int WritePosition;
94+
}
95+
96+
private sealed class OtlpArrayTagWriter : ArrayTagWriter<OtlpTagWriterArrayState>
97+
{
98+
[ThreadStatic]
99+
private static byte[]? threadBuffer;
100+
101+
public override OtlpTagWriterArrayState BeginWriteArray()
102+
{
103+
threadBuffer ??= new byte[2048];
104+
105+
return new OtlpTagWriterArrayState
106+
{
107+
Buffer = threadBuffer,
108+
WritePosition = 0,
109+
};
110+
}
111+
112+
public override void WriteNullValue(ref OtlpTagWriterArrayState state)
113+
{
114+
}
115+
116+
public override void WriteIntegralValue(ref OtlpTagWriterArrayState state, long value)
117+
{
118+
var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1;
119+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN);
120+
state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Int_Value, (ulong)value);
121+
}
122+
123+
public override void WriteFloatingPointValue(ref OtlpTagWriterArrayState state, double value)
124+
{
125+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN);
126+
state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Double_Value, value);
127+
}
128+
129+
public override void WriteBooleanValue(ref OtlpTagWriterArrayState state, bool value)
130+
{
131+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN);
132+
state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_Bool_Value, value);
133+
}
134+
135+
public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnlySpan<char> value)
136+
{
137+
// Write KeyValue.Value tag, length and value.
138+
var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(value);
139+
var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString);
140+
141+
// length = numberOfUtf8CharsInString + tagSize + length field size.
142+
state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN);
143+
state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value);
144+
}
145+
146+
public override void EndWriteArray(ref OtlpTagWriterArrayState state)
147+
{
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)