package com.yammer.telemetry.tracing;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import java.math.BigInteger;
import java.util.logging.Logger;
public class SpanHelper {
private static final ThreadLocal<SpanContext> spanContext = new ThreadLocal<>();
static final Logger LOG = Logger.getLogger(SpanHelper.class.getName());
static Sampling sampler = Sampling.ON;
private static final IDGenerator idGenerator = new IDGenerator();
public static Sampling getSampler() {
return sampler;
}
public static void setSampler(Sampling sampler) {
SpanHelper.sampler = sampler;
}
/**
* Starts a new trace.
*
* @param name Name to be given to the root span in the trace.
* @return The root span of the newly created trace.
*/
public static Span startTrace(String name) {
return start(name, Optional.<BigInteger>absent(), Optional.<BigInteger>absent(), Optional.<BigInteger>absent(), sampler.trace() ? TraceLevel.ON : TraceLevel.OFF);
}
/**
* Starts a new span within a trace. Uses the current thread context to determine the
* trace ID and parent span ID.
*
* @param name Name to be given to the span.
* @return The newly started span.
*/
public static Span startSpan(String name) {
return start(name, Optional.<BigInteger>absent(), Optional.<BigInteger>absent(), Optional.<BigInteger>absent(), TraceLevel.INHERIT);
}
/**
* Attach to an existing span. This is useful when a span has been created elsewhere
* and you'd like to log annotations against that span locally. For example across thread boundaries in the local
* vm.
*
* @param traceId ID of the trace of the span being attached.
* @param spanId ID of the span being attached.
* @param name Name for the span - useful for debug
* @return The attached span.
*/
public static Span attachSpan(BigInteger traceId, BigInteger spanId, String name) {
return start(name, Optional.of(traceId), Optional.of(spanId), Optional.<BigInteger>absent(), TraceLevel.ON);
}
/**
* Starts a new span under the specified trace and parent spanId.
*
* This is used for attaching to an external span for example with an incoming http request.
*
* @param traceId - the current traceId
* @param parentSpanId - the parent span, if available
* @param name - the name for this span
* @return the newly created span
*/
public static Span startSpan(BigInteger traceId, BigInteger parentSpanId, String name) {
return start(name, Optional.of(traceId), Optional.<BigInteger>absent(), Optional.of(parentSpanId), TraceLevel.ON);
}
public static Optional<Span> currentSpan() {
SpanContext context = spanContext.get();
if (context == null) {
return Optional.absent();
} else {
return context.currentSpan();
}
}
private static Span start(String name, Optional<BigInteger> traceId, Optional<BigInteger> spanId, Optional<BigInteger> parentSpanId, TraceLevel traceLevel) {
SpanContext context = spanContext.get();
if (context == null) {
context = new SpanContext();
spanContext.set(context);
}
if (traceLevel == TraceLevel.INHERIT) {
traceLevel = context.currentTraceLevel();
}
if (!traceId.isPresent()) {
traceId = context.currentTraceId();
if (!traceId.isPresent()) {
traceId = Optional.of(idGenerator.generateTraceId());
}
}
if (!parentSpanId.isPresent()) {
parentSpanId = context.currentSpanId();
}
if (!spanId.isPresent()) {
spanId = Optional.of(idGenerator.generateSpanId());
}
final Span span = (traceLevel == TraceLevel.OFF) ?
new DisabledSpan() :
new EnabledSpan(traceId.get(), spanId.get(), parentSpanId, name, traceLevel);
context.startSpan(span);
return span;
}
static long nowInNanoseconds() {
return System.currentTimeMillis() * 1000000;
}
static Optional<SpanContext> currentContext() {
return Optional.fromNullable(spanContext.get());
}
/**
* This is available for testing to allow checking before and after states are as expected on the span context.
*
* @return an immutable list of the current state of spans in the thread local context.
*/
static ImmutableList<Span> captureSpans() {
SpanContext context = spanContext.get();
if (context != null) {
return context.captureSpans();
}
return ImmutableList.of();
}
}