otel tracing: add binary format, grpcTraceBinContextPropagator (#11409) · grpc/grpc-java@043ba55

1+

/*

2+

* Copyright 2024 The gRPC Authors

3+

*

4+

* Licensed under the Apache License, Version 2.0 (the "License");

5+

* you may not use this file except in compliance with the License.

6+

* You may obtain a copy of the License at

7+

*

8+

* http://www.apache.org/licenses/LICENSE-2.0

9+

*

10+

* Unless required by applicable law or agreed to in writing, software

11+

* distributed under the License is distributed on an "AS IS" BASIS,

12+

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

13+

* See the License for the specific language governing permissions and

14+

* limitations under the License.

15+

*/

16+17+

package io.grpc.opentelemetry;

18+19+20+

import static com.google.common.base.Preconditions.checkNotNull;

21+22+

import io.grpc.Metadata;

23+

import io.opentelemetry.api.trace.SpanContext;

24+

import io.opentelemetry.api.trace.SpanId;

25+

import io.opentelemetry.api.trace.TraceFlags;

26+

import io.opentelemetry.api.trace.TraceId;

27+

import io.opentelemetry.api.trace.TraceState;

28+

import java.util.Arrays;

29+30+

/**

31+

* Binary encoded {@link SpanContext} for context propagation. This is adapted from OpenCensus

32+

* binary format.

33+

*

34+

* <p>BinaryFormat format:

35+

*

36+

* <ul>

37+

* <li>Binary value: &lt;version_id&gt;&lt;version_format&gt;

38+

* <li>version_id: 1-byte representing the version id.

39+

* <li>For version_id = 0:

40+

* <ul>

41+

* <li>version_format: &lt;field&gt;&lt;field&gt;

42+

* <li>field_format: &lt;field_id&gt;&lt;field_format&gt;

43+

* <li>Fields:

44+

* <ul>

45+

* <li>TraceId: (field_id = 0, len = 16, default = &#34;0000000000000000&#34;) -

46+

* 16-byte array representing the trace_id.

47+

* <li>SpanId: (field_id = 1, len = 8, default = &#34;00000000&#34;) - 8-byte array

48+

* representing the span_id.

49+

* <li>TraceFlags: (field_id = 2, len = 1, default = &#34;0&#34;) - 1-byte array

50+

* representing the trace_flags.

51+

* </ul>

52+

* <li>Fields MUST be encoded using the field id order (smaller to higher).

53+

* <li>Valid value example:

54+

* <ul>

55+

* <li>{0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97,

56+

* 98, 99, 100, 101, 102, 103, 104, 2, 1}

57+

* <li>version_id = 0;

58+

* <li>trace_id = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}

59+

* <li>span_id = {97, 98, 99, 100, 101, 102, 103, 104};

60+

* <li>trace_flags = {1};

61+

* </ul>

62+

* </ul>

63+

* </ul>

64+

*/

65+

final class BinaryFormat implements Metadata.BinaryMarshaller<SpanContext> {

66+

private static final byte VERSION_ID = 0;

67+

private static final int VERSION_ID_OFFSET = 0;

68+

private static final byte ID_SIZE = 1;

69+

private static final byte TRACE_ID_FIELD_ID = 0;

70+71+

private static final int TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE;

72+

private static final int TRACE_ID_OFFSET = TRACE_ID_FIELD_ID_OFFSET + ID_SIZE;

73+

private static final int TRACE_ID_SIZE = TraceId.getLength() / 2;

74+75+

private static final byte SPAN_ID_FIELD_ID = 1;

76+

private static final int SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TRACE_ID_SIZE;

77+

private static final int SPAN_ID_OFFSET = SPAN_ID_FIELD_ID_OFFSET + ID_SIZE;

78+

private static final int SPAN_ID_SIZE = SpanId.getLength() / 2;

79+80+

private static final byte TRACE_FLAG_FIELD_ID = 2;

81+

private static final int TRACE_FLAG_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SPAN_ID_SIZE;

82+

private static final int TRACE_FLAG_OFFSET = TRACE_FLAG_FIELD_ID_OFFSET + ID_SIZE;

83+

private static final int REQUIRED_FORMAT_LENGTH = 3 * ID_SIZE + TRACE_ID_SIZE + SPAN_ID_SIZE;

84+

private static final int TRACE_FLAG_SIZE = TraceFlags.getLength() / 2;

85+

private static final int ALL_FORMAT_LENGTH = REQUIRED_FORMAT_LENGTH + ID_SIZE + TRACE_FLAG_SIZE;

86+87+

private static final BinaryFormat INSTANCE = new BinaryFormat();

88+89+

public static BinaryFormat getInstance() {

90+

return INSTANCE;

91+

}

92+93+

@Override

94+

public byte[] toBytes(SpanContext spanContext) {

95+

checkNotNull(spanContext, "spanContext");

96+

byte[] bytes = new byte[ALL_FORMAT_LENGTH];

97+

bytes[VERSION_ID_OFFSET] = VERSION_ID;

98+

bytes[TRACE_ID_FIELD_ID_OFFSET] = TRACE_ID_FIELD_ID;

99+

System.arraycopy(spanContext.getTraceIdBytes(), 0, bytes, TRACE_ID_OFFSET, TRACE_ID_SIZE);

100+

bytes[SPAN_ID_FIELD_ID_OFFSET] = SPAN_ID_FIELD_ID;

101+

System.arraycopy(spanContext.getSpanIdBytes(), 0, bytes, SPAN_ID_OFFSET, SPAN_ID_SIZE);

102+

bytes[TRACE_FLAG_FIELD_ID_OFFSET] = TRACE_FLAG_FIELD_ID;

103+

bytes[TRACE_FLAG_OFFSET] = spanContext.getTraceFlags().asByte();

104+

return bytes;

105+

}

106+107+108+

@Override

109+

public SpanContext parseBytes(byte[] serialized) {

110+

checkNotNull(serialized, "bytes");

111+

if (serialized.length == 0 || serialized[0] != VERSION_ID) {

112+

throw new IllegalArgumentException("Unsupported version.");

113+

}

114+

if (serialized.length < REQUIRED_FORMAT_LENGTH) {

115+

throw new IllegalArgumentException("Invalid input: truncated");

116+

}

117+

String traceId;

118+

String spanId;

119+

TraceFlags traceFlags = TraceFlags.getDefault();

120+

int pos = 1;

121+

if (serialized[pos] == TRACE_ID_FIELD_ID) {

122+

traceId = TraceId.fromBytes(

123+

Arrays.copyOfRange(serialized, pos + ID_SIZE, pos + ID_SIZE + TRACE_ID_SIZE));

124+

pos += ID_SIZE + TRACE_ID_SIZE;

125+

} else {

126+

throw new IllegalArgumentException("Invalid input: expected trace ID at offset " + pos);

127+

}

128+

if (serialized[pos] == SPAN_ID_FIELD_ID) {

129+

spanId = SpanId.fromBytes(

130+

Arrays.copyOfRange(serialized, pos + ID_SIZE, pos + ID_SIZE + SPAN_ID_SIZE));

131+

pos += ID_SIZE + SPAN_ID_SIZE;

132+

} else {

133+

throw new IllegalArgumentException("Invalid input: expected span ID at offset " + pos);

134+

}

135+

if (serialized.length > pos && serialized[pos] == TRACE_FLAG_FIELD_ID) {

136+

if (serialized.length < ALL_FORMAT_LENGTH) {

137+

throw new IllegalArgumentException("Invalid input: truncated");

138+

}

139+

traceFlags = TraceFlags.fromByte(serialized[pos + ID_SIZE]);

140+

}

141+

return SpanContext.create(traceId, spanId, traceFlags, TraceState.getDefault());

142+

}

143+

}