opentelemetry-java-instrume.../dd-trace-ot/src/main/java/datadog/opentracing/DDSpanContext.java

319 lines
8.8 KiB
Java

package datadog.opentracing;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Maps;
import datadog.opentracing.decorators.AbstractDecorator;
import datadog.trace.api.DDTags;
import datadog.trace.common.sampling.PrioritySampling;
import io.opentracing.tag.Tags;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import lombok.extern.slf4j.Slf4j;
/**
* SpanContext represents Span state that must propagate to descendant Spans and across process
* boundaries.
*
* <p>SpanContext is logically divided into two pieces: (1) the user-level "Baggage" that propagates
* across Span boundaries and (2) any Datadog fields that are needed to identify or contextualize
* the associated Span instance
*/
@Slf4j
public class DDSpanContext implements io.opentracing.SpanContext {
// Opentracing attributes
private final long traceId;
private final long spanId;
private final long parentId;
private final String threadName = Thread.currentThread().getName();
private final long threadId = Thread.currentThread().getId();
/** The collection of all span related to this one */
private final Queue<DDSpan> trace;
// DD attributes
/** For technical reasons, the ref to the original tracer */
private final DDTracer tracer;
private Map<String, String> baggageItems;
/** The service name is required, otherwise the span are dropped by the agent */
private String serviceName;
/** The resource associated to the service (server_web, database, etc.) */
private String resourceName;
/** True indicates that the span reports an error */
private boolean errorFlag;
/** The type of the span. If null, the Datadog Agent will report as a custom */
private String spanType;
/** Each span have an operation name describing the current span */
private String operationName;
/** The sampling priority of the trace */
private volatile int samplingPriority = PrioritySampling.UNSET;
/** When true, the samplingPriority cannot be changed. */
private volatile boolean samplingPriorityLocked = false;
// Others attributes
/** Tags are associated to the current span, they will not propagate to the children span */
private Map<String, Object> tags;
public DDSpanContext(
final long traceId,
final long spanId,
final long parentId,
final String serviceName,
final String operationName,
final String resourceName,
final int samplingPriority,
final Map<String, String> baggageItems,
final boolean errorFlag,
final String spanType,
final Map<String, Object> tags,
final Queue<DDSpan> trace,
final DDTracer tracer) {
this.traceId = traceId;
this.spanId = spanId;
this.parentId = parentId;
if (baggageItems == null) {
this.baggageItems = Collections.emptyMap();
} else {
this.baggageItems = baggageItems;
}
this.serviceName = serviceName;
this.operationName = operationName;
this.resourceName = resourceName;
this.samplingPriority = samplingPriority;
this.errorFlag = errorFlag;
this.spanType = spanType;
this.tags = tags;
if (trace == null) {
// TODO: figure out better concurrency model.
this.trace = new ConcurrentLinkedQueue<>();
} else {
this.trace = trace;
}
this.tracer = tracer;
}
public long getTraceId() {
return this.traceId;
}
public long getParentId() {
return this.parentId;
}
public long getSpanId() {
return this.spanId;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(final String serviceName) {
this.serviceName = serviceName;
}
public String getResourceName() {
return this.resourceName == null || this.resourceName.isEmpty()
? this.operationName
: this.resourceName;
}
public void setResourceName(final String resourceName) {
this.resourceName = resourceName;
}
public boolean getErrorFlag() {
return errorFlag;
}
public void setErrorFlag(final boolean errorFlag) {
this.errorFlag = errorFlag;
}
public String getSpanType() {
return spanType;
}
public void setSpanType(final String spanType) {
this.spanType = spanType;
}
public void setSamplingPriority(int newPriority) {
if (samplingPriorityLocked) {
log.warn(
"samplingPriority locked at {}. Refusing to set to {}", samplingPriority, newPriority);
} else {
synchronized (this) {
// sync with lockSamplingPriority
this.samplingPriority = newPriority;
}
}
}
public int getSamplingPriority() {
return samplingPriority;
}
/**
* Prevent future changes to the context's sampling priority.
*
* <p>Used when a span is extracted or injected for propagation.
*
* <p>Has no effect if the sampling priority is unset.
*
* @return true if the sampling priority was locked.
*/
public boolean lockSamplingPriority() {
if (!samplingPriorityLocked) {
synchronized (this) {
// sync with setSamplingPriority
if (samplingPriority == PrioritySampling.UNSET) {
log.debug("{} : refusing to lock unset samplingPriority", this);
} else {
this.samplingPriorityLocked = true;
log.debug("{} : locked samplingPriority to {}", this, this.samplingPriority);
}
}
}
return samplingPriorityLocked;
}
public void setBaggageItem(final String key, final String value) {
if (this.baggageItems.isEmpty()) {
this.baggageItems = new HashMap<>();
}
this.baggageItems.put(key, value);
}
public String getBaggageItem(final String key) {
return this.baggageItems.get(key);
}
public Map<String, String> getBaggageItems() {
return baggageItems;
}
/* (non-Javadoc)
* @see io.opentracing.SpanContext#baggageItems()
*/
@Override
public Iterable<Map.Entry<String, String>> baggageItems() {
return this.baggageItems.entrySet();
}
@JsonIgnore
public Queue<DDSpan> getTrace() {
return this.trace;
}
@JsonIgnore
public DDTracer getTracer() {
return this.tracer;
}
/**
* Add a tag to the span. Tags are not propagated to the children
*
* @param tag the tag-name
* @param value the value of the tag. tags with null values are ignored.
*/
public synchronized void setTag(final String tag, final Object value) {
if (value == null) {
tags.remove(tag);
return;
}
if (tag.equals(DDTags.SERVICE_NAME)) {
setServiceName(value.toString());
return;
} else if (tag.equals(DDTags.RESOURCE_NAME)) {
setResourceName(value.toString());
return;
} else if (tag.equals(DDTags.SPAN_TYPE)) {
setSpanType(value.toString());
return;
}
if (this.tags.isEmpty()) {
this.tags = new HashMap<>();
}
this.tags.put(tag, value);
// Call decorators
final List<AbstractDecorator> decorators = tracer.getSpanContextDecorators(tag);
if (decorators != null && value != null) {
for (final AbstractDecorator decorator : decorators) {
try {
decorator.afterSetTag(this, tag, value);
} catch (final Throwable ex) {
log.warn(
"Could not decorate the span decorator={}: {}",
decorator.getClass().getSimpleName(),
ex.getMessage());
}
}
}
// Error management
if (Tags.ERROR.getKey().equals(tag)
&& Boolean.TRUE.equals(value instanceof String ? Boolean.valueOf((String) value) : value)) {
this.errorFlag = true;
}
}
public synchronized Map<String, Object> getTags() {
if (tags.isEmpty()) {
tags = Maps.newHashMapWithExpectedSize(2);
}
tags.put(DDTags.THREAD_NAME, threadName);
tags.put(DDTags.THREAD_ID, threadId);
tags.put(DDTags.SPAN_TYPE, getSpanType());
return Collections.unmodifiableMap(tags);
}
@Override
public String toString() {
final StringBuilder s =
new StringBuilder()
.append("Span [ t_id=")
.append(traceId)
.append(", s_id=")
.append(spanId)
.append(", p_id=")
.append(parentId)
.append("] trace=")
.append(getServiceName())
.append("/")
.append(getOperationName())
.append("/")
.append(getResourceName());
if (getSamplingPriority() != PrioritySampling.UNSET) {
s.append(" samplingPriority=").append(getSamplingPriority());
}
if (errorFlag) {
s.append(" *errored*");
}
if (tags != null) {
s.append(" tags=").append(new TreeMap(tags));
}
return s.toString();
}
public String getOperationName() {
return operationName;
}
public void setOperationName(final String operationName) {
this.operationName = operationName;
}
}