Home | History | Annotate | Download | only in trace
      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