aboutsummaryrefslogtreecommitdiff
path: root/api/src/main/java/io/opencensus/trace/SpanBuilder.java
blob: 1a4e29e9e50a01007f7274535bee90245af5c9f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*
 * 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>
   *
   * @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");
    }
  }
}