package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
import java.util.HashMap;
import java.util.Map;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.tag.Tags;
import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
/**
* Implementation of the opentracing.io {@link Span}. This implementation is not thread safe, as
* anyway one span should be bounded to one thread.
* <p>
* <b>Limitations:</b> This span implementation is ignoring the calls to the <code>log</code>
* methods as we currently don't support displaying of log events in inspectIT.
*
* @author Ivan Senic
*
*/
public class SpanImpl implements Span {
/**
* Tracer.
*/
private final TracerImpl tracer;
/**
* Operation name as per io.tracing specification.
*/
private String operationName;
/**
* {@link SpanContext} of this span.
*/
private SpanContextImpl spanContext;
/**
* Start time of the span. Microseconds since epoch.
*/
private long startTimeMicros;
/**
* Start time nanos of the span. Can be used for more precise duration calculation.
*/
private long startTimeNanos;
/**
* Duration of the span in microseconds. We keep the span duration as double to that we can keep
* the nano fraction when possible.
*/
private double duration;
/**
* If the span should be reported to the {@link Reporter}.
*/
private boolean report = true;
/**
* Tags of this span. We save all tag values as string.
*/
private Map<String, String> tags;
/**
* Default constructor. Sets only the tracer.
*
* @param tracer
* Tracer.
*/
public SpanImpl(TracerImpl tracer) {
this.tracer = tracer;
}
/**
* {@inheritDoc}
*/
@Override
public SpanContextImpl context() {
return spanContext;
}
/**
* Internal start method with start time in microseconds and nano ticks.
*
* @param startTimeMicros
* Start time in microseconds. Must not be 0 or negative.
* @param startTimeNanos
* Current nano ticks. In case this value is not zero duration will be calculated
* using nano time.
*/
void start(long startTimeMicros, long startTimeNanos) {
if (startTimeMicros <= 0) {
throw new IllegalArgumentException("Start time in microseconds must be provided.");
}
this.startTimeMicros = startTimeMicros;
this.startTimeNanos = startTimeNanos;
tracer.spanStarted(this);
}
/**
* {@inheritDoc}
*/
@Override
public void finish() {
if (startTimeNanos != 0) {
finishWithNanos(tracer.getTimer().getCurrentNanoTime());
} else {
finish(tracer.getTimer().getCurrentTimeMicroseconds());
}
}
/**
* {@inheritDoc}
*/
@Override
public void finish(long finishMicros) {
duration = finishMicros - startTimeMicros;
tracer.spanEnded(this);
}
/**
* Finishes the span with the nano duration, uses {@link #startTimeNanos} to calculate final
* duration.
*
* @param nanos
* Current nano time..
*/
private void finishWithNanos(long nanos) {
duration = (nanos - startTimeNanos) / 1000.d;
tracer.spanEnded(this);
}
/**
* {@inheritDoc}
* <p>
* Delegates call to {@link #finish()}.
*/
@Override
public void close() {
finish();
}
/**
* {@inheritDoc}
*/
@Override
public Span setTag(String key, String value) {
return setTagInternal(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public Span setTag(String key, boolean value) {
return setTagInternal(key, String.valueOf(value));
}
/**
* {@inheritDoc}
*/
@Override
public Span setTag(String key, Number value) {
return setTagInternal(key, value.toString());
}
/**
* Internal method for span adding.
*
* @param key
* Span key
* @param value
* Span value
* @return This object
*/
private Span setTagInternal(String key, String value) {
if (null == tags) {
tags = new HashMap<String, String>(1, 1f);
}
tags.put(key, value);
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*/
@Override
public Span log(Map<String, ?> fields) {
// ignored
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*/
@Override
public Span log(long timestampMicroseconds, Map<String, ?> fields) {
// ignored
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*/
@Override
public Span log(String event) {
// ignored
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*/
@Override
public Span log(long timestampMicroseconds, String event) {
// ignored
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*
* @deprecated use {@link #log(Map)}
*/
@Deprecated
@Override
public Span log(String eventName, Object payload) {
// ignored
return this;
}
/**
* {@inheritDoc}
* <p>
* Not implemented by the inspectIT SDK.
*
* @deprecated use {@link #log(Map)}
*/
@Deprecated
@Override
public Span log(long timestampMicroseconds, String eventName, Object payload) {
// ignored
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Span setBaggageItem(String key, String value) {
if (null != spanContext) {
spanContext.setBaggageItem(key, value);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public String getBaggageItem(String key) {
if (null != spanContext) {
return spanContext.getBaggageItem(key);
}
return null;
}
/**
* If span is of a type client. Only returns <code>true</code> if span is explicitly declared as
* client.
*
* @return If span is of a type client.
* @see Tags#SPAN_KIND
*/
public boolean isClient() {
if (tags != null) {
String kind = tags.get(Tags.SPAN_KIND.getKey());
return Tags.SPAN_KIND_CLIENT.equals(kind);
}
return false;
}
/**
* If span is of a type server. Only returns <code>false</code> if span is explicitly declared
* as client.
*
* @return If span is of a type server.
* @see Tags#SPAN_KIND
*/
public boolean isServer() {
return !isClient();
}
/**
* Gets {@link #operationName}.
*
* @return {@link #operationName}
*/
public String getOperationName() {
return this.operationName;
}
/**
* {@inheritDoc}
*/
@Override
public Span setOperationName(String operationName) {
this.operationName = operationName;
return this;
}
/**
* Gets {@link #startTimeMicros}.
*
* @return {@link #startTimeMicros}
*/
public long getStartTimeMicros() {
return this.startTimeMicros;
}
/**
* Gets {@link #duration}.
*
* @return {@link #duration}
*/
public double getDuration() {
return this.duration;
}
/**
* Gets {@link #tags}.
*
* @return {@link #tags}
*/
public Map<String, String> getTags() {
return this.tags;
}
/**
* Sets {@link #spanContext}.
*
* @param spanContext
* New value for {@link #spanContext}
*/
void setSpanContext(SpanContextImpl spanContext) {
this.spanContext = spanContext;
}
/**
* Gets {@link #report}.
*
* @return {@link #report}
*/
public boolean isReport() {
return this.report;
}
/**
* Sets {@link #report}.
*
* @param report
* New value for {@link #report}
*/
void setReport(boolean report) {
this.report = report;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((this.spanContext == null) ? 0 : this.spanContext.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SpanImpl other = (SpanImpl) obj;
if (this.spanContext == null) {
if (other.spanContext != null) {
return false;
}
} else if (!this.spanContext.equals(other.spanContext)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "SpanImpl [spanContext=" + this.spanContext + ", operationName=" + this.operationName + ", duration=" + this.duration + ", report=" + this.report + ", tags=" + this.tags + "]";
}
}