package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import io.opentracing.References;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer.SpanBuilder;
import rocks.inspectit.agent.java.sdk.opentracing.Timer;
/**
* Builder for the span. Note that builder is delegating the calls to the span that's created
* immediately on the builder initialization. This way we save the copying of the data to the span
* later on.
* <p>
* <b>Limitations:</b> This span implementation is saving only one (first) referenced context as the
* parent. For any additional referenced contexts only the baggage propagation will be done.
*
* @author Ivan Senic
*
*/
public class SpanBuilderImpl implements SpanBuilder {
/**
* Span being created.
*/
private final SpanImpl span;
/**
* Timer to use if startTimestamp is not provided in the builder.
*/
private final Timer timer;
/**
* Collected baggage from all parents.
*/
private final Map<String, String> baggage = new HashMap<String, String>();
/**
* Manually specified timestamp.
*/
private long startTimestamp;
/**
* Parent context. Can be <code>null</code> to denote the new span.
*/
private SpanContextImpl parent;
/**
* Reference type to the {@link #parent} context.
*/
private String referenceType;
/**
* Creates new span builder.
*
* @param tracer
* {@link TracerImpl}
* @param operationName
* Operation name.
*/
public SpanBuilderImpl(TracerImpl tracer, String operationName) {
this.timer = tracer.getTimer();
this.span = new SpanImpl(tracer);
this.span.setOperationName(operationName);
}
/**
* {@inheritDoc}
*/
@Override
public Iterable<Entry<String, String>> baggageItems() {
return baggage.entrySet();
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl asChildOf(SpanContext parent) {
if (null != parent) {
addReference(References.CHILD_OF, parent);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl asChildOf(Span parent) {
if (null != parent) {
addReference(References.CHILD_OF, parent.context());
}
return this;
}
/**
* {@inheritDoc}
* <p>
* If the {@link #parent} is not set this method will set the passed context as the main
* referenced context. All the baggage from the context will be passed no matter if the
* {@link #parent} is already set or not.
*/
@Override
public SpanBuilderImpl addReference(String referenceType, SpanContext referencedContext) {
if (References.CHILD_OF.equals(referenceType) || References.FOLLOWS_FROM.equals(referenceType)) {
// we will set the main parent only if it's not set already
if ((null == parent) && (referencedContext instanceof SpanContextImpl)) {
this.parent = (SpanContextImpl) referencedContext;
this.referenceType = referenceType;
}
}
// for now we only directly reference one parent, but collect baggage from all
// reference contexts
withBaggageFrom(referencedContext);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl withTag(String key, String value) {
span.setTag(key, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl withTag(String key, boolean value) {
span.setTag(key, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl withTag(String key, Number value) {
span.setTag(key, value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public SpanBuilderImpl withStartTimestamp(long microseconds) {
this.startTimestamp = microseconds;
return this;
}
/**
* Explicitly states that the span created should not be reported.
*
* @return {@link SpanBuilderImpl} for connecting.
*/
public SpanBuilderImpl doNotReport() {
this.span.setReport(false);
return this;
}
/**
* Collects baggage from parent. Copied from the io.opentracing implementation.
*
* @param from
* context to copy baggage from
*/
private void withBaggageFrom(SpanContext from) {
Iterable<Entry<String, String>> baggageItems = from.baggageItems();
if ((null == baggageItems) || (null == baggageItems.iterator())) {
return;
}
for (Entry<String, String> baggageItem : baggageItems) {
baggage.put(baggageItem.getKey(), baggageItem.getValue());
}
}
/**
* {@inheritDoc}
*/
@Override
public SpanImpl start() {
// resolve context
SpanContextImpl context = SpanContextImpl.build(parent, referenceType, baggage);
span.setSpanContext(context);
// if startTimestamp was not specified in the builder, we use the timer
long nanoTime = 0;
if (startTimestamp <= 0) {
startTimestamp = timer.getCurrentTimeMicroseconds();
nanoTime = timer.getCurrentNanoTime();
}
// set start time as last operation so get more accuracy
span.start(startTimestamp, nanoTime);
return span;
}
}