First draft of datadog-api
ADD
dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java
+package datadog.trace.tracer.ext;
+
+import datadog.trace.tracer.Span;
+import datadog.trace.tracer.Trace;
+
+/**
+ * A scope holds a single span or trace continuation and may optionally close out its span or
+ * continuation.
+ *
+ * <p>To create a scope, see {@link TracerContext#pushScope(Span, boolean)} and {@link
+ * TracerContext#pushScope(Trace.Continuation, boolean)}.
+ *
+ * <p>All created scopes must be closed with {@link }
+ */
+public interface Scope {
+ /** Get the span held by this scope. */
+ Span span();
+
+ /**
+ * Close this scope. This method must be invoked on all created scopes.
+ *
+ * <p>Attempting to close a scope which is not on the top of its TracerContext's scope-stack is an
+ * error. See {@link TracerContext#topOfScopeStack()}.
+ */
+ void close();
+}

ADD
dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java
+package datadog.trace.tracer.ext;
+
+import datadog.trace.tracer.Span;
+import datadog.trace.tracer.Trace;
+import datadog.trace.tracer.Tracer;
+
+/**
+ * Provides a place to store a shared global tracer with convenient span-building helper methods.
+ *
+ * <p>Also maintains a stack of active scopes (or scope-stack). The span of the scope on the top of
+ * the scope stack is used as the parent for newly created spans.
+ *
+ * <p>This class is thread safe.
+ */
+public final class TracerContext {
+ // global TracerContext
+ /** Get the global TracerContext */
+ public static TracerContext getGlobalContext() {
+ return null;
+ }
+
+ /**
+ * Register the global TracerContext.
+ *
+ * @param context The context to register.
+ * @param replaceExisting If true, the existing global TracerContext will be replaced
+ * @return The old global TracerContext, or null if no previous context ws registered
+ */
+ public static TracerContext registerGlobalContext(
+ TracerContext context, boolean replaceExisting) {
+ return null;
+ }
+
+ /** @return True if a global TracerContext has been registered */
+ public static boolean isRegistered() {
+ return false;
+ }
+
+ private final Tracer tracer;
+
+ public TracerContext(Tracer tracer) {
+ this.tracer = tracer;
+ }
+
+ /** @return The tracer associated with this TracerContext */
+ public Tracer getTracer() {
+ return tracer;
+ }
+
+ // TODO: convenience APIs like buildSpan, etc.
+
+ /**
+ * Push a new scope to the top of this scope-stack. The scope's span will be the given span.
+ *
+ * @param span
+ * @param finishSpanOnScopeClose
+ * @return
+ */
+ public Scope pushScope(Span span, boolean finishSpanOnScopeClose) {
+ return null;
+ }
+
+ /**
+ * Push a new scope to the top of this scope-stack. The scope's span will be the continuation's
+ * span.
+ *
+ * @param continuation
+ * @param closeContinuationOnScopeClose
+ * @return
+ */
+ public Scope pushScope(Trace.Continuation continuation, boolean closeContinuationOnScopeClose) {
+ return null;
+ }
+
+ /**
+ * Pop the given scope off the top of the scope stack.
+ *
+ * <p>If the given scope is not the topmost scope on the stack an error will be thrown.
+ *
+ * @param scope the topmost scope in the scope stack.
+ */
+ public void popScope(Scope scope) {}
+
+ /** @return The scope on the top of this scope stack or null if there is no active scope. */
+ public Scope topOfScopeStack() {
+ return null;
+ }
+}

MODIFY
dd-trace/README.md
dd-trace/README.md
# Datadog Tracer Internal API
Contains the core elements needed to create and report APM traces to Datadog.
-It's recommended to use `dd-trace-utils` in addition to this api.
+It's recommended to use `:dd-trace-ext` in addition to this api.

ADD
dd-trace/src/main/java/datadog/trace/tracer/Clock.java
+package datadog.trace.tracer;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple wrapper for system clock that aims to provide the current time
+ *
+ * <p>
+ *
+ * <p>
+ *
+ * <p>
+ *
+ * <p>The JDK provides two clocks:
+ * <li>one in nanoseconds, for precision, but it can only use to measure durations
+ * <li>one in milliseconds, for accuracy, useful to provide epoch time
+ *
+ * <p>
+ *
+ * <p>At this time, we are using a millis precision (converted to micros) in order to guarantee
+ * consistency between the span start times and the durations
+ */
+public class Clock {
+
+ /**
+ * Get the current nanos ticks (i.e. System.nanonTime()), this method can't be use for date
+ * accuracy (only duration calculations)
+ *
+ * @return The current nanos ticks
+ */
+ public static long nanoTime() {
+ return System.nanoTime();
+ }
+
+ /**
+ * Get the current epoch time in micros.
+ *
+ * <p>Note: The actual precision is the millis.
+ *
+ * @return the current epoch time in micros
+ */
+ public static long epochTimeMicro() {
+ return TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
+ }
+
+ /**
+ * Get the current epoch time in nanos.
+ *
+ * <p>Note: The actual precision is the millis. This will overflow ~290 years after epoch
+ *
+ * @return the current epoch time in nanos
+ */
+ public static long epochTimeNano() {
+ return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
+ }
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/Span.java
+package datadog.trace.tracer;
+
+/**
+ * A single measurement of time with arbitrary key-value attributes.
+ *
+ * <p>All spans are thread safe.
+ *
+ * <p>Spans may not be constructed individually, but created through a {@link Trace}
+ */
+public interface Span {
+
+ /** @return The trace this span is associated with. */
+ Trace getTrace();
+
+ /** Stop the span's timer. Has no effect if the span is already finished. */
+ void finish();
+
+ /**
+ * Stop the span's timer. Has no effect if the span is already finished.
+ *
+ * <p>It's undefined behavior to specify a finish timestamp which occurred before this span's
+ * start timestamp.
+ *
+ * @param finishTimestampNanoseconds Epoch time in nanoseconds.
+ */
+ void finish(long finishTimestampNanoseconds);
+
+ /** Returns true if a finish method has been invoked on this span. */
+ boolean isFinished();
+
+ /**
+ * Attach a {@link LifecycleInterceptor} to this span.
+ *
+ * @param interceptor the interceptor to attach.
+ */
+ void addInterceptor(LifecycleInterceptor interceptor);
+
+ /**
+ * Get the span context for this span.
+ *
+ * @return the span context.
+ */
+ SpanContext getContext();
+
+ /**
+ * Get a meta value on a span.
+ *
+ * @param key The meta key
+ * @return The value currently associated with the given key. Null if no associated. TODO: can
+ * return null
+ */
+ Object getMeta(String key);
+
+ /**
+ * Set key-value metadata on the span.
+ *
+ * <p>TODO: Forbid setting null?
+ */
+ void setMeta(String key, String value);
+
+ /** {@link Span#setMeta(String, String)} for boolean values */
+ void setMeta(String key, boolean value);
+
+ /** {@link Span#setMeta(String, String)} for number values */
+ void setMeta(String key, Number value);
+
+ /** Get the span's name */
+ String getName();
+ /**
+ * Set the span's name.
+ *
+ * @param newName the new name for the span.
+ */
+ void setName(String newName);
+
+ String getResource();
+
+ void setResource(String newResource);
+
+ String getService();
+
+ void setService(String newService);
+
+ String getType();
+
+ void setType(String newType);
+
+ boolean isErrored();
+
+ /** Attach a throwable to this span. */
+ void attachThrowable();
+
+ /**
+ * Mark the span as having an error.
+ *
+ * @param isErrored true if the span has an error.
+ */
+ void setError(boolean isErrored);
+
+ // TODO: OpenTracing Span#log methods. Do we need something here to support them? Current DDSpan
+ // does not implement.
+
+ /**
+ * A LifecycleInterceptor allows adding hooks to particular events between a span starting and
+ * finishing.
+ */
+ interface LifecycleInterceptor {
+ /**
+ * Called after a span is started.
+ *
+ * @param span the started span
+ */
+ void spanStarted(Span span);
+
+ /** Called after a span's metadata is updated. */
+ void afterMetadataSet(Span span, Object key, Object value);
+
+ /**
+ * Called after a span is finished.
+ *
+ * @param span the started span
+ */
+ void spanFinished(Span span);
+ }
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/SpanContext.java
+package datadog.trace.tracer;
+
+/**
+ * All attributes of a {@link Span} which propagate for distributed tracing.
+ *
+ * <p>All Spans must have a SpanContext, but not all SpanContexts require a span.
+ *
+ * <p>All SpanContexts are thread safe.
+ */
+public interface SpanContext {
+ /**
+ * Get this context's span id.
+ *
+ * @return 64 bit unsigned integer in String format.
+ */
+ String getSpanId();
+
+ /**
+ * Get this context's parent span id.
+ *
+ * @return 64 bit unsigned integer in String format.
+ */
+ String getParentId();
+
+ /**
+ * Get this context's trace id.
+ *
+ * @return 64 bit unsigned integer in String format.
+ */
+ String getTraceId();
+
+ /**
+ * Get the sampling flag for this context.
+ *
+ * @return sampling flag for null if no sampling flags are set.
+ */
+ // TODO: should we add a @Nullable annotation to our project?
+ Integer getSamplingFlags();
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/Trace.java
+package datadog.trace.tracer;
+
+/**
+ * A tree of {@link Span}s with a single root node plus logic to determine when to report said tree
+ * to the backend.
+ *
+ * <p>A trace will be written when all of its spans are finished and all trace continuations are
+ * closed.
+ */
+public interface Trace {
+ /** Get the tracer which created this trace. */
+ Tracer getTracer();
+
+ /** Get the root span for this trace. This will never be null. */
+ Span getRootSpan();
+
+ /**
+ * Create a new span in this trace as a child of the given parentSpan.
+ *
+ * @param parentSpan the parent to use. Must be a span in this trace.
+ * @return the new span. It is the caller's responsibility to ensure {@link Span#finish()} is
+ * eventually invoked on this span.
+ */
+ Span createSpan(Span parentSpan);
+
+ /**
+ * Create a new continuation for this trace
+ *
+ * @param parentSpan the parent to use. Must be a span in this trace.
+ * @return the new continuation. It is the caller's responsibility to ensure {@link
+ * Continuation#close()} is eventually invoked on this continuation.
+ */
+ Continuation createContinuation(Span parentSpan);
+
+ interface LifecycleInterceptor {
+ /**
+ * Invoked when a trace is eligible for writing but hasn't been handed off to its writer yet.
+ *
+ * @param trace The intercepted trace.
+ */
+ void beforeTraceWritten(Trace trace);
+ }
+
+ /** A way to prevent a trace from reporting without creating a span. */
+ interface Continuation {
+ /**
+ * Close the continuation. Continuation's trace will not block reporting on account of this
+ * continuation.
+ *
+ * <p>Has no effect after the first invocation.
+ */
+ void close();
+ }
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/Tracer.java
+package datadog.trace.tracer;
+
+import datadog.trace.api.Config;
+import datadog.trace.tracer.sampling.Sampler;
+import datadog.trace.tracer.writer.Writer;
+
+/** A Tracer creates {@link Trace}s and holds common settings across traces. */
+public class Tracer {
+ /** Default service name if none provided on the trace or span */
+ final String serviceName = null;
+ /** Writer is an charge of reporting traces and spans to the desired endpoint */
+ final Writer writer = null;
+ /** Sampler defines the sampling policy in order to reduce the number of traces for instance */
+ final Sampler sampler = null;
+ /** Settings for this tracer. */
+ final Config config = null;
+
+ // TODO: doc inject and extract
+
+ public <T> void inject(final SpanContext spanContext, final Object format, final T carrier) {}
+
+ public <T> SpanContext extract(final Object format, final T carrier) {
+ return null;
+ }
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java
+package datadog.trace.tracer.impl;
+
+import datadog.trace.tracer.Clock;
+import datadog.trace.tracer.Span;
+import datadog.trace.tracer.SpanContext;
+import datadog.trace.tracer.Trace;
+import java.util.Map;
+
+public class SpanImpl implements Span {
+ private final Clock clock = null;
+ private final Trace trace = null;
+ // See See https://docs.datadoghq.com/api/?lang=python#tracing
+ // Required attributes to report to datadog.
+ private final SpanContext context = null;
+ /* Span name. May not exceed 100 characters. */
+ private final String name = "";
+ /* Span resource (e.g. http endpoint). May not exceed 5000 characters. */
+ private final String resource = "";
+ /* Span service. May not exceed 100 characters. */
+ private final String service = "";
+ /* The start time of the request in nanoseconds from the unix epoch. */
+ private final long startEpochNano = -1;
+ /* Duration of the span in nanoseconds */
+ private final long durationNano = -1;
+ // optional attributes to report to datadog
+ /* The type of the span (web, db, etc). See DDSpanTypes. */
+ private final String type = null;
+ /* Marks the span as having an error. */
+ private final boolean isErrored = false;
+ /* Additional key-value pairs for a span. */
+ private final Map<String, Object> meta = null;
+
+ /**
+ * Create a span with a start time of the current timestamp.
+ *
+ * @param clock The clock to use to measure the span's duration.
+ */
+ SpanImpl(final Clock clock) {}
+
+ /**
+ * Create a span with the a specific start timestamp.
+ *
+ * @param clock The clock to use to measure the span's duration.
+ * @param startTimeNanoseconds Epoch time in nanoseconds when this span started.
+ */
+ SpanImpl(final Clock clock, final long startTimeNanoseconds) {}
+
+ @Override
+ public Trace getTrace() {
+ return null;
+ }
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public void finish(long finishTimestampNanoseconds) {}
+
+ @Override
+ public boolean isFinished() {
+ return false;
+ }
+
+ @Override
+ public void attachThrowable() {}
+
+ @Override
+ public void setError(boolean isErrored) {}
+
+ @Override
+ public void addInterceptor(LifecycleInterceptor interceptor) {}
+
+ @Override
+ public SpanContext getContext() {
+ return null;
+ }
+
+ @Override
+ public Object getMeta(String key) {
+ return null;
+ }
+
+ @Override
+ public void setMeta(String key, String value) {}
+
+ @Override
+ public void setMeta(String key, boolean value) {}
+
+ @Override
+ public void setMeta(String key, Number value) {}
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public void setName(String newName) {}
+
+ @Override
+ public String getResource() {
+ return null;
+ }
+
+ @Override
+ public void setResource(String newResource) {}
+
+ @Override
+ public String getService() {
+ return null;
+ }
+
+ @Override
+ public void setService(String newService) {}
+
+ @Override
+ public String getType() {
+ return null;
+ }
+
+ @Override
+ public void setType(String newType) {}
+
+ @Override
+ public boolean isErrored() {
+ return false;
+ }
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java
+package datadog.trace.tracer.impl;
+
+import datadog.trace.tracer.Span;
+import datadog.trace.tracer.writer.Writer;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TraceImpl {
+ private final Writer writer = null;
+ /**
+ * Count of all unfinished spans and continuations.
+ *
+ * <p>When the count reaches 0 this trace will be reported.
+ */
+ private final AtomicInteger referenceCount = new AtomicInteger(0);
+
+ /**
+ * Thread safe list of spans. List is ordered by span-creation time. The root span will always be
+ * the first element. Note that span creation time may differ from span start timestamp (in cases
+ * where span creators specify a custom start timestamp).
+ *
+ * <p>References to these spans are weakly held. If api users create spans but do not finish them
+ * we will clean up this trace and increment the trace count with the writer. However, the trace
+ * itself will not be reported because incorrect api usage produces suspect data.
+ *
+ * <p>TODO: There is a potential edge-case with the root span. If the root span is GC'd everything
+ * relying on getRootSpan() will break.
+ */
+ private final List<Span> spans = null;
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/sampling/Sampler.java
+package datadog.trace.tracer.sampling;
+
+import datadog.trace.tracer.Trace;
+
+/**
+ * Keeps or discards traces.
+ *
+ * <p>Note that in most cases the sampler will keep all traces. Most of the sampling logic is done
+ * downstream by the trace-agent or dd-backend.
+ */
+public interface Sampler {
+ /**
+ * Run tracer sampling logic on the trace.
+ *
+ * @param trace
+ * @return true if the trace should be kept/written/reported.
+ */
+ boolean sample(Trace trace);
+}

ADD
dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java
+package datadog.trace.tracer.writer;
+
+import datadog.trace.tracer.Trace;
+import java.util.List;
+
+/** A writer sends traces to some place. */
+public interface Writer {
+ /**
+ * Write a trace represented by the entire list of all the finished spans
+ *
+ * @param traces the list of traces to write
+ */
+ void write(List<Trace> traces);
+
+ /**
+ * Inform the writer that traces occurred but will not be written. Used by tracer-side sampling.
+ *
+ * @param numTraces the number of traces to increment the writer's total count by.
+ */
+ void incrementTraceCount(int numTraces);
+
+ /** Start the writer */
+ void start();
+
+ /**
+ * Indicates to the writer that no future writing will come and it should terminates all
+ * connections and tasks
+ */
+ void close();
+}

First draft of datadog-api