1 /*
2  * Copyright 2017, OpenCensus Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.opencensus.trace;
18 
19 import com.google.errorprone.annotations.MustBeClosed;
20 import io.opencensus.common.Scope;
21 import io.opencensus.internal.Utils;
22 import java.util.List;
23 import java.util.concurrent.Callable;
24 import javax.annotation.Nullable;
25 
26 /**
27  * {@link SpanBuilder} is used to construct {@link Span} instances which define arbitrary scopes of
28  * code that are sampled for distributed tracing as a single atomic unit.
29  *
30  * <p>This is a simple example where all the work is being done within a single scope and a single
31  * thread and the Context is automatically propagated:
32  *
33  * <pre>{@code
34  * class MyClass {
35  *   private static final Tracer tracer = Tracing.getTracer();
36  *   void doWork {
37  *     // Create a Span as a child of the current Span.
38  *     try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
39  *       tracer.getCurrentSpan().addAnnotation("my annotation");
40  *       doSomeWork();  // Here the new span is in the current Context, so it can be used
41  *                      // implicitly anywhere down the stack.
42  *     }
43  *   }
44  * }
45  * }</pre>
46  *
47  * <p>There might be cases where you do not perform all the work inside one static scope and the
48  * Context is automatically propagated:
49  *
50  * <pre>{@code
51  * class MyRpcServerInterceptorListener implements RpcServerInterceptor.Listener {
52  *   private static final Tracer tracer = Tracing.getTracer();
53  *   private Span mySpan;
54  *
55  *   public MyRpcInterceptor() {}
56  *
57  *   public void onRequest(String rpcName, Metadata metadata) {
58  *     // Create a Span as a child of the remote Span.
59  *     mySpan = tracer.spanBuilderWithRemoteParent(
60  *         getTraceContextFromMetadata(metadata), rpcName).startSpan();
61  *   }
62  *
63  *   public void onExecuteHandler(ServerCallHandler serverCallHandler) {
64  *     try (Scope ws = tracer.withSpan(mySpan)) {
65  *       tracer.getCurrentSpan().addAnnotation("Start rpc execution.");
66  *       serverCallHandler.run();  // Here the new span is in the current Context, so it can be
67  *                                 // used implicitly anywhere down the stack.
68  *     }
69  *   }
70  *
71  *   // Called when the RPC is canceled and guaranteed onComplete will not be called.
72  *   public void onCancel() {
73  *     // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
74  *     mySpan.end(EndSpanOptions.builder().setStatus(Status.CANCELLED));
75  *   }
76  *
77  *   // Called when the RPC is done and guaranteed onCancel will not be called.
78  *   public void onComplete(RpcStatus rpcStatus) {
79  *     // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
80  *     mySpan.end(EndSpanOptions.builder().setStatus(rpcStatusToCanonicalTraceStatus(status));
81  *   }
82  * }
83  * }</pre>
84  *
85  * <p>This is a simple example where all the work is being done within a single scope and the
86  * Context is manually propagated:
87  *
88  * <pre>{@code
89  * class MyClass {
90  *   private static final Tracer tracer = Tracing.getTracer();
91  *   void DoWork(Span parent) {
92  *     Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
93  *     childSpan.addAnnotation("my annotation");
94  *     try {
95  *       doSomeWork(childSpan); // Manually propagate the new span down the stack.
96  *     } finally {
97  *       // To make sure we end the span even in case of an exception.
98  *       childSpan.end();  // Manually end the span.
99  *     }
100  *   }
101  * }
102  * }</pre>
103  *
104  * <p>If your Java version is less than Java SE 7, see {@link SpanBuilder#startSpan} and {@link
105  * SpanBuilder#startScopedSpan} for usage examples.
106  *
107  * @since 0.5
108  */
109 public abstract class SpanBuilder {
110 
111   /**
112    * Sets the {@link Sampler} to use. If not set, the implementation will provide a default.
113    *
114    * @param sampler the {@code Sampler} to use when determining sampling for a {@code Span}.
115    * @return this.
116    * @since 0.5
117    */
setSampler(Sampler sampler)118   public abstract SpanBuilder setSampler(Sampler sampler);
119 
120   /**
121    * Sets the {@code List} of parent links. Links are used to link {@link Span}s in different
122    * traces. Used (for example) in batching operations, where a single batch handler processes
123    * multiple requests from different traces.
124    *
125    * @param parentLinks new links to be added.
126    * @return this.
127    * @throws NullPointerException if {@code parentLinks} is {@code null}.
128    * @since 0.5
129    */
setParentLinks(List<Span> parentLinks)130   public abstract SpanBuilder setParentLinks(List<Span> parentLinks);
131 
132   /**
133    * Sets the option {@link Span.Options#RECORD_EVENTS} for the newly created {@code Span}. If not
134    * called, the implementation will provide a default.
135    *
136    * @param recordEvents new value determining if this {@code Span} should have events recorded.
137    * @return this.
138    * @since 0.5
139    */
setRecordEvents(boolean recordEvents)140   public abstract SpanBuilder setRecordEvents(boolean recordEvents);
141 
142   /**
143    * Sets the {@link Span.Kind} for the newly created {@code Span}. If not called, the
144    * implementation will provide a default.
145    *
146    * @param spanKind the kind of the newly created {@code Span}.
147    * @return this.
148    * @since 0.14
149    */
setSpanKind(@ullable Span.Kind spanKind)150   public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
151     return this;
152   }
153 
154   /**
155    * Starts a new {@link Span}.
156    *
157    * <p>Users <b>must</b> manually call {@link Span#end()} or {@link Span#end(EndSpanOptions)} to
158    * end this {@code Span}.
159    *
160    * <p>Does not install the newly created {@code Span} to the current Context.
161    *
162    * <p>Example of usage:
163    *
164    * <pre>{@code
165    * class MyClass {
166    *   private static final Tracer tracer = Tracing.getTracer();
167    *   void DoWork(Span parent) {
168    *     Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
169    *     childSpan.addAnnotation("my annotation");
170    *     try {
171    *       doSomeWork(childSpan); // Manually propagate the new span down the stack.
172    *     } finally {
173    *       // To make sure we end the span even in case of an exception.
174    *       childSpan.end();  // Manually end the span.
175    *     }
176    *   }
177    * }
178    * }</pre>
179    *
180    * @return the newly created {@code Span}.
181    * @since 0.5
182    */
startSpan()183   public abstract Span startSpan();
184 
185   /**
186    * Starts a new span and sets it as the {@link Tracer#getCurrentSpan current span}.
187    *
188    * <p>Enters the scope of code where the newly created {@code Span} is in the current Context, and
189    * returns an object that represents that scope. When the returned object is closed, the scope is
190    * exited, the previous Context is restored, and the newly created {@code Span} is ended using
191    * {@link Span#end}.
192    *
193    * <p>Supports try-with-resource idiom.
194    *
195    * <p>Example of usage:
196    *
197    * <pre>{@code
198    * class MyClass {
199    *   private static final Tracer tracer = Tracing.getTracer();
200    *   void doWork {
201    *     // Create a Span as a child of the current Span.
202    *     try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
203    *       tracer.getCurrentSpan().addAnnotation("my annotation");
204    *       doSomeWork();  // Here the new span is in the current Context, so it can be used
205    *                      // implicitly anywhere down the stack. Anytime in this closure the span
206    *                      // can be accessed via tracer.getCurrentSpan().
207    *     }
208    *   }
209    * }
210    * }</pre>
211    *
212    * <p>Prior to Java SE 7, you can use a finally block to ensure that a resource is closed (the
213    * {@code Span} is ended and removed from the Context) regardless of whether the try statement
214    * completes normally or abruptly.
215    *
216    * <p>Example of usage prior to Java SE7:
217    *
218    * <pre>{@code
219    * class MyClass {
220    *   private static Tracer tracer = Tracing.getTracer();
221    *   void doWork {
222    *     // Create a Span as a child of the current Span.
223    *     Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan();
224    *     try {
225    *       tracer.getCurrentSpan().addAnnotation("my annotation");
226    *       doSomeWork();  // Here the new span is in the current Context, so it can be used
227    *                      // implicitly anywhere down the stack. Anytime in this closure the span
228    *                      // can be accessed via tracer.getCurrentSpan().
229    *     } finally {
230    *       ss.close();
231    *     }
232    *   }
233    * }
234    * }</pre>
235    *
236    * <p>WARNING: The try-with-resources feature to auto-close spans as described above can sound
237    * very tempting due to its convenience, but it comes with an important and easy-to-miss
238    * trade-off: the span will be closed before any {@code catch} or {@code finally} blocks get a
239    * chance to execute. So if you need to catch any exceptions and log information about them (for
240    * example), then you do not want to use the try-with-resources shortcut because that logging will
241    * not be tagged with the span info of the span it logically falls under, and if you try to
242    * retrieve {@code Tracer.getCurrentSpan()} then you'll either get the parent span if one exists
243    * or {@code BlankSpan} if there was no parent span. This can be confusing and seem
244    * counter-intuitive, but it's the way try-with-resources works.
245    *
246    * @return an object that defines a scope where the newly created {@code Span} will be set to the
247    *     current Context.
248    * @since 0.5
249    */
250   @MustBeClosed
startScopedSpan()251   public final Scope startScopedSpan() {
252     return CurrentSpanUtils.withSpan(startSpan(), /* endSpan= */ true);
253   }
254 
255   /**
256    * Starts a new span and runs the given {@code Runnable} with the newly created {@code Span} as
257    * the current {@code Span}, and ends the {@code Span} after the {@code Runnable} is run.
258    *
259    * <p>Any error will end up as a {@link Status#UNKNOWN}.
260    *
261    * <pre><code>
262    * tracer.spanBuilder("MyRunnableSpan").startSpanAndRun(myRunnable);
263    * </code></pre>
264    *
265    * <p>It is equivalent with the following code:
266    *
267    * <pre><code>
268    * Span span = tracer.spanBuilder("MyRunnableSpan").startSpan();
269    * Runnable newRunnable = tracer.withSpan(span, myRunnable);
270    * try {
271    *   newRunnable.run();
272    * } finally {
273    *   span.end();
274    * }
275    * </code></pre>
276    *
277    * @param runnable the {@code Runnable} to run in the {@code Span}.
278    * @since 0.11.0
279    */
startSpanAndRun(final Runnable runnable)280   public final void startSpanAndRun(final Runnable runnable) {
281     final Span span = startSpan();
282     CurrentSpanUtils.withSpan(span, /* endSpan= */ true, runnable).run();
283   }
284 
285   /**
286    * Starts a new span and calls the given {@code Callable} with the newly created {@code Span} as
287    * the current {@code Span}, and ends the {@code Span} after the {@code Callable} is called.
288    *
289    * <p>Any error will end up as a {@link Status#UNKNOWN}.
290    *
291    * <pre><code>
292    * MyResult myResult = tracer.spanBuilder("MyCallableSpan").startSpanAndCall(myCallable);
293    * </code></pre>
294    *
295    * <p>It is equivalent with the following code:
296    *
297    * <pre><code>
298    * Span span = tracer.spanBuilder("MyCallableSpan").startSpan();
299    * {@code Callable<MyResult>} newCallable = tracer.withSpan(span, myCallable);
300    * MyResult myResult = null;
301    * try {
302    *   myResult = newCallable.call();
303    * } finally {
304    *   span.end();
305    * }
306    * );
307    * </code></pre>
308    *
309    * @param callable the {@code Callable} to run in the {@code Span}.
310    * @since 0.11.0
311    */
startSpanAndCall(Callable<V> callable)312   public final <V> V startSpanAndCall(Callable<V> callable) throws Exception {
313     final Span span = startSpan();
314     return CurrentSpanUtils.withSpan(span, /* endSpan= */ true, callable).call();
315   }
316 
317   static final class NoopSpanBuilder extends SpanBuilder {
createWithParent(String spanName, @Nullable Span parent)318     static NoopSpanBuilder createWithParent(String spanName, @Nullable Span parent) {
319       return new NoopSpanBuilder(spanName);
320     }
321 
createWithRemoteParent( String spanName, @Nullable SpanContext remoteParentSpanContext)322     static NoopSpanBuilder createWithRemoteParent(
323         String spanName, @Nullable SpanContext remoteParentSpanContext) {
324       return new NoopSpanBuilder(spanName);
325     }
326 
327     @Override
startSpan()328     public Span startSpan() {
329       return BlankSpan.INSTANCE;
330     }
331 
332     @Override
setSampler(@ullable Sampler sampler)333     public SpanBuilder setSampler(@Nullable Sampler sampler) {
334       return this;
335     }
336 
337     @Override
setParentLinks(List<Span> parentLinks)338     public SpanBuilder setParentLinks(List<Span> parentLinks) {
339       return this;
340     }
341 
342     @Override
setRecordEvents(boolean recordEvents)343     public SpanBuilder setRecordEvents(boolean recordEvents) {
344       return this;
345     }
346 
347     @Override
setSpanKind(@ullable Span.Kind spanKind)348     public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
349       return this;
350     }
351 
NoopSpanBuilder(String name)352     private NoopSpanBuilder(String name) {
353       Utils.checkNotNull(name, "name");
354     }
355   }
356 }
357