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