1 /* 2 * Copyright 2017, 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.checkNotNull; 20 21 import com.google.common.annotations.VisibleForTesting; 22 import io.opencensus.trace.SpanContext; 23 import io.opencensus.trace.SpanId; 24 import io.opencensus.trace.TraceId; 25 import io.opencensus.trace.TraceOptions; 26 import io.opencensus.trace.Tracestate; 27 import io.opencensus.trace.propagation.SpanContextParseException; 28 import io.opencensus.trace.propagation.TextFormat; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 33 /*>>> 34 import org.checkerframework.checker.nullness.qual.NonNull; 35 */ 36 37 /** 38 * Implementation of the B3 propagation protocol. See <a 39 * href=https://github.com/openzipkin/b3-propagation>b3-propagation</a>. 40 */ 41 final class B3Format extends TextFormat { 42 private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build(); 43 @VisibleForTesting static final String X_B3_TRACE_ID = "X-B3-TraceId"; 44 @VisibleForTesting static final String X_B3_SPAN_ID = "X-B3-SpanId"; 45 @VisibleForTesting static final String X_B3_PARENT_SPAN_ID = "X-B3-ParentSpanId"; 46 @VisibleForTesting static final String X_B3_SAMPLED = "X-B3-Sampled"; 47 @VisibleForTesting static final String X_B3_FLAGS = "X-B3-Flags"; 48 private static final List<String> FIELDS = 49 Collections.unmodifiableList( 50 Arrays.asList( 51 X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS)); 52 53 // Used as the upper TraceId.SIZE hex characters of the traceID. B3-propagation used to send 54 // TraceId.SIZE hex characters (8-bytes traceId) in the past. 55 private static final String UPPER_TRACE_ID = "0000000000000000"; 56 // Sampled value via the X_B3_SAMPLED header. 57 private static final String SAMPLED_VALUE = "1"; 58 // "Debug" sampled value. 59 private static final String FLAGS_VALUE = "1"; 60 61 @Override fields()62 public List<String> fields() { 63 return FIELDS; 64 } 65 66 @Override inject( SpanContext spanContext, C carrier, Setter<C> setter)67 public <C /*>>> extends @NonNull Object*/> void inject( 68 SpanContext spanContext, C carrier, Setter<C> setter) { 69 checkNotNull(spanContext, "spanContext"); 70 checkNotNull(setter, "setter"); 71 checkNotNull(carrier, "carrier"); 72 setter.put(carrier, X_B3_TRACE_ID, spanContext.getTraceId().toLowerBase16()); 73 setter.put(carrier, X_B3_SPAN_ID, spanContext.getSpanId().toLowerBase16()); 74 if (spanContext.getTraceOptions().isSampled()) { 75 setter.put(carrier, X_B3_SAMPLED, SAMPLED_VALUE); 76 } 77 } 78 79 @Override extract(C carrier, Getter<C> getter)80 public <C /*>>> extends @NonNull Object*/> SpanContext extract(C carrier, Getter<C> getter) 81 throws SpanContextParseException { 82 checkNotNull(carrier, "carrier"); 83 checkNotNull(getter, "getter"); 84 try { 85 TraceId traceId; 86 String traceIdStr = getter.get(carrier, X_B3_TRACE_ID); 87 if (traceIdStr != null) { 88 if (traceIdStr.length() == TraceId.SIZE) { 89 // This is an 8-byte traceID. 90 traceIdStr = UPPER_TRACE_ID + traceIdStr; 91 } 92 traceId = TraceId.fromLowerBase16(traceIdStr); 93 } else { 94 throw new SpanContextParseException("Missing X_B3_TRACE_ID."); 95 } 96 SpanId spanId; 97 String spanIdStr = getter.get(carrier, X_B3_SPAN_ID); 98 if (spanIdStr != null) { 99 spanId = SpanId.fromLowerBase16(spanIdStr); 100 } else { 101 throw new SpanContextParseException("Missing X_B3_SPAN_ID."); 102 } 103 TraceOptions traceOptions = TraceOptions.DEFAULT; 104 if (SAMPLED_VALUE.equals(getter.get(carrier, X_B3_SAMPLED)) 105 || FLAGS_VALUE.equals(getter.get(carrier, X_B3_FLAGS))) { 106 traceOptions = TraceOptions.builder().setIsSampled(true).build(); 107 } 108 return SpanContext.create(traceId, spanId, traceOptions, TRACESTATE_DEFAULT); 109 } catch (IllegalArgumentException e) { 110 throw new SpanContextParseException("Invalid input.", e); 111 } 112 } 113 } 114