com.datadoghq.trace.DDBaseSpan Maven / Gradle / Ivy
package com.datadoghq.trace;
import com.datadoghq.trace.util.Clock;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.opentracing.BaseSpan;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class DDBaseSpan implements BaseSpan {
/** The context attached to the span */
protected final DDSpanContext context;
/** StartTime stores the creation time of the span in milliseconds */
protected long startTimeMicro;
/** StartTimeNano stores the only the nanoseconds for more accuracy */
protected long startTimeNano;
/** The duration in nanoseconds computed using the startTimeMicro and startTimeNano */
protected long durationNano;
/**
* A simple constructor. Currently, users have
*
* @param timestampMicro if set, use this time instead of the auto-generated time
* @param context the context
*/
protected DDBaseSpan(final long timestampMicro, final DDSpanContext context) {
this.context = context;
// record the start time in nano (current milli + nano delta)
if (timestampMicro == 0L) {
this.startTimeMicro = Clock.currentMicroTime();
} else {
this.startTimeMicro = timestampMicro;
}
this.startTimeNano = Clock.currentNanoTicks();
// track each span of the trace
this.context.getTrace().add(this);
}
public final void finish() {
finish(Clock.currentMicroTime());
}
public final void finish(final long stoptimeMicros) {
// Ensure that duration is at least 1. Less than 1 is possible due to our use of system clock instead of nano time.
this.durationNano =
Math.max(1, TimeUnit.MICROSECONDS.toNanos(stoptimeMicros - this.startTimeMicro));
afterFinish();
}
/**
* Close the span. If the current span is the parent, check if each child has also been closed If
* not, warned it
*/
protected final void afterFinish() {
log.debug("{} - Closing the span.", this);
// warn if one of the parent's children is not finished
if (this.isRootSpan()) {
final Queue> spans = this.context().getTrace();
for (final DDBaseSpan> span : spans) {
if (span.getDurationNano() == 0L) {
log.warn(
"{} - The parent span is marked as finished but this span isn't. You have to close each children.",
this);
}
}
this.context.getTracer().write(this.context.getTrace());
log.debug("{} - Write the trace", this);
}
}
/**
* Check if the span is the root parent. It means that the traceId is the same as the spanId
*
* @return true if root, false otherwise
*/
protected final boolean isRootSpan() {
if (context().getTrace().isEmpty()) {
return false;
}
// First item of the array AND tracer set
final DDBaseSpan> first = context().getTrace().peek();
return first.context().getSpanId() == this.context().getSpanId()
&& this.context.getTracer() != null;
}
public void setErrorMeta(final Throwable error) {
context.setErrorFlag(true);
setTag(DDTags.ERROR_MSG, error.getMessage());
setTag(DDTags.ERROR_TYPE, error.getClass().getName());
final StringWriter errorString = new StringWriter();
error.printStackTrace(new PrintWriter(errorString));
setTag(DDTags.ERROR_STACK, errorString.toString());
}
private boolean extractError(final Map map) {
if (map.get("error.object") instanceof Throwable) {
final Throwable error = (Throwable) map.get("error.object");
setErrorMeta(error);
return true;
}
return false;
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#setTag(java.lang.String, java.lang.String)
*/
@Override
public final S setTag(final String tag, final String value) {
this.context().setTag(tag, (Object) value);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#setTag(java.lang.String, boolean)
*/
@Override
public final S setTag(final String tag, final boolean value) {
this.context().setTag(tag, (Object) value);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#setTag(java.lang.String, java.lang.Number)
*/
@Override
public final S setTag(final String tag, final Number value) {
this.context().setTag(tag, (Object) value);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#context()
*/
@Override
public final DDSpanContext context() {
return this.context;
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#getBaggageItem(java.lang.String)
*/
@Override
public final String getBaggageItem(final String key) {
return this.context.getBaggageItem(key);
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#setBaggageItem(java.lang.String, java.lang.String)
*/
@Override
public final S setBaggageItem(final String key, final String value) {
this.context.setBaggageItem(key, value);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#setOperationName(java.lang.String)
*/
@Override
public final S setOperationName(final String operationName) {
this.context().setOperationName(operationName);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(java.util.Map)
*/
@Override
public final S log(final Map map) {
if (!extractError(map)) {
log.debug("`log` method is not implemented. Doing nothing");
}
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(long, java.util.Map)
*/
@Override
public final S log(final long l, final Map map) {
if (!extractError(map)) {
log.debug("`log` method is not implemented. Doing nothing");
}
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(java.lang.String)
*/
@Override
public final S log(final String s) {
log.debug("`log` method is not implemented. Provided log: {}", s);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(long, java.lang.String)
*/
@Override
public final S log(final long l, final String s) {
log.debug("`log` method is not implemented. Provided log: {}", s);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(java.lang.String, java.lang.Object)
*/
@Override
public final S log(final String s, final Object o) {
log.debug("`log` method is not implemented. Provided log: {}", s);
return thisInstance();
}
/* (non-Javadoc)
* @see io.opentracing.BaseSpan#log(long, java.lang.String, java.lang.Object)
*/
@Override
public final S log(final long l, final String s, final Object o) {
log.debug("`log` method is not implemented. Provided log: {}", s);
return thisInstance();
}
public final S setServiceName(final String serviceName) {
this.context().setServiceName(serviceName);
return thisInstance();
}
public final S setResourceName(final String resourceName) {
this.context().setResourceName(resourceName);
return thisInstance();
}
public final S setSpanType(final String type) {
this.context().setSpanType(type);
return thisInstance();
}
protected abstract S thisInstance();
// Getters and JSON serialisation instructions
/**
* Meta merges baggage and tags (stringified values)
*
* @return merged context baggage and tags
*/
@JsonGetter
public Map getMeta() {
final Map meta = new HashMap<>();
for (final Entry entry : context().getBaggageItems().entrySet()) {
meta.put(entry.getKey(), entry.getValue());
}
for (final Entry entry : getTags().entrySet()) {
meta.put(entry.getKey(), String.valueOf(entry.getValue()));
}
return meta;
}
@JsonGetter("start")
public long getStartTime() {
return startTimeMicro * 1000L;
}
@JsonGetter("duration")
public long getDurationNano() {
return durationNano;
}
@JsonGetter("service")
public String getServiceName() {
return context.getServiceName();
}
@JsonGetter("trace_id")
public long getTraceId() {
return context.getTraceId();
}
@JsonGetter("span_id")
public long getSpanId() {
return context.getSpanId();
}
@JsonGetter("parent_id")
public long getParentId() {
return context.getParentId();
}
@JsonGetter("resource")
public String getResourceName() {
return context.getResourceName();
}
@JsonGetter("name")
public String getOperationName() {
return context.getOperationName();
}
@JsonIgnore
public Map getTags() {
return this.context().getTags();
}
@JsonGetter
public String getType() {
return context.getSpanType();
}
@JsonGetter
public int getError() {
return context.getErrorFlag() ? 1 : 0;
}
@Override
public String toString() {
return new StringBuilder()
.append(context.toString())
.append(", duration_ns=")
.append(durationNano)
.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy