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

com.datadoghq.trace.DDTracer Maven / Gradle / Ivy

There is a newer version: 0.2.12
Show newest version
package com.datadoghq.trace;

import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.propagation.Codec;
import com.datadoghq.trace.propagation.HTTPCodec;
import com.datadoghq.trace.sampling.AllSampler;
import com.datadoghq.trace.sampling.Sampler;
import com.datadoghq.trace.writer.DDAgentWriter;
import com.datadoghq.trace.writer.Writer;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.propagation.Format;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;


/**
 * DDTracer makes it easy to send traces and span to DD using the OpenTracing instrumentation.
 */
public class DDTracer implements io.opentracing.Tracer {

	/**
	 * Writer is an charge of reporting traces and spans to the desired endpoint
	 */
	private Writer writer;
	/**
	 * Sampler defines the sampling policy in order to reduce the number of traces for instance
	 */
	private final Sampler sampler;

	/**
	 * Default service name if none provided on the trace or span
	 */
	private final String defaultServiceName;

	/**
	 * Span context decorators
	 */
	private final Map> spanContextDecorators = new HashMap>();


	private final static Logger logger = LoggerFactory.getLogger(DDTracer.class);
	private final CodecRegistry registry;

	public static final String UNASSIGNED_DEFAULT_SERVICE_NAME = "unnamed-java-app";
	public static final Writer UNASSIGNED_WRITER = new DDAgentWriter();
	public static final Sampler UNASSIGNED_SAMPLER = new AllSampler();

	/**
	 * Default constructor, trace/spans are logged, no trace/span dropped
	 */
	public DDTracer() {
		this(UNASSIGNED_WRITER);
	}

	public DDTracer(Writer writer) {
		this(writer, new AllSampler());
	}

	public DDTracer(Writer writer, Sampler sampler) {
		this(UNASSIGNED_DEFAULT_SERVICE_NAME, writer, sampler);
	}

	public DDTracer(String defaultServiceName, Writer writer, Sampler sampler) {
		this.defaultServiceName = defaultServiceName;
		this.writer = writer;
		this.writer.start();
		this.sampler = sampler;
		registry = new CodecRegistry();
		registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
	}

	/**
	 * Returns the list of span context decorators
	 *
	 * @return the list of span context decorators
	 */
	public List getSpanContextDecorators(String tag) {
		return spanContextDecorators.get(tag);
	}

	/**
	 * Add a new decorator in the list ({@link DDSpanContextDecorator})
	 *
	 * @param decorator The decorator in the list
	 */
	public void addDecorator(DDSpanContextDecorator decorator) {

		List list = spanContextDecorators.get(decorator.getMatchingTag());
		if (list == null) {
			list = new ArrayList();
		}
		list.add(decorator);

		spanContextDecorators.put(decorator.getMatchingTag(), list);
	}


	public DDSpanBuilder buildSpan(String operationName) {
		return new DDSpanBuilder(operationName);
	}


	public  void inject(SpanContext spanContext, Format format, T carrier) {

		Codec codec = registry.get(format);
		if (codec == null) {
			logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
		} else {
			codec.inject((DDSpanContext) spanContext, carrier);
		}
	}

	public  SpanContext extract(Format format, T carrier) {

		Codec codec = registry.get(format);
		if (codec == null) {
			logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
		} else {
			return codec.extract(carrier);
		}
		return null;
	}


	/**
	 * We use the sampler to know if the trace has to be reported/written.
	 * The sampler is called on the first span (root span) of the trace.
	 * If the trace is marked as a sample, we report it.
	 *
	 * @param trace a list of the spans related to the same trace
	 */
	public void write(List trace) {
		if (trace.isEmpty()) {
			return;
		}
		if (this.sampler.sample((DDSpan) trace.get(0))) {
			this.writer.write(trace);
		}
	}

	public void close() {
		writer.close();
	}

	/**
	 * Spans are built using this builder
	 */
	public class DDSpanBuilder implements SpanBuilder {

		/**
		 * Each span must have an operationName according to the opentracing specification
		 */
		private String operationName;

		// Builder attributes
		private Map tags = Collections.emptyMap();
		private long timestamp;
		private SpanContext parent;
		private String serviceName;
		private String resourceName;
		private boolean errorFlag;
		private String spanType;

		/**
		 * This method actually build the span according to the builder settings
		 * DD-Agent requires a serviceName. If it has not been provided, the method will throw a RuntimeException
		 *
		 * @return An fresh span
		 */
		public DDSpan start() {

			// build the context
			DDSpanContext context = buildSpanContext();
			DDSpan span = new DDSpan(this.timestamp, context);

			logger.debug("{} - Starting a new span.", span);

			return span;
		}


		public DDTracer.DDSpanBuilder withTag(String tag, Number number) {
			return withTag(tag, (Object) number);
		}

		public DDTracer.DDSpanBuilder withTag(String tag, String string) {
			if (tag.equals(DDTags.SERVICE_NAME)) {
				return withServiceName(string);
			} else if (tag.equals(DDTags.RESOURCE_NAME)) {
				return withResourceName(string);
			} else if (tag.equals(DDTags.SPAN_TYPE)) {
				return withSpanType(string);
			} else {
				return withTag(tag, (Object) string);
			}
		}

		public DDTracer.DDSpanBuilder withTag(String tag, boolean bool) {
			return withTag(tag, (Object) bool);
		}

		public DDSpanBuilder(String operationName) {
			this.operationName = operationName;
		}


		public DDTracer.DDSpanBuilder withStartTimestamp(long timestampMillis) {
			this.timestamp = timestampMillis;
			return this;
		}

		public DDTracer.DDSpanBuilder withServiceName(String serviceName) {
			this.serviceName = serviceName;
			return this;
		}

		public DDTracer.DDSpanBuilder withResourceName(String resourceName) {
			this.resourceName = resourceName;
			return this;
		}

		public DDTracer.DDSpanBuilder withErrorFlag() {
			this.errorFlag = true;
			return this;
		}

		public DDTracer.DDSpanBuilder withSpanType(String spanType) {
			this.spanType = spanType;
			return this;
		}

		public Iterable> baggageItems() {
			if (parent == null) {
				return Collections.emptyList();
			}
			return parent.baggageItems();
		}

		public DDTracer.DDSpanBuilder asChildOf(Span span) {
			return asChildOf(span == null ? null : span.context());
		}

		public DDTracer.DDSpanBuilder asChildOf(SpanContext spanContext) {
			this.parent = spanContext;
			return this;
		}

		public DDTracer.DDSpanBuilder addReference(String referenceType, SpanContext spanContext) {
			logger.debug("`addReference` method is not implemented. Doing nothing");
			return this;
		}

		// Private methods
		private DDTracer.DDSpanBuilder withTag(String tag, Object value) {
			if (this.tags.isEmpty()) {
				this.tags = new HashMap();
			}
			this.tags.put(tag, value);
			return this;
		}

		private long generateNewId() {
			return System.nanoTime();
		}

		/**
		 * Build the SpanContext, if the actual span has a parent, the following attributes must be propagated:
		 * - ServiceName
		 * - Baggage
		 * - Trace (a list of all spans related)
		 * - SpanType
		 *
		 * @return the context
		 */
		private DDSpanContext buildSpanContext() {
			long generatedId = generateNewId();
			DDSpanContext context;
			DDSpanContext p = this.parent != null ? (DDSpanContext) this.parent : null;

			String spanType = this.spanType;
			if (spanType == null && this.parent != null) {
				spanType = p.getSpanType();
			}

			String serviceName = this.serviceName;
			if (serviceName == null) {
				if (p != null && p.getServiceName() != null) {
					serviceName = p.getServiceName();
				} else {
					serviceName = defaultServiceName;
				}
			}

			String operationName = this.operationName != null ? this.operationName : this.resourceName;

			//this.operationName, this.tags,

			// some attributes are inherited from the parent
			context = new DDSpanContext(
					this.parent == null ? generatedId : p.getTraceId(),
					generatedId,
					this.parent == null ? 0L : p.getSpanId(),
					serviceName,
					operationName,
					this.resourceName,
					this.parent == null ? null : p.getBaggageItems(),
					errorFlag,
					spanType,
					this.tags,
					this.parent == null ? null : p.getTrace(),
					DDTracer.this
			);

			return context;
		}

	}


	private static class CodecRegistry {

		private final Map, Codec> codecs = new HashMap, Codec>();

		@SuppressWarnings("unchecked")
		 Codec get(Format format) {
			return (Codec) codecs.get(format);
		}

		public  void register(Format format, Codec codec) {
			codecs.put(format, codec);
		}

	}

	@Override
	public String toString() {
		return "DDTracer{" +
				"writer=" + writer +
				", sampler=" + sampler +
				'}';
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy