All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavefront.opentracing.WavefrontSpanBuilder Maven / Gradle / Ivy

Go to download

Implements OpenTracing API for collecting and sending tracing data to Wavefront from Java applications.

The newest version!
package com.wavefront.opentracing;

import com.wavefront.sdk.common.Pair;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

import io.opentracing.References;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.tag.Tag;

/**
 * The class used for building {@link WavefrontSpan}s in accordance with the OpenTracing spec:
 *
 * https://github.com/opentracing/specification/blob/master/specification.md
 *
 * @author Vikram Raman ([email protected])
 */
@NotThreadSafe
public class WavefrontSpanBuilder implements Tracer.SpanBuilder {

  /** The tracer to report spans to. */
  private final WavefrontTracer tracer;

  /** The operation name. Required for every span per opentracing spec. */
  private final String operationName;

  /** The list of parent references. */
  private List parents = null;

  /** The list of follows from references. */
  private List follows = null;

  private long startTimeMicros;
  private boolean ignoreActiveSpan = false;

  private boolean useFullSpanId = true;
  private final List> tags = new ArrayList<>();

  public WavefrontSpanBuilder(String operationName, WavefrontTracer tracer) {
    this.operationName = operationName;
    this.tracer = tracer;
  }

  @Override
  public Tracer.SpanBuilder asChildOf(SpanContext parentContext) {
    return addReference(References.CHILD_OF, parentContext);
  }

  @Override
  public Tracer.SpanBuilder asChildOf(Span parent) {
    return addReference(References.CHILD_OF, parent == null ? null : parent.context());
  }

  @Override
  public Tracer.SpanBuilder addReference(String type, SpanContext spanContext) {
    if (!(spanContext instanceof WavefrontSpanContext) ||
        (!References.CHILD_OF.equals(type) && !References.FOLLOWS_FROM.equals(type))) {
      return this;
    }
    Reference ref = new Reference((WavefrontSpanContext) spanContext, type);
    if (References.CHILD_OF.equals(type)) {
      if (parents == null) {
        parents = new ArrayList<>(1);
      }
      parents.add(ref);
    } else if (References.FOLLOWS_FROM.equals(type)) {
      if (follows == null) {
        follows = new ArrayList<>(1);
      }
      follows.add(ref);
    }
    return this;
  }

  @Override
  public Tracer.SpanBuilder ignoreActiveSpan() {
    this.ignoreActiveSpan = true;
    return this;
  }

  @Override
  public Tracer.SpanBuilder withTag(String key, String value) {
    return setTagObject(key, value);
  }

  @Override
  public Tracer.SpanBuilder withTag(String key, boolean value) {
    return setTagObject(key, value);
  }

  @Override
  public Tracer.SpanBuilder withTag(String key, Number value) {
    return setTagObject(key, value);
  }

  @Override
  public  Tracer.SpanBuilder withTag(Tag tag, T value) {
    return setTagObject(tag.getKey(), value);
  }

  private Tracer.SpanBuilder setTagObject(String key, Object value) {
    if (key != null && !key.isEmpty() && value != null) {
      tags.add(Pair.of(key, value.toString()));
    }
    return this;
  }

  @Override
  public Tracer.SpanBuilder withStartTimestamp(long startMicros) {
    this.startTimeMicros = startMicros;
    return this;
  }

  @Override
  public Span start() {
    long startTimeNanos = 0;
    if (startTimeMicros == 0) {
      startTimeMicros = tracer.currentTimeMicros();
      startTimeNanos = System.nanoTime();
    }
    WavefrontSpanContext ctx = createSpanContext();
    if (!ctx.isSampled()) {
      // this indicates a root span and that no decision has been inherited from a parent span.
      // perform head based sampling as no sampling decision has been obtained for this span yet.
      boolean decision = tracer.sample(operationName, ctx.getTraceId().getLeastSignificantBits(), 0);
      ctx = ctx.withSamplingDecision(decision);
    }
    return new WavefrontSpan(tracer, operationName, ctx, startTimeMicros, startTimeNanos, parents,
        follows, tags);
  }

  private WavefrontSpanContext createSpanContext() {
    UUID spanId = generateSpanId();
    WavefrontSpanContext traceCtx = traceAncestry();
    UUID traceId = (traceCtx == null) ? UUID.randomUUID() : traceCtx.getTraceId();
    Boolean sampling = (traceCtx == null) ? null : traceCtx.getSamplingDecision();
    return new WavefrontSpanContext(traceId, spanId, getBaggage(), sampling);
  }

  @Nullable
  private Map getBaggage() {
    return addItems(follows, addItems(parents, null));
  }

  /**
   * Gets a map containing baggage items of all the given references.
   *
   * @param references the list of references to process
   * @param baggage the map to add items to, can be null in which case a new map is created/returned
   * @return the map containing baggage items from all references
   */
  @Nullable
  private Map addItems(List references,
                                       @Nullable Map baggage) {
    if (references != null && !references.isEmpty()) {
      for (Reference ref : references) {
        Map refBaggage = ref.getSpanContext().getBaggage();
        if (refBaggage != null && !refBaggage.isEmpty()) {
          if (baggage == null) {
            baggage = new HashMap<>();
          }
          baggage.putAll(refBaggage);
        }
      }
    }
    return baggage;
  }

  @Nullable
  private WavefrontSpanContext traceAncestry() {
    if (parents != null && !parents.isEmpty()) {
      // prefer child_of relationship for assigning traceId
      return parents.get(0).getSpanContext();
    }
    if (follows != null && !follows.isEmpty()) {
      return follows.get(0).getSpanContext();
    }

    // use active span as parent if ignoreActiveSpan is false
    Span parentSpan = !ignoreActiveSpan ? tracer.activeSpan() : null;
    if (parentSpan != null) {
      asChildOf(parentSpan);
    }

    // root span if parentSpan is null
    return parentSpan == null ? null : ((WavefrontSpanContext) parentSpan.context());
  }

  private UUID generateSpanId() {
    if (tracer.isUseSpanId128Bit()) return UUID.randomUUID();

    // Generate a span id that can be represented in 64 bits, allowing lossless conversion and
    // propagation to systems such as Zipkin and OpenTelemetry (which specify 64-bit span ids)
    long id;
    do {
      id = ThreadLocalRandom.current().nextLong();
    } while (id == 0);
    return new UUID(0L, id);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy