blob: 5565e9de7d13fef8402f636a961cde4c28cf10a0 [file] [log] [blame]
/*
* Copyright 2017, OpenCensus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opencensus.implcore.trace;
import static com.google.common.base.Preconditions.checkNotNull;
import io.opencensus.common.Clock;
import io.opencensus.implcore.internal.TimestampConverter;
import io.opencensus.implcore.trace.internal.RandomHandler;
import io.opencensus.trace.Link;
import io.opencensus.trace.Link.Type;
import io.opencensus.trace.Sampler;
import io.opencensus.trace.Span;
import io.opencensus.trace.Span.Kind;
import io.opencensus.trace.SpanBuilder;
import io.opencensus.trace.SpanContext;
import io.opencensus.trace.SpanId;
import io.opencensus.trace.TraceId;
import io.opencensus.trace.TraceOptions;
import io.opencensus.trace.Tracestate;
import io.opencensus.trace.config.TraceConfig;
import io.opencensus.trace.config.TraceParams;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.annotation.Nullable;
/** Implementation of the {@link SpanBuilder}. */
final class SpanBuilderImpl extends SpanBuilder {
private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build();
private static final TraceOptions SAMPLED_TRACE_OPTIONS =
TraceOptions.builder().setIsSampled(true).build();
private static final TraceOptions NOT_SAMPLED_TRACE_OPTIONS =
TraceOptions.builder().setIsSampled(false).build();
private final Options options;
private final String name;
@Nullable private final Span parent;
@Nullable private final SpanContext remoteParentSpanContext;
@Nullable private Sampler sampler;
private List<Span> parentLinks = Collections.<Span>emptyList();
@Nullable private Boolean recordEvents;
@Nullable private Kind kind;
private Span startSpanInternal(
@Nullable SpanContext parent,
@Nullable Boolean hasRemoteParent,
String name,
@Nullable Sampler sampler,
List<Span> parentLinks,
@Nullable Boolean recordEvents,
@Nullable Kind kind,
@Nullable TimestampConverter timestampConverter) {
TraceParams activeTraceParams = options.traceConfig.getActiveTraceParams();
Random random = options.randomHandler.current();
TraceId traceId;
SpanId spanId = SpanId.generateRandomId(random);
SpanId parentSpanId = null;
// TODO(bdrutu): Handle tracestate correctly not just propagate.
Tracestate tracestate = TRACESTATE_DEFAULT;
if (parent == null || !parent.isValid()) {
// New root span.
traceId = TraceId.generateRandomId(random);
// This is a root span so no remote or local parent.
hasRemoteParent = null;
} else {
// New child span.
traceId = parent.getTraceId();
parentSpanId = parent.getSpanId();
tracestate = parent.getTracestate();
}
TraceOptions traceOptions =
makeSamplingDecision(
parent,
hasRemoteParent,
name,
sampler,
parentLinks,
traceId,
spanId,
activeTraceParams)
? SAMPLED_TRACE_OPTIONS
: NOT_SAMPLED_TRACE_OPTIONS;
Span span =
(traceOptions.isSampled() || Boolean.TRUE.equals(recordEvents))
? RecordEventsSpanImpl.startSpan(
SpanContext.create(traceId, spanId, traceOptions, tracestate),
name,
kind,
parentSpanId,
hasRemoteParent,
activeTraceParams,
options.startEndHandler,
timestampConverter,
options.clock)
: NoRecordEventsSpanImpl.create(
SpanContext.create(traceId, spanId, traceOptions, tracestate));
linkSpans(span, parentLinks);
return span;
}
private static boolean makeSamplingDecision(
@Nullable SpanContext parent,
@Nullable Boolean hasRemoteParent,
String name,
@Nullable Sampler sampler,
List<Span> parentLinks,
TraceId traceId,
SpanId spanId,
TraceParams activeTraceParams) {
// If users set a specific sampler in the SpanBuilder, use it.
if (sampler != null) {
return sampler.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks);
}
// Use the default sampler if this is a root Span or this is an entry point Span (has remote
// parent).
if (Boolean.TRUE.equals(hasRemoteParent) || parent == null || !parent.isValid()) {
return activeTraceParams
.getSampler()
.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks);
}
// Parent is always different than null because otherwise we use the default sampler.
return parent.getTraceOptions().isSampled() || isAnyParentLinkSampled(parentLinks);
}
private static boolean isAnyParentLinkSampled(List<Span> parentLinks) {
for (Span parentLink : parentLinks) {
if (parentLink.getContext().getTraceOptions().isSampled()) {
return true;
}
}
return false;
}
private static void linkSpans(Span span, List<Span> parentLinks) {
if (!parentLinks.isEmpty()) {
Link childLink = Link.fromSpanContext(span.getContext(), Type.CHILD_LINKED_SPAN);
for (Span linkedSpan : parentLinks) {
linkedSpan.addLink(childLink);
span.addLink(Link.fromSpanContext(linkedSpan.getContext(), Type.PARENT_LINKED_SPAN));
}
}
}
static SpanBuilderImpl createWithParent(String spanName, @Nullable Span parent, Options options) {
return new SpanBuilderImpl(spanName, null, parent, options);
}
static SpanBuilderImpl createWithRemoteParent(
String spanName, @Nullable SpanContext remoteParentSpanContext, Options options) {
return new SpanBuilderImpl(spanName, remoteParentSpanContext, null, options);
}
private SpanBuilderImpl(
String name,
@Nullable SpanContext remoteParentSpanContext,
@Nullable Span parent,
Options options) {
this.name = checkNotNull(name, "name");
this.parent = parent;
this.remoteParentSpanContext = remoteParentSpanContext;
this.options = options;
}
@Override
public Span startSpan() {
SpanContext parentContext = remoteParentSpanContext;
Boolean hasRemoteParent = Boolean.TRUE;
TimestampConverter timestampConverter = null;
if (remoteParentSpanContext == null) {
// This is not a child of a remote Span. Get the parent SpanContext from the parent Span if
// any.
Span parent = this.parent;
hasRemoteParent = Boolean.FALSE;
if (parent != null) {
parentContext = parent.getContext();
// Pass the timestamp converter from the parent to ensure that the recorded events are in
// the right order. Implementation uses System.nanoTime() which is monotonically increasing.
if (parent instanceof RecordEventsSpanImpl) {
timestampConverter = ((RecordEventsSpanImpl) parent).getTimestampConverter();
}
} else {
hasRemoteParent = null;
}
}
return startSpanInternal(
parentContext,
hasRemoteParent,
name,
sampler,
parentLinks,
recordEvents,
kind,
timestampConverter);
}
static final class Options {
private final RandomHandler randomHandler;
private final RecordEventsSpanImpl.StartEndHandler startEndHandler;
private final Clock clock;
private final TraceConfig traceConfig;
Options(
RandomHandler randomHandler,
RecordEventsSpanImpl.StartEndHandler startEndHandler,
Clock clock,
TraceConfig traceConfig) {
this.randomHandler = checkNotNull(randomHandler, "randomHandler");
this.startEndHandler = checkNotNull(startEndHandler, "startEndHandler");
this.clock = checkNotNull(clock, "clock");
this.traceConfig = checkNotNull(traceConfig, "traceConfig");
}
}
@Override
public SpanBuilderImpl setSampler(Sampler sampler) {
this.sampler = checkNotNull(sampler, "sampler");
return this;
}
@Override
public SpanBuilderImpl setParentLinks(List<Span> parentLinks) {
this.parentLinks = checkNotNull(parentLinks, "parentLinks");
return this;
}
@Override
public SpanBuilderImpl setRecordEvents(boolean recordEvents) {
this.recordEvents = recordEvents;
return this;
}
@Override
public SpanBuilderImpl setSpanKind(@Nullable Kind kind) {
this.kind = kind;
return this;
}
}