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; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import io.opencensus.common.Clock; 22 import io.opencensus.implcore.internal.TimestampConverter; 23 import io.opencensus.implcore.trace.internal.RandomHandler; 24 import io.opencensus.trace.Link; 25 import io.opencensus.trace.Link.Type; 26 import io.opencensus.trace.Sampler; 27 import io.opencensus.trace.Span; 28 import io.opencensus.trace.Span.Kind; 29 import io.opencensus.trace.SpanBuilder; 30 import io.opencensus.trace.SpanContext; 31 import io.opencensus.trace.SpanId; 32 import io.opencensus.trace.TraceId; 33 import io.opencensus.trace.TraceOptions; 34 import io.opencensus.trace.Tracestate; 35 import io.opencensus.trace.config.TraceConfig; 36 import io.opencensus.trace.config.TraceParams; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.Random; 40 import javax.annotation.Nullable; 41 42 /** Implementation of the {@link SpanBuilder}. */ 43 final class SpanBuilderImpl extends SpanBuilder { 44 private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build(); 45 46 private static final TraceOptions SAMPLED_TRACE_OPTIONS = 47 TraceOptions.builder().setIsSampled(true).build(); 48 private static final TraceOptions NOT_SAMPLED_TRACE_OPTIONS = 49 TraceOptions.builder().setIsSampled(false).build(); 50 51 private final Options options; 52 private final String name; 53 @Nullable private final Span parent; 54 @Nullable private final SpanContext remoteParentSpanContext; 55 @Nullable private Sampler sampler; 56 private List<Span> parentLinks = Collections.<Span>emptyList(); 57 @Nullable private Boolean recordEvents; 58 @Nullable private Kind kind; 59 60 private Span startSpanInternal( 61 @Nullable SpanContext parent, 62 @Nullable Boolean hasRemoteParent, 63 String name, 64 @Nullable Sampler sampler, 65 List<Span> parentLinks, 66 @Nullable Boolean recordEvents, 67 @Nullable Kind kind, 68 @Nullable TimestampConverter timestampConverter) { 69 TraceParams activeTraceParams = options.traceConfig.getActiveTraceParams(); 70 Random random = options.randomHandler.current(); 71 TraceId traceId; 72 SpanId spanId = SpanId.generateRandomId(random); 73 SpanId parentSpanId = null; 74 // TODO(bdrutu): Handle tracestate correctly not just propagate. 75 Tracestate tracestate = TRACESTATE_DEFAULT; 76 if (parent == null || !parent.isValid()) { 77 // New root span. 78 traceId = TraceId.generateRandomId(random); 79 // This is a root span so no remote or local parent. 80 hasRemoteParent = null; 81 } else { 82 // New child span. 83 traceId = parent.getTraceId(); 84 parentSpanId = parent.getSpanId(); 85 tracestate = parent.getTracestate(); 86 } 87 TraceOptions traceOptions = 88 makeSamplingDecision( 89 parent, 90 hasRemoteParent, 91 name, 92 sampler, 93 parentLinks, 94 traceId, 95 spanId, 96 activeTraceParams) 97 ? SAMPLED_TRACE_OPTIONS 98 : NOT_SAMPLED_TRACE_OPTIONS; 99 Span span = 100 (traceOptions.isSampled() || Boolean.TRUE.equals(recordEvents)) 101 ? RecordEventsSpanImpl.startSpan( 102 SpanContext.create(traceId, spanId, traceOptions, tracestate), 103 name, 104 kind, 105 parentSpanId, 106 hasRemoteParent, 107 activeTraceParams, 108 options.startEndHandler, 109 timestampConverter, 110 options.clock) 111 : NoRecordEventsSpanImpl.create( 112 SpanContext.create(traceId, spanId, traceOptions, tracestate)); 113 linkSpans(span, parentLinks); 114 return span; 115 } 116 117 private static boolean makeSamplingDecision( 118 @Nullable SpanContext parent, 119 @Nullable Boolean hasRemoteParent, 120 String name, 121 @Nullable Sampler sampler, 122 List<Span> parentLinks, 123 TraceId traceId, 124 SpanId spanId, 125 TraceParams activeTraceParams) { 126 // If users set a specific sampler in the SpanBuilder, use it. 127 if (sampler != null) { 128 return sampler.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks); 129 } 130 // Use the default sampler if this is a root Span or this is an entry point Span (has remote 131 // parent). 132 if (Boolean.TRUE.equals(hasRemoteParent) || parent == null || !parent.isValid()) { 133 return activeTraceParams 134 .getSampler() 135 .shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks); 136 } 137 // Parent is always different than null because otherwise we use the default sampler. 138 return parent.getTraceOptions().isSampled() || isAnyParentLinkSampled(parentLinks); 139 } 140 141 private static boolean isAnyParentLinkSampled(List<Span> parentLinks) { 142 for (Span parentLink : parentLinks) { 143 if (parentLink.getContext().getTraceOptions().isSampled()) { 144 return true; 145 } 146 } 147 return false; 148 } 149 150 private static void linkSpans(Span span, List<Span> parentLinks) { 151 if (!parentLinks.isEmpty()) { 152 Link childLink = Link.fromSpanContext(span.getContext(), Type.CHILD_LINKED_SPAN); 153 for (Span linkedSpan : parentLinks) { 154 linkedSpan.addLink(childLink); 155 span.addLink(Link.fromSpanContext(linkedSpan.getContext(), Type.PARENT_LINKED_SPAN)); 156 } 157 } 158 } 159 160 static SpanBuilderImpl createWithParent(String spanName, @Nullable Span parent, Options options) { 161 return new SpanBuilderImpl(spanName, null, parent, options); 162 } 163 164 static SpanBuilderImpl createWithRemoteParent( 165 String spanName, @Nullable SpanContext remoteParentSpanContext, Options options) { 166 return new SpanBuilderImpl(spanName, remoteParentSpanContext, null, options); 167 } 168 169 private SpanBuilderImpl( 170 String name, 171 @Nullable SpanContext remoteParentSpanContext, 172 @Nullable Span parent, 173 Options options) { 174 this.name = checkNotNull(name, "name"); 175 this.parent = parent; 176 this.remoteParentSpanContext = remoteParentSpanContext; 177 this.options = options; 178 } 179 180 @Override 181 public Span startSpan() { 182 SpanContext parentContext = remoteParentSpanContext; 183 Boolean hasRemoteParent = Boolean.TRUE; 184 TimestampConverter timestampConverter = null; 185 if (remoteParentSpanContext == null) { 186 // This is not a child of a remote Span. Get the parent SpanContext from the parent Span if 187 // any. 188 Span parent = this.parent; 189 hasRemoteParent = Boolean.FALSE; 190 if (parent != null) { 191 parentContext = parent.getContext(); 192 // Pass the timestamp converter from the parent to ensure that the recorded events are in 193 // the right order. Implementation uses System.nanoTime() which is monotonically increasing. 194 if (parent instanceof RecordEventsSpanImpl) { 195 timestampConverter = ((RecordEventsSpanImpl) parent).getTimestampConverter(); 196 } 197 } else { 198 hasRemoteParent = null; 199 } 200 } 201 return startSpanInternal( 202 parentContext, 203 hasRemoteParent, 204 name, 205 sampler, 206 parentLinks, 207 recordEvents, 208 kind, 209 timestampConverter); 210 } 211 212 static final class Options { 213 private final RandomHandler randomHandler; 214 private final RecordEventsSpanImpl.StartEndHandler startEndHandler; 215 private final Clock clock; 216 private final TraceConfig traceConfig; 217 218 Options( 219 RandomHandler randomHandler, 220 RecordEventsSpanImpl.StartEndHandler startEndHandler, 221 Clock clock, 222 TraceConfig traceConfig) { 223 this.randomHandler = checkNotNull(randomHandler, "randomHandler"); 224 this.startEndHandler = checkNotNull(startEndHandler, "startEndHandler"); 225 this.clock = checkNotNull(clock, "clock"); 226 this.traceConfig = checkNotNull(traceConfig, "traceConfig"); 227 } 228 } 229 230 @Override 231 public SpanBuilderImpl setSampler(Sampler sampler) { 232 this.sampler = checkNotNull(sampler, "sampler"); 233 return this; 234 } 235 236 @Override 237 public SpanBuilderImpl setParentLinks(List<Span> parentLinks) { 238 this.parentLinks = checkNotNull(parentLinks, "parentLinks"); 239 return this; 240 } 241 242 @Override 243 public SpanBuilderImpl setRecordEvents(boolean recordEvents) { 244 this.recordEvents = recordEvents; 245 return this; 246 } 247 248 @Override 249 public SpanBuilderImpl setSpanKind(@Nullable Kind kind) { 250 this.kind = kind; 251 return this; 252 } 253 } 254