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

com.undefinedlabs.scope.SpanBuilder Maven / Gradle / Ivy

package com.undefinedlabs.scope;

import org.apache.commons.lang3.StringUtils;
import com.undefinedlabs.scope.statistics.Statistics;
import io.opentracing.References;
import io.opentracing.Tracer;
import io.opentracing.tag.Tag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SpanBuilder implements Tracer.SpanBuilder {

  private final ScopeTracer scopeTracer;
  private final String operationName;
  private final Map tags;
  private long startTimeMicroseconds;
  private long startTimeNanoseconds;
  private boolean ignoreActiveSpan;
  private List references;

  public SpanBuilder(final ScopeTracer scopeTracer, final String operationName) {
    this.scopeTracer = scopeTracer;
    this.operationName = StringUtils.abbreviate(operationName, 255);
    references = Collections.EMPTY_LIST;
    tags = new HashMap<>();
    ignoreActiveSpan = false;
  }

  @Override
  public Tracer.SpanBuilder asChildOf(final io.opentracing.SpanContext parent) {
    return addReference(References.CHILD_OF, parent);
  }

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

  @Override
  public Tracer.SpanBuilder addReference(
      final String referenceType, final io.opentracing.SpanContext referenced) {
    synchronized (this) {
      if (referenceType == null
          || !(referenced instanceof SpanContext)
          || (!References.CHILD_OF.equals(referenceType)
              && !References.FOLLOWS_FROM.equals(referenceType))) {
        return this;
      }

      // Optimization when there is only one parent.
      if (references.isEmpty()) {
        references =
            Collections.singletonList(new SpanReference((SpanContext) referenced, referenceType));
      } else {
        if (references.size() == 1) {
          references = new ArrayList<>(references);
        }
        references.add(new SpanReference((SpanContext) referenced, referenceType));
      }
    }

    return this;
  }

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

    return this;
  }

  @Override
  public Tracer.SpanBuilder withTag(final String key, final String value) {
    synchronized (this) {
      tags.put(key, value);
    }

    return this;
  }

  @Override
  public Tracer.SpanBuilder withTag(final String key, final boolean value) {
    synchronized (this) {
      tags.put(key, value);
    }

    return this;
  }

  @Override
  public Tracer.SpanBuilder withTag(final String key, final Number value) {
    synchronized (this) {
      tags.put(key, value);
    }

    return this;
  }

  @Override
  public  Tracer.SpanBuilder withTag(final Tag tag, final T value) {
    synchronized (this) {
      tags.put(tag.getKey(), value);
    }

    return this;
  }

  @Override
  public Tracer.SpanBuilder withStartTimestamp(final long microseconds) {
    startTimeMicroseconds = microseconds;
    startTimeNanoseconds = microseconds * 1000;
    return this;
  }

  @Override
  public io.opentracing.Span start() {
    synchronized (this) {

      // Check if active span should be established as CHILD_OF relationship
      if (!ignoreActiveSpan && references.isEmpty() && scopeTracer.activeSpan() != null) {
        asChildOf(scopeTracer.activeSpan());
      }

      // If there is no previous references, new context is created.
      final SpanContext context =
          references.isEmpty() || !references.get(0).getSpanContext().hasTrace()
              ? createNewContext()
              : createChildContext();

      boolean computeDurationViaNanoTicks = false;

      if (startTimeMicroseconds == 0) {
        startTimeMicroseconds = scopeTracer.clock().currentTimeMicros();

        if (!scopeTracer.clock().isMicrosAccurate()) {
          startTimeNanoseconds = scopeTracer.clock().currentNanoTicks();
          computeDurationViaNanoTicks = true;
        }
      }

      final Span span =
          new Span(
              scopeTracer,
              operationName,
              context,
              startTimeMicroseconds,
              startTimeNanoseconds,
              computeDurationViaNanoTicks,
              tags,
              references);

      scopeTracer.registerStartedSpan(span);
      Statistics.INSTANCE.registerStartedSpan(span);
      return span;
    }
  }

  private SpanContext createNewContext() {
    // Random long
    final TraceId spanId = TraceId.generateRandomId();
    final TraceId traceId = spanId;

    return new SpanContext(traceId, spanId, null, getBaggage());
  }

  private SpanContext createChildContext() {
    final SpanReference preferredReference = preferredReference();
    final TraceId parentTraceId = preferredReference.getSpanContext().getTraceId();
    final TraceId spanId = TraceId.generateRandomId();
    final TraceId parentSpanId = preferredReference.getSpanContext().getSpanId();

    return new SpanContext(parentTraceId, spanId, parentSpanId, getBaggage());
  }

  private SpanReference preferredReference() {
    final SpanReference preferredReference = references.get(0);

    for (final SpanReference reference : references) {
      if (References.CHILD_OF.equals(reference.getType())
          && !References.CHILD_OF.equals(preferredReference.getType())) {
        return reference;
      }
    }

    return preferredReference;
  }

  private Map getBaggage() {
    Map baggage = null;

    if (references.size() == 1) {
      return references.get(0).getSpanContext().getBaggage();
    }

    for (final SpanReference reference : references) {
      if (reference.getSpanContext().getBaggage() != null) {
        if (baggage == null) {
          baggage = new HashMap<>();
        }
        baggage.putAll(reference.getSpanContext().getBaggage());
      }
    }

    return baggage;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy