aboutsummaryrefslogtreecommitdiff
path: root/api/src/main/java/io/opencensus/trace/SpanBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'api/src/main/java/io/opencensus/trace/SpanBuilder.java')
-rw-r--r--api/src/main/java/io/opencensus/trace/SpanBuilder.java356
1 files changed, 356 insertions, 0 deletions
diff --git a/api/src/main/java/io/opencensus/trace/SpanBuilder.java b/api/src/main/java/io/opencensus/trace/SpanBuilder.java
new file mode 100644
index 00000000..f3a436a6
--- /dev/null
+++ b/api/src/main/java/io/opencensus/trace/SpanBuilder.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2017, OpenCensus Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.opencensus.trace;
+
+import com.google.errorprone.annotations.MustBeClosed;
+import io.opencensus.common.Scope;
+import io.opencensus.internal.Utils;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.Nullable;
+
+/**
+ * {@link SpanBuilder} is used to construct {@link Span} instances which define arbitrary scopes of
+ * code that are sampled for distributed tracing as a single atomic unit.
+ *
+ * <p>This is a simple example where all the work is being done within a single scope and a single
+ * thread and the Context is automatically propagated:
+ *
+ * <pre>{@code
+ * class MyClass {
+ * private static final Tracer tracer = Tracing.getTracer();
+ * void doWork {
+ * // Create a Span as a child of the current Span.
+ * try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
+ * tracer.getCurrentSpan().addAnnotation("my annotation");
+ * doSomeWork(); // Here the new span is in the current Context, so it can be used
+ * // implicitly anywhere down the stack.
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>There might be cases where you do not perform all the work inside one static scope and the
+ * Context is automatically propagated:
+ *
+ * <pre>{@code
+ * class MyRpcServerInterceptorListener implements RpcServerInterceptor.Listener {
+ * private static final Tracer tracer = Tracing.getTracer();
+ * private Span mySpan;
+ *
+ * public MyRpcInterceptor() {}
+ *
+ * public void onRequest(String rpcName, Metadata metadata) {
+ * // Create a Span as a child of the remote Span.
+ * mySpan = tracer.spanBuilderWithRemoteParent(
+ * getTraceContextFromMetadata(metadata), rpcName).startSpan();
+ * }
+ *
+ * public void onExecuteHandler(ServerCallHandler serverCallHandler) {
+ * try (Scope ws = tracer.withSpan(mySpan)) {
+ * tracer.getCurrentSpan().addAnnotation("Start rpc execution.");
+ * serverCallHandler.run(); // Here the new span is in the current Context, so it can be
+ * // used implicitly anywhere down the stack.
+ * }
+ * }
+ *
+ * // Called when the RPC is canceled and guaranteed onComplete will not be called.
+ * public void onCancel() {
+ * // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
+ * mySpan.end(EndSpanOptions.builder().setStatus(Status.CANCELLED));
+ * }
+ *
+ * // Called when the RPC is done and guaranteed onCancel will not be called.
+ * public void onComplete(RpcStatus rpcStatus) {
+ * // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
+ * mySpan.end(EndSpanOptions.builder().setStatus(rpcStatusToCanonicalTraceStatus(status));
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>This is a simple example where all the work is being done within a single scope and the
+ * Context is manually propagated:
+ *
+ * <pre>{@code
+ * class MyClass {
+ * private static final Tracer tracer = Tracing.getTracer();
+ * void DoWork(Span parent) {
+ * Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
+ * childSpan.addAnnotation("my annotation");
+ * try {
+ * doSomeWork(childSpan); // Manually propagate the new span down the stack.
+ * } finally {
+ * // To make sure we end the span even in case of an exception.
+ * childSpan.end(); // Manually end the span.
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>If your Java version is less than Java SE 7, see {@link SpanBuilder#startSpan} and {@link
+ * SpanBuilder#startScopedSpan} for usage examples.
+ *
+ * @since 0.5
+ */
+public abstract class SpanBuilder {
+
+ /**
+ * Sets the {@link Sampler} to use. If not set, the implementation will provide a default.
+ *
+ * @param sampler the {@code Sampler} to use when determining sampling for a {@code Span}.
+ * @return this.
+ * @since 0.5
+ */
+ public abstract SpanBuilder setSampler(Sampler sampler);
+
+ /**
+ * Sets the {@code List} of parent links. Links are used to link {@link Span}s in different
+ * traces. Used (for example) in batching operations, where a single batch handler processes
+ * multiple requests from different traces.
+ *
+ * @param parentLinks new links to be added.
+ * @return this.
+ * @throws NullPointerException if {@code parentLinks} is {@code null}.
+ * @since 0.5
+ */
+ public abstract SpanBuilder setParentLinks(List<Span> parentLinks);
+
+ /**
+ * Sets the option {@link Span.Options#RECORD_EVENTS} for the newly created {@code Span}. If not
+ * called, the implementation will provide a default.
+ *
+ * @param recordEvents new value determining if this {@code Span} should have events recorded.
+ * @return this.
+ * @since 0.5
+ */
+ public abstract SpanBuilder setRecordEvents(boolean recordEvents);
+
+ /**
+ * Sets the {@link Span.Kind} for the newly created {@code Span}. If not called, the
+ * implementation will provide a default.
+ *
+ * @param spanKind the kind of the newly created {@code Span}.
+ * @return this.
+ * @since 0.14
+ */
+ public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
+ return this;
+ }
+
+ /**
+ * Starts a new {@link Span}.
+ *
+ * <p>Users <b>must</b> manually call {@link Span#end()} or {@link Span#end(EndSpanOptions)} to
+ * end this {@code Span}.
+ *
+ * <p>Does not install the newly created {@code Span} to the current Context.
+ *
+ * <p>Example of usage:
+ *
+ * <pre>{@code
+ * class MyClass {
+ * private static final Tracer tracer = Tracing.getTracer();
+ * void DoWork(Span parent) {
+ * Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
+ * childSpan.addAnnotation("my annotation");
+ * try {
+ * doSomeWork(childSpan); // Manually propagate the new span down the stack.
+ * } finally {
+ * // To make sure we end the span even in case of an exception.
+ * childSpan.end(); // Manually end the span.
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * @return the newly created {@code Span}.
+ * @since 0.5
+ */
+ public abstract Span startSpan();
+
+ /**
+ * Starts a new span and sets it as the {@link Tracer#getCurrentSpan current span}.
+ *
+ * <p>Enters the scope of code where the newly created {@code Span} is in the current Context, and
+ * returns an object that represents that scope. When the returned object is closed, the scope is
+ * exited, the previous Context is restored, and the newly created {@code Span} is ended using
+ * {@link Span#end}.
+ *
+ * <p>Supports try-with-resource idiom.
+ *
+ * <p>Example of usage:
+ *
+ * <pre>{@code
+ * class MyClass {
+ * private static final Tracer tracer = Tracing.getTracer();
+ * void doWork {
+ * // Create a Span as a child of the current Span.
+ * try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
+ * tracer.getCurrentSpan().addAnnotation("my annotation");
+ * doSomeWork(); // Here the new span is in the current Context, so it can be used
+ * // implicitly anywhere down the stack. Anytime in this closure the span
+ * // can be accessed via tracer.getCurrentSpan().
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>Prior to Java SE 7, you can use a finally block to ensure that a resource is closed (the
+ * {@code Span} is ended and removed from the Context) regardless of whether the try statement
+ * completes normally or abruptly.
+ *
+ * <p>Example of usage prior to Java SE7:
+ *
+ * <pre>{@code
+ * class MyClass {
+ * private static Tracer tracer = Tracing.getTracer();
+ * void doWork {
+ * // Create a Span as a child of the current Span.
+ * Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan();
+ * try {
+ * tracer.getCurrentSpan().addAnnotation("my annotation");
+ * doSomeWork(); // Here the new span is in the current Context, so it can be used
+ * // implicitly anywhere down the stack. Anytime in this closure the span
+ * // can be accessed via tracer.getCurrentSpan().
+ * } finally {
+ * ss.close();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>WARNING: The try-with-resources feature to auto-close spans as described above can sound
+ * very tempting due to its convenience, but it comes with an important and easy-to-miss
+ * trade-off: the span will be closed before any {@code catch} or {@code finally} blocks get a
+ * chance to execute. So if you need to catch any exceptions and log information about them (for
+ * example), then you do not want to use the try-with-resources shortcut because that logging will
+ * not be tagged with the span info of the span it logically falls under, and if you try to
+ * retrieve {@code Tracer.getCurrentSpan()} then you'll either get the parent span if one exists
+ * or {@code BlankSpan} if there was no parent span. This can be confusing and seem
+ * counter-intuitive, but it's the way try-with-resources works.
+ *
+ * @return an object that defines a scope where the newly created {@code Span} will be set to the
+ * current Context.
+ * @since 0.5
+ */
+ @MustBeClosed
+ public final Scope startScopedSpan() {
+ return CurrentSpanUtils.withSpan(startSpan(), /* endSpan= */ true);
+ }
+
+ /**
+ * Starts a new span and runs the given {@code Runnable} with the newly created {@code Span} as
+ * the current {@code Span}, and ends the {@code Span} after the {@code Runnable} is run.
+ *
+ * <p>Any error will end up as a {@link Status#UNKNOWN}.
+ *
+ * <pre><code>
+ * tracer.spanBuilder("MyRunnableSpan").startSpanAndRun(myRunnable);
+ * </code></pre>
+ *
+ * <p>It is equivalent with the following code:
+ *
+ * <pre><code>
+ * Span span = tracer.spanBuilder("MyRunnableSpan").startSpan();
+ * Runnable newRunnable = tracer.withSpan(span, myRunnable);
+ * try {
+ * newRunnable.run();
+ * } finally {
+ * span.end();
+ * }
+ * </code></pre>
+ *
+ * @param runnable the {@code Runnable} to run in the {@code Span}.
+ * @since 0.11.0
+ */
+ public final void startSpanAndRun(final Runnable runnable) {
+ final Span span = startSpan();
+ CurrentSpanUtils.withSpan(span, /* endSpan= */ true, runnable).run();
+ }
+
+ /**
+ * Starts a new span and calls the given {@code Callable} with the newly created {@code Span} as
+ * the current {@code Span}, and ends the {@code Span} after the {@code Callable} is called.
+ *
+ * <p>Any error will end up as a {@link Status#UNKNOWN}.
+ *
+ * <pre><code>
+ * MyResult myResult = tracer.spanBuilder("MyCallableSpan").startSpanAndCall(myCallable);
+ * </code></pre>
+ *
+ * <p>It is equivalent with the following code:
+ *
+ * <pre><code>
+ * Span span = tracer.spanBuilder("MyCallableSpan").startSpan();
+ * {@code Callable<MyResult>} newCallable = tracer.withSpan(span, myCallable);
+ * MyResult myResult = null;
+ * try {
+ * myResult = newCallable.call();
+ * } finally {
+ * span.end();
+ * }
+ * );
+ * </code></pre>
+ *
+ * @param callable the {@code Callable} to run in the {@code Span}.
+ * @since 0.11.0
+ */
+ public final <V> V startSpanAndCall(Callable<V> callable) throws Exception {
+ final Span span = startSpan();
+ return CurrentSpanUtils.withSpan(span, /* endSpan= */ true, callable).call();
+ }
+
+ static final class NoopSpanBuilder extends SpanBuilder {
+ static NoopSpanBuilder createWithParent(String spanName, @Nullable Span parent) {
+ return new NoopSpanBuilder(spanName);
+ }
+
+ static NoopSpanBuilder createWithRemoteParent(
+ String spanName, @Nullable SpanContext remoteParentSpanContext) {
+ return new NoopSpanBuilder(spanName);
+ }
+
+ @Override
+ public Span startSpan() {
+ return BlankSpan.INSTANCE;
+ }
+
+ @Override
+ public SpanBuilder setSampler(@Nullable Sampler sampler) {
+ return this;
+ }
+
+ @Override
+ public SpanBuilder setParentLinks(List<Span> parentLinks) {
+ return this;
+ }
+
+ @Override
+ public SpanBuilder setRecordEvents(boolean recordEvents) {
+ return this;
+ }
+
+ @Override
+ public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
+ return this;
+ }
+
+ private NoopSpanBuilder(String name) {
+ Utils.checkNotNull(name, "name");
+ }
+ }
+}