From 0e6a416b57fa796b1ebaf5d81de54706100742d1 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Mon, 3 Dec 2018 11:45:29 -0800 Subject: [PATCH 1/4] Initial gradle projects --- dd-trace-ext/README.md | 4 ++++ dd-trace-ext/dd-trace-ext.gradle | 6 ++++++ dd-trace/README.md | 4 ++++ dd-trace/dd-trace.gradle | 17 +++++++++++++++++ settings.gradle | 15 ++++++++++++--- 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 dd-trace-ext/README.md create mode 100644 dd-trace-ext/dd-trace-ext.gradle create mode 100644 dd-trace/README.md create mode 100644 dd-trace/dd-trace.gradle diff --git a/dd-trace-ext/README.md b/dd-trace-ext/README.md new file mode 100644 index 0000000000..54c177619f --- /dev/null +++ b/dd-trace-ext/README.md @@ -0,0 +1,4 @@ +# Convenience Utils for Datadog Tracer Internal API +- Trace Scopes +- Global Tracer +- Async controls diff --git a/dd-trace-ext/dd-trace-ext.gradle b/dd-trace-ext/dd-trace-ext.gradle new file mode 100644 index 0000000000..bd6e0d7315 --- /dev/null +++ b/dd-trace-ext/dd-trace-ext.gradle @@ -0,0 +1,6 @@ +description = 'dd-trace-ext' +apply from: "${rootDir}/gradle/java.gradle" + +dependencies { + compile project(':dd-trace') +} diff --git a/dd-trace/README.md b/dd-trace/README.md new file mode 100644 index 0000000000..4d9f601f2c --- /dev/null +++ b/dd-trace/README.md @@ -0,0 +1,4 @@ +# 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. diff --git a/dd-trace/dd-trace.gradle b/dd-trace/dd-trace.gradle new file mode 100644 index 0000000000..e41c60d76b --- /dev/null +++ b/dd-trace/dd-trace.gradle @@ -0,0 +1,17 @@ +description = 'dd-trace' +apply from: "${rootDir}/gradle/java.gradle" + +// TODO: Move over any special setups from dd-trace-ot (e.g. trace-agent integration tests). + +dependencies { + annotationProcessor deps.autoservice + implementation deps.autoservice + + compile project(':dd-trace-api') + + compile deps.jackson + compile deps.slf4j + // any higher versions seems to break ES tests with this exception: + // java.lang.NoSuchMethodError: com.fasterxml.jackson.dataformat.smile.SmileGenerator.getOutputContext() + compile group: 'org.msgpack', name: 'jackson-dataformat-msgpack', version: '0.8.14' +} diff --git a/settings.gradle b/settings.gradle index f1bbb807a8..b96b646b67 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,13 +1,22 @@ rootProject.name = 'dd-trace-java' -include ':dd-trace-ot' -include ':dd-java-agent' +// core tracing projects +include ':dd-trace-api' include ':dd-java-agent:agent-bootstrap' +include ':dd-trace' +include ':dd-trace-ext' + +// implements for third-party tracing libraries +include ':dd-trace-ot' + +// agent projects +include ':dd-java-agent' include ':dd-java-agent:agent-tooling' include ':dd-java-agent:agent-jmxfetch' + +// misc include ':dd-java-agent:testing' include ':dd-java-agent-ittests' -include ':dd-trace-api' // instrumentation: include ':dd-java-agent:instrumentation:akka-http-10.0' From 3ad6a4b38284f8654837f961532cf8cf3dd3aaf5 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Mon, 3 Dec 2018 16:35:57 -0800 Subject: [PATCH 2/4] First draft of datadog-api --- .../java/datadog/trace/tracer/ext/Scope.java | 26 ++++ .../trace/tracer/ext/TracerContext.java | 88 ++++++++++++ dd-trace/README.md | 2 +- .../main/java/datadog/trace/tracer/Clock.java | 56 ++++++++ .../main/java/datadog/trace/tracer/Span.java | 125 +++++++++++++++++ .../datadog/trace/tracer/SpanContext.java | 39 ++++++ .../main/java/datadog/trace/tracer/Trace.java | 54 ++++++++ .../java/datadog/trace/tracer/Tracer.java | 25 ++++ .../datadog/trace/tracer/impl/SpanImpl.java | 128 ++++++++++++++++++ .../datadog/trace/tracer/impl/TraceImpl.java | 30 ++++ .../trace/tracer/sampling/Sampler.java | 19 +++ .../datadog/trace/tracer/writer/Writer.java | 30 ++++ 12 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java create mode 100644 dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/Clock.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/Span.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/SpanContext.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/Trace.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/Tracer.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/sampling/Sampler.java create mode 100644 dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java new file mode 100644 index 0000000000..4b07974aa3 --- /dev/null +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java @@ -0,0 +1,26 @@ +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. + * + *

To create a scope, see {@link TracerContext#pushScope(Span, boolean)} and {@link + * TracerContext#pushScope(Trace.Continuation, boolean)}. + * + *

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. + * + *

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(); +} diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java new file mode 100644 index 0000000000..ce31b8bed7 --- /dev/null +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java @@ -0,0 +1,88 @@ +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. + * + *

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. + * + *

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. + * + *

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; + } +} diff --git a/dd-trace/README.md b/dd-trace/README.md index 4d9f601f2c..7043db7a19 100644 --- a/dd-trace/README.md +++ b/dd-trace/README.md @@ -1,4 +1,4 @@ # 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. diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Clock.java b/dd-trace/src/main/java/datadog/trace/tracer/Clock.java new file mode 100644 index 0000000000..22bfd41704 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/Clock.java @@ -0,0 +1,56 @@ +package datadog.trace.tracer; + +import java.util.concurrent.TimeUnit; + +/** + * A simple wrapper for system clock that aims to provide the current time + * + *

+ * + *

+ * + *

+ * + *

The JDK provides two clocks: + *

  • one in nanoseconds, for precision, but it can only use to measure durations + *
  • one in milliseconds, for accuracy, useful to provide epoch time + * + *

    + * + *

    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. + * + *

    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. + * + *

    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()); + } +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Span.java b/dd-trace/src/main/java/datadog/trace/tracer/Span.java new file mode 100644 index 0000000000..56db15c864 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/Span.java @@ -0,0 +1,125 @@ +package datadog.trace.tracer; + +/** + * A single measurement of time with arbitrary key-value attributes. + * + *

    All spans are thread safe. + * + *

    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. + * + *

    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. + * + *

    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); + } +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/SpanContext.java b/dd-trace/src/main/java/datadog/trace/tracer/SpanContext.java new file mode 100644 index 0000000000..d3ad805ef1 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/SpanContext.java @@ -0,0 +1,39 @@ +package datadog.trace.tracer; + +/** + * All attributes of a {@link Span} which propagate for distributed tracing. + * + *

    All Spans must have a SpanContext, but not all SpanContexts require a span. + * + *

    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(); +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Trace.java b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java new file mode 100644 index 0000000000..6d2c2fcfee --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java @@ -0,0 +1,54 @@ +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. + * + *

    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. + * + *

    Has no effect after the first invocation. + */ + void close(); + } +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java new file mode 100644 index 0000000000..a9b2b6e2b7 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java @@ -0,0 +1,25 @@ +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 void inject(final SpanContext spanContext, final Object format, final T carrier) {} + + public SpanContext extract(final Object format, final T carrier) { + return null; + } +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java new file mode 100644 index 0000000000..c002baf25e --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java @@ -0,0 +1,128 @@ +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 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; + } +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java new file mode 100644 index 0000000000..a978acb3bc --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java @@ -0,0 +1,30 @@ +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. + * + *

    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). + * + *

    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. + * + *

    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 spans = null; +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/sampling/Sampler.java b/dd-trace/src/main/java/datadog/trace/tracer/sampling/Sampler.java new file mode 100644 index 0000000000..3479ef9c51 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/sampling/Sampler.java @@ -0,0 +1,19 @@ +package datadog.trace.tracer.sampling; + +import datadog.trace.tracer.Trace; + +/** + * Keeps or discards traces. + * + *

    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); +} diff --git a/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java b/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java new file mode 100644 index 0000000000..996104a1c6 --- /dev/null +++ b/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java @@ -0,0 +1,30 @@ +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 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(); +} From 9cc2e58a2650b034d1288bd206f8464ca2c59636 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 6 Dec 2018 09:15:48 -0800 Subject: [PATCH 3/4] Require all span and trace creation through Tracer --- .../java/datadog/trace/tracer/ext/Scope.java | 4 +-- .../trace/tracer/ext/TracerContext.java | 4 +-- .../main/java/datadog/trace/tracer/Span.java | 2 +- .../trace/tracer/{impl => }/SpanImpl.java | 14 ++++----- .../main/java/datadog/trace/tracer/Trace.java | 2 ++ .../trace/tracer/{impl => }/TraceImpl.java | 10 +++++-- .../java/datadog/trace/tracer/Tracer.java | 29 ++++++++++++++++--- 7 files changed, 46 insertions(+), 19 deletions(-) rename dd-trace/src/main/java/datadog/trace/tracer/{impl => }/SpanImpl.java (86%) rename dd-trace/src/main/java/datadog/trace/tracer/{impl => }/TraceImpl.java (87%) diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java index 4b07974aa3..580adc2936 100644 --- a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java @@ -4,13 +4,13 @@ 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 + * A scope holds a single span or trace continuation and may optionally finish its span or * continuation. * *

    To create a scope, see {@link TracerContext#pushScope(Span, boolean)} and {@link * TracerContext#pushScope(Trace.Continuation, boolean)}. * - *

    All created scopes must be closed with {@link } + *

    All created scopes must be closed with {@link Scope#close()} */ public interface Scope { /** Get the span held by this scope. */ diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java index ce31b8bed7..4179433d1d 100644 --- a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java @@ -81,8 +81,8 @@ public final class TracerContext { */ 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 The scope on the top of this scope-stack or null if there is no active scope. */ + public Scope peekScope() { return null; } } diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Span.java b/dd-trace/src/main/java/datadog/trace/tracer/Span.java index 56db15c864..0d2bd73056 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Span.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Span.java @@ -5,7 +5,7 @@ package datadog.trace.tracer; * *

    All spans are thread safe. * - *

    Spans may not be constructed individually, but created through a {@link Trace} + *

    To create a Span, see {@link Tracer#buildTrace()} */ public interface Span { diff --git a/dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java similarity index 86% rename from dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java rename to dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java index c002baf25e..fa4f622519 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/impl/SpanImpl.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java @@ -1,9 +1,5 @@ -package datadog.trace.tracer.impl; +package datadog.trace.tracer; -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 { @@ -33,17 +29,19 @@ public class SpanImpl implements Span { /** * Create a span with a start time of the current timestamp. * + * @param parentContext identifies the parent of this span. May be null. * @param clock The clock to use to measure the span's duration. */ - SpanImpl(final Clock clock) {} + SpanImpl(final SpanContext parentContext, final Clock clock) {} /** * Create a span with the a specific start timestamp. * + * @param parentContext identifies the parent of this span. May be null. * @param clock The clock to use to measure the span's duration. - * @param startTimeNanoseconds Epoch time in nanoseconds when this span started. + * @param startTimestampNanoseconds Epoch time in nanoseconds when this span started. */ - SpanImpl(final Clock clock, final long startTimeNanoseconds) {} + SpanImpl(final SpanContext parentContext, final Clock clock, final long startTimestampNanoseconds) {} @Override public Trace getTrace() { diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Trace.java b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java index 6d2c2fcfee..1b764ffd07 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Trace.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java @@ -6,6 +6,8 @@ package datadog.trace.tracer; * *

    A trace will be written when all of its spans are finished and all trace continuations are * closed. + * + *

    To create a Trace, see {@link Tracer#buildTrace()} */ public interface Trace { /** Get the tracer which created this trace. */ diff --git a/dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java similarity index 87% rename from dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java rename to dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java index a978acb3bc..3295a58a8c 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/impl/TraceImpl.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java @@ -1,6 +1,5 @@ -package datadog.trace.tracer.impl; +package datadog.trace.tracer; -import datadog.trace.tracer.Span; import datadog.trace.tracer.writer.Writer; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -27,4 +26,11 @@ public class TraceImpl { * relying on getRootSpan() will break. */ private final List spans = null; + + /** + * Create a new Trace. + * + * @param tracer the Tracer to apply settings from. + */ + TraceImpl(Tracer tracer) {} } diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java index a9b2b6e2b7..db363a2ff4 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java @@ -7,13 +7,34 @@ 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; + private final String serviceName = null; /** Writer is an charge of reporting traces and spans to the desired endpoint */ - final Writer writer = null; + private final Writer writer = null; /** Sampler defines the sampling policy in order to reduce the number of traces for instance */ - final Sampler sampler = null; + private final Sampler sampler = null; /** Settings for this tracer. */ - final Config config = null; + private final Config config = null; + /** The clock to use for tracing. */ + private final Clock clock = null; + + /** + * Construct a new trace using this tracer's settings and return the root span. + * + * @return The root span of the new trace. + */ + public Span buildTrace() { + return null; + } + + /** + * Construct a new trace using this tracer's settings and return the root span. + * + * @param rootSpanStartTimestampNanoseconds Epoch time in nanoseconds when the root span started. + * @return The root span of the new trace. + */ + public Span buildTrace(final long rootSpanStartTimestampNanoseconds) { + return null; + } // TODO: doc inject and extract From f498a3ac5a2bff1bee646190a607632ba462664c Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 6 Dec 2018 17:07:45 -0800 Subject: [PATCH 4/4] Changes from sync --- .../datadog/trace/tracer/ext/Examples.java | 76 +++++++++++++++++++ .../java/datadog/trace/tracer/ext/Scope.java | 8 +- .../trace/tracer/ext/TracerContext.java | 6 +- dd-trace/dd-trace.gradle | 2 - .../main/java/datadog/trace/tracer/Span.java | 14 +--- .../java/datadog/trace/tracer/SpanImpl.java | 26 +++++-- .../main/java/datadog/trace/tracer/Trace.java | 5 +- .../java/datadog/trace/tracer/TraceImpl.java | 62 +++++++++------ .../java/datadog/trace/tracer/Tracer.java | 9 ++- .../datadog/trace/tracer/writer/Writer.java | 11 +-- 10 files changed, 157 insertions(+), 62 deletions(-) create mode 100644 dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Examples.java diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Examples.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Examples.java new file mode 100644 index 0000000000..2e1603d710 --- /dev/null +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Examples.java @@ -0,0 +1,76 @@ +package datadog.trace.tracer.ext; + +import datadog.trace.tracer.Span; +import datadog.trace.tracer.Trace; +import datadog.trace.tracer.Tracer; + +// Keeping in PR for potential discussions. Will eventually remove. +// TODO: remove +class Examples { + private Examples() {} + + public static void test() { + final Throwable someThrowable = null; + // registration + TracerContext.registerGlobalContext(new TracerContext(new Tracer()), false); + + // scope + final TracerContext ctx = TracerContext.getGlobalContext(); + // without try-with-resources + { + Span rootSpan = ctx.getTracer().buildTrace(null); + final Scope scope = ctx.pushScope(rootSpan); + rootSpan.setError(true); + rootSpan.attachThrowable(someThrowable); + scope.close(); + rootSpan.finish(); + } + + /* + // with try-with-resources finishOnClose=true + { + Span rootSpan = ctx.getTracer().buildTrace(null); + try (Scope scope = ctx.pushScope(rootSpan)) { + try { + // the body + } catch (Throwable t) { + rootSpan.setError(true); + rootSpan.attachThrowable(t); + throw t; + } + } + } + */ + + // with try-with-resources finishOnClose=false + { + Span rootSpan = ctx.getTracer().buildTrace(null); + try (Scope scope = ctx.pushScope(rootSpan)) { + // the body + } catch (Throwable t) { + rootSpan.setError(true); + rootSpan.attachThrowable(t); + throw t; + } finally { + rootSpan.finish(); + } + } + + // continuations + { + Span rootSpan = ctx.getTracer().buildTrace(null); + final Trace.Continuation cont = rootSpan.getTrace().createContinuation(rootSpan); + { // on another thread + final Span parent = cont.span(); + try { + // body + } finally { + cont.close(); + } + } + } + + // create a span as a child of the currently active span + Span childSpan = ctx.peekScope().span().getTrace().createSpan(ctx.peekScope().span()); + } +} diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java index 580adc2936..2694822929 100644 --- a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/Scope.java @@ -7,12 +7,12 @@ import datadog.trace.tracer.Trace; * A scope holds a single span or trace continuation and may optionally finish its span or * continuation. * - *

    To create a scope, see {@link TracerContext#pushScope(Span, boolean)} and {@link - * TracerContext#pushScope(Trace.Continuation, boolean)}. + *

    To create a scope, see {@link TracerContext#pushScope(Span)} and {@link + * TracerContext#pushScope(Trace.Continuation)}. * *

    All created scopes must be closed with {@link Scope#close()} */ -public interface Scope { +public interface Scope extends AutoCloseable { /** Get the span held by this scope. */ Span span(); @@ -20,7 +20,7 @@ public interface Scope { * Close this scope. This method must be invoked on all created scopes. * *

    Attempting to close a scope which is not on the top of its TracerContext's scope-stack is an - * error. See {@link TracerContext#topOfScopeStack()}. + * error. See {@link TracerContext#peekScope()}. */ void close(); } diff --git a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java index 4179433d1d..a14cfdfe64 100644 --- a/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java +++ b/dd-trace-ext/src/main/java/datadog/trace/tracer/ext/TracerContext.java @@ -53,10 +53,9 @@ public final class TracerContext { * 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) { + public Scope pushScope(Span span) { return null; } @@ -65,10 +64,9 @@ public final class TracerContext { * span. * * @param continuation - * @param closeContinuationOnScopeClose * @return */ - public Scope pushScope(Trace.Continuation continuation, boolean closeContinuationOnScopeClose) { + public Scope pushScope(Trace.Continuation continuation) { return null; } diff --git a/dd-trace/dd-trace.gradle b/dd-trace/dd-trace.gradle index e41c60d76b..64f4a8f769 100644 --- a/dd-trace/dd-trace.gradle +++ b/dd-trace/dd-trace.gradle @@ -1,8 +1,6 @@ description = 'dd-trace' apply from: "${rootDir}/gradle/java.gradle" -// TODO: Move over any special setups from dd-trace-ot (e.g. trace-agent integration tests). - dependencies { annotationProcessor deps.autoservice implementation deps.autoservice diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Span.java b/dd-trace/src/main/java/datadog/trace/tracer/Span.java index 0d2bd73056..ff41aee958 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Span.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Span.java @@ -28,13 +28,6 @@ public interface Span { /** 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. * @@ -88,7 +81,7 @@ public interface Span { boolean isErrored(); /** Attach a throwable to this span. */ - void attachThrowable(); + void attachThrowable(Throwable t); /** * Mark the span as having an error. @@ -101,10 +94,9 @@ public interface Span { // does not implement. /** - * A LifecycleInterceptor allows adding hooks to particular events between a span starting and - * finishing. + * A Interceptor allows adding hooks to particular events between a span starting and finishing. */ - interface LifecycleInterceptor { + interface Interceptor { /** * Called after a span is started. * diff --git a/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java index fa4f622519..339ad0c8a8 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/SpanImpl.java @@ -1,6 +1,8 @@ package datadog.trace.tracer; +import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; public class SpanImpl implements Span { private final Clock clock = null; @@ -17,7 +19,7 @@ public class SpanImpl implements Span { /* 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; + private final AtomicLong durationNano = new AtomicLong(-1); // optional attributes to report to datadog /* The type of the span (web, db, etc). See DDSpanTypes. */ private final String type = null; @@ -29,19 +31,32 @@ public class SpanImpl implements Span { /** * Create a span with a start time of the current timestamp. * + * @param trace The trace to associate this span with * @param parentContext identifies the parent of this span. May be null. * @param clock The clock to use to measure the span's duration. + * @param interceptors interceptors to run on the span */ - SpanImpl(final SpanContext parentContext, final Clock clock) {} + SpanImpl( + final TraceImpl trace, + final SpanContext parentContext, + final Clock clock, + List interceptors) {} /** * Create a span with the a specific start timestamp. * + * @param trace The trace to associate this span with * @param parentContext identifies the parent of this span. May be null. * @param clock The clock to use to measure the span's duration. + * @param interceptors interceptors to run on the span * @param startTimestampNanoseconds Epoch time in nanoseconds when this span started. */ - SpanImpl(final SpanContext parentContext, final Clock clock, final long startTimestampNanoseconds) {} + SpanImpl( + final TraceImpl trace, + final SpanContext parentContext, + final Clock clock, + List interceptors, + final long startTimestampNanoseconds) {} @Override public Trace getTrace() { @@ -60,14 +75,11 @@ public class SpanImpl implements Span { } @Override - public void attachThrowable() {} + public void attachThrowable(Throwable t) {} @Override public void setError(boolean isErrored) {} - @Override - public void addInterceptor(LifecycleInterceptor interceptor) {} - @Override public SpanContext getContext() { return null; diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Trace.java b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java index 1b764ffd07..0137fdb8b5 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Trace.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Trace.java @@ -34,7 +34,7 @@ public interface Trace { */ Continuation createContinuation(Span parentSpan); - interface LifecycleInterceptor { + interface Interceptor { /** * Invoked when a trace is eligible for writing but hasn't been handed off to its writer yet. * @@ -52,5 +52,8 @@ public interface Trace { *

    Has no effect after the first invocation. */ void close(); + + // TODO: doc + Span span(); } } diff --git a/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java b/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java index 3295a58a8c..766a51fa95 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/TraceImpl.java @@ -1,36 +1,54 @@ package datadog.trace.tracer; import datadog.trace.tracer.writer.Writer; +import java.lang.ref.WeakReference; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Set; -public class TraceImpl { +public class TraceImpl implements Trace { private final Writer writer = null; - /** - * Count of all unfinished spans and continuations. - * - *

    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). - * - *

    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. - * - *

    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 spans = null; + // TODO: Document approach to weak-referencdes and cleanup. If a span has to be closed by our GC + // logic the trace should increment the writer's count but not report (invalid api usage produces + // suspect data). + private final Set> inFlightSpans = null; + private final Set> inFlightContinuations = null; + + /** Strong refs to spans which are closed */ + private final List finishedSpans = null; + + private final Span rootSpan = null; /** * Create a new Trace. * * @param tracer the Tracer to apply settings from. */ - TraceImpl(Tracer tracer) {} + TraceImpl( + Tracer tracer, + SpanContext rootSpanParentContext, + final long rootSpanStartTimestampNanoseconds) {} + + @Override + public Tracer getTracer() { + return null; + } + + @Override + public Span getRootSpan() { + return null; + } + + @Override + public Span createSpan(Span parentSpan) { + return null; + } + + @Override + public Continuation createContinuation(Span parentSpan) { + return null; + } + + // TODO methods to inform the trace that continuations and spans finished/closed. Also be able to + // inform trace when a span finishes due to GC. } diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java index db363a2ff4..0eeb0d0553 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java @@ -7,7 +7,7 @@ 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 */ - private final String serviceName = null; + private final String defaultServiceName = null; /** Writer is an charge of reporting traces and spans to the desired endpoint */ private final Writer writer = null; /** Sampler defines the sampling policy in order to reduce the number of traces for instance */ @@ -22,7 +22,7 @@ public class Tracer { * * @return The root span of the new trace. */ - public Span buildTrace() { + public Span buildTrace(final SpanContext parentContext) { return null; } @@ -32,12 +32,13 @@ public class Tracer { * @param rootSpanStartTimestampNanoseconds Epoch time in nanoseconds when the root span started. * @return The root span of the new trace. */ - public Span buildTrace(final long rootSpanStartTimestampNanoseconds) { + public Span buildTrace( + final SpanContext parentContext, final long rootSpanStartTimestampNanoseconds) { return null; } // TODO: doc inject and extract - + // TODO: inject and extract helpers on span context? public void inject(final SpanContext spanContext, final Object format, final T carrier) {} public SpanContext extract(final Object format, final T carrier) { diff --git a/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java b/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java index 996104a1c6..b10ded6791 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/writer/Writer.java @@ -1,23 +1,20 @@ 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 + * @param trace the trace to write */ - void write(List traces); + void write(Trace trace); /** - * 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. + * Inform the writer that a trace occurred but will not be written. Used by tracer-side sampling. */ - void incrementTraceCount(int numTraces); + void incrementTraceCount(); /** Start the writer */ void start();