• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018, OpenCensus 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.opencensus.implcore.trace.propagation;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 
22 import com.google.common.annotations.VisibleForTesting;
23 import com.google.common.base.Splitter;
24 import io.opencensus.trace.SpanContext;
25 import io.opencensus.trace.SpanId;
26 import io.opencensus.trace.TraceId;
27 import io.opencensus.trace.TraceOptions;
28 import io.opencensus.trace.Tracestate;
29 import io.opencensus.trace.propagation.SpanContextParseException;
30 import io.opencensus.trace.propagation.TextFormat;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.regex.Pattern;
35 
36 /*>>>
37 import org.checkerframework.checker.nullness.qual.NonNull;
38 */
39 
40 /**
41  * Implementation of the TraceContext propagation protocol. See <a
42  * href=https://github.com/w3c/distributed-tracing>w3c/distributed-tracing</a>.
43  */
44 public class TraceContextFormat extends TextFormat {
45   private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build();
46   @VisibleForTesting static final String TRACEPARENT = "traceparent";
47   @VisibleForTesting static final String TRACESTATE = "tracestate";
48   private static final List<String> FIELDS =
49       Collections.unmodifiableList(Arrays.asList(TRACEPARENT, TRACESTATE));
50 
51   private static final String VERSION = "00";
52   private static final int VERSION_SIZE = 2;
53   private static final char TRACEPARENT_DELIMITER = '-';
54   private static final int TRACEPARENT_DELIMITER_SIZE = 1;
55   private static final int TRACE_ID_HEX_SIZE = 2 * TraceId.SIZE;
56   private static final int SPAN_ID_HEX_SIZE = 2 * SpanId.SIZE;
57   private static final int TRACE_OPTION_HEX_SIZE = 2 * TraceOptions.SIZE;
58   private static final int TRACE_ID_OFFSET = VERSION_SIZE + TRACEPARENT_DELIMITER_SIZE;
59   private static final int SPAN_ID_OFFSET =
60       TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE;
61   private static final int TRACE_OPTION_OFFSET =
62       SPAN_ID_OFFSET + SPAN_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE;
63   private static final int TRACEPARENT_HEADER_SIZE = TRACE_OPTION_OFFSET + TRACE_OPTION_HEX_SIZE;
64   private static final int TRACESTATE_MAX_SIZE = 512;
65   private static final int TRACESTATE_MAX_MEMBERS = 32;
66   private static final char TRACESTATE_KEY_VALUE_DELIMITER = '=';
67   private static final char TRACESTATE_ENTRY_DELIMITER = ',';
68   private static final Splitter TRACESTATE_ENTRY_DELIMITER_SPLITTER =
69       Splitter.on(Pattern.compile("[ \t]*" + TRACESTATE_ENTRY_DELIMITER + "[ \t]*"));
70 
71   @Override
fields()72   public List<String> fields() {
73     return FIELDS;
74   }
75 
76   @Override
inject( SpanContext spanContext, C carrier, Setter<C> setter)77   public <C /*>>> extends @NonNull Object*/> void inject(
78       SpanContext spanContext, C carrier, Setter<C> setter) {
79     checkNotNull(spanContext, "spanContext");
80     checkNotNull(setter, "setter");
81     checkNotNull(carrier, "carrier");
82     char[] chars = new char[TRACEPARENT_HEADER_SIZE];
83     chars[0] = VERSION.charAt(0);
84     chars[1] = VERSION.charAt(1);
85     chars[2] = TRACEPARENT_DELIMITER;
86     spanContext.getTraceId().copyLowerBase16To(chars, TRACE_ID_OFFSET);
87     chars[SPAN_ID_OFFSET - 1] = TRACEPARENT_DELIMITER;
88     spanContext.getSpanId().copyLowerBase16To(chars, SPAN_ID_OFFSET);
89     chars[TRACE_OPTION_OFFSET - 1] = TRACEPARENT_DELIMITER;
90     spanContext.getTraceOptions().copyLowerBase16To(chars, TRACE_OPTION_OFFSET);
91     setter.put(carrier, TRACEPARENT, new String(chars));
92     List<Tracestate.Entry> entries = spanContext.getTracestate().getEntries();
93     if (entries.isEmpty()) {
94       // No need to add an empty "tracestate" header.
95       return;
96     }
97     StringBuilder stringBuilder = new StringBuilder(TRACESTATE_MAX_SIZE);
98     for (Tracestate.Entry entry : entries) {
99       if (stringBuilder.length() != 0) {
100         stringBuilder.append(TRACESTATE_ENTRY_DELIMITER);
101       }
102       stringBuilder
103           .append(entry.getKey())
104           .append(TRACESTATE_KEY_VALUE_DELIMITER)
105           .append(entry.getValue());
106     }
107     setter.put(carrier, TRACESTATE, stringBuilder.toString());
108   }
109 
110   @Override
extract(C carrier, Getter<C> getter)111   public <C /*>>> extends @NonNull Object*/> SpanContext extract(C carrier, Getter<C> getter)
112       throws SpanContextParseException {
113     checkNotNull(carrier, "carrier");
114     checkNotNull(getter, "getter");
115     TraceId traceId;
116     SpanId spanId;
117     TraceOptions traceOptions;
118     String traceparent = getter.get(carrier, TRACEPARENT);
119     if (traceparent == null) {
120       throw new SpanContextParseException("Traceparent not present");
121     }
122     try {
123       // TODO(bdrutu): Do we need to verify that version is hex and that for the version
124       // the length is the expected one?
125       checkArgument(
126           traceparent.charAt(TRACE_OPTION_OFFSET - 1) == TRACEPARENT_DELIMITER
127               && (traceparent.length() == TRACEPARENT_HEADER_SIZE
128                   || (traceparent.length() > TRACEPARENT_HEADER_SIZE
129                       && traceparent.charAt(TRACEPARENT_HEADER_SIZE) == TRACEPARENT_DELIMITER))
130               && traceparent.charAt(SPAN_ID_OFFSET - 1) == TRACEPARENT_DELIMITER
131               && traceparent.charAt(TRACE_OPTION_OFFSET - 1) == TRACEPARENT_DELIMITER,
132           "Missing or malformed TRACEPARENT.");
133 
134       traceId = TraceId.fromLowerBase16(traceparent, TRACE_ID_OFFSET);
135       spanId = SpanId.fromLowerBase16(traceparent, SPAN_ID_OFFSET);
136       traceOptions = TraceOptions.fromLowerBase16(traceparent, TRACE_OPTION_OFFSET);
137     } catch (IllegalArgumentException e) {
138       throw new SpanContextParseException("Invalid traceparent: " + traceparent, e);
139     }
140 
141     String tracestate = getter.get(carrier, TRACESTATE);
142     try {
143       if (tracestate == null || tracestate.isEmpty()) {
144         return SpanContext.create(traceId, spanId, traceOptions, TRACESTATE_DEFAULT);
145       }
146       Tracestate.Builder tracestateBuilder = Tracestate.builder();
147       List<String> listMembers = TRACESTATE_ENTRY_DELIMITER_SPLITTER.splitToList(tracestate);
148       checkArgument(
149           listMembers.size() <= TRACESTATE_MAX_MEMBERS, "Tracestate has too many elements.");
150       // Iterate in reverse order because when call builder set the elements is added in the
151       // front of the list.
152       for (int i = listMembers.size() - 1; i >= 0; i--) {
153         String listMember = listMembers.get(i);
154         int index = listMember.indexOf(TRACESTATE_KEY_VALUE_DELIMITER);
155         checkArgument(index != -1, "Invalid tracestate list-member format.");
156         tracestateBuilder.set(
157             listMember.substring(0, index), listMember.substring(index + 1, listMember.length()));
158       }
159       return SpanContext.create(traceId, spanId, traceOptions, tracestateBuilder.build());
160     } catch (IllegalArgumentException e) {
161       throw new SpanContextParseException("Invalid tracestate: " + tracestate, e);
162     }
163   }
164 }
165