diff options
| author | Bogdan Drutu <bdrutu@google.com> | 2017-03-07 17:49:43 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-03-07 17:49:43 -0800 |
| commit | b1d43a650e5bc36c6dfdf1dc5cb4c71bd5db00c7 (patch) | |
| tree | ca8c734b78de7d3e0d7f803f5b8c8bdf48babb7e /core | |
| parent | d5fdf57eefe0d64752a5d680744c9c47b6350524 (diff) | |
| download | platform_external_opencensus-java-b1d43a650e5bc36c6dfdf1dc5cb4c71bd5db00c7.tar.gz platform_external_opencensus-java-b1d43a650e5bc36c6dfdf1dc5cb4c71bd5db00c7.tar.bz2 platform_external_opencensus-java-b1d43a650e5bc36c6dfdf1dc5cb4c71bd5db00c7.zip | |
Add a SpanBuilder class. (#123)
* Add a SpanBuilder to avoid exponential increase in the number of startSpan[XXX] methods in the Tracer class.
* Modify examples and comments based on Adrian's feedback.
Diffstat (limited to 'core')
8 files changed, 664 insertions, 523 deletions
diff --git a/core/src/main/java/com/google/instrumentation/trace/Span.java b/core/src/main/java/com/google/instrumentation/trace/Span.java index 26de28f7..28ed14a7 100644 --- a/core/src/main/java/com/google/instrumentation/trace/Span.java +++ b/core/src/main/java/com/google/instrumentation/trace/Span.java @@ -25,7 +25,7 @@ import javax.annotation.Nullable; * An abstract class that represents a span. It has an associated {@link SpanContext} and a set of * {@link Options}. * - * <p>Spans are created by the {@link Tracer#startSpan} method. + * <p>Spans are created by the {@link SpanBuilder#startSpan} method. * * <p>{@code Span} implements NonThrowingCloseable, to support try-with-resource idiom. See {@link * Span#close}. diff --git a/core/src/main/java/com/google/instrumentation/trace/SpanBuilder.java b/core/src/main/java/com/google/instrumentation/trace/SpanBuilder.java new file mode 100644 index 00000000..25e2db56 --- /dev/null +++ b/core/src/main/java/com/google/instrumentation/trace/SpanBuilder.java @@ -0,0 +1,295 @@ +/* + * Copyright 2017, Google Inc. + * 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 com.google.instrumentation.trace; + +import com.google.instrumentation.common.NonThrowingCloseable; +import com.google.instrumentation.common.Timestamp; + +import java.util.List; +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 the + * Context is manually propagated: + * + * <pre>{@code + * class MyClass { + * private static final Tracer tracer = Tracer.getTracer(); + * void doWork(Span parent) { + * // Create a Span as a child of the given parent. + * try (Span span = tracer.spanBuilder(parent, "MyChildSpan").startSpan()) { + * span.addAnnotation("my annotation"); + * doSomeWork(span); // Manually propagate the new span down the stack. + * } + * // "span" will be ended here. + * } + * } + * }</pre> + * + * <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 = Tracer.getTracer(); + * void doWork { + * // Create a Span as a child of the current Span. + * try (NonThrowingCloseable 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 = Tracer.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 (NonThrowingCloseable 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>If your Java version is less than Java SE 7, see {@link SpanBuilder#startSpan} and {@link + * SpanBuilder#startScopedSpan} for usage examples. + */ +public final class SpanBuilder { + private final SpanFactory spanFactory; + private final ContextSpanHandler contextSpanHandler; + private SpanContext parentSpanContext; + private boolean hasRemoteParent; + private final String name; + private final StartSpanOptions startSpanOption = new StartSpanOptions(); + + SpanBuilder( + SpanFactory spanFactory, + ContextSpanHandler contextSpanHandler, + SpanContext parentSpanContext, + boolean hasRemoteParent, + String name) { + this.parentSpanContext = parentSpanContext; + this.hasRemoteParent = hasRemoteParent; + this.name = name; + this.spanFactory = spanFactory; + this.contextSpanHandler = contextSpanHandler; + } + + /** + * Sets the start time for the {@link Span}. + * + * @param startTime The start time for the {@code Span}. If {@code null} is used, then the current + * system time at the point at which {@link #startSpan} is called will be used. + * @return this. + */ + public SpanBuilder setStartTime(@Nullable Timestamp startTime) { + startSpanOption.setStartTime(startTime); + return this; + } + + /** + * Sets the {@link Sampler} to use. If a {@code null} value is passed, the implementation will + * provide a default. + * + * @param sampler The {@code Sampler} to use when determining sampling for a {@code Span}. + * @return this. + */ + public SpanBuilder setSampler(@Nullable Sampler sampler) { + startSpanOption.setSampler(sampler); + return this; + } + + /** + * 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. + */ + public SpanBuilder setParentLinks(@Nullable List<Span> parentLinks) { + startSpanOption.setParentLinks(parentLinks); + return this; + } + + /** + * Sets recordEvents. + * + * @param recordEvents New value determining if this {@code Span} should have events recorded. If + * a {@code null} value is passed, the implementation will provide a default. + * @return this. + */ + public SpanBuilder setRecordEvents(@Nullable Boolean recordEvents) { + startSpanOption.setRecordEvents(recordEvents); + return this; + } + + /** + * If called this will force the newly created {@code Span} to be a root span. As a consequence, + * any parent specified (or inherited from the Context) will be ignored (N.B. does not apply to + * linked parents set through {@link #setParentLinks}). + * + * <p>This is useful when {@link Tracer#spanBuilder(String)} is used and the newly created {@code + * Span} needs to be decoupled from the parent {@code Span}. + * + * @return this. + */ + public SpanBuilder becomeRoot() { + parentSpanContext = null; + hasRemoteParent = false; + return this; + } + + /** + * Starts a new {@link Span}. + * + * <p>If used without try-with-resources <b>must</b> manually call {@link Span#end}. + * + * <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 = Tracer.getTracer(); + * void DoWork() { + * try (Span span = tracer.spanBuilder(null, "MyRootSpan").startSpan()) { + * span.addAnnotation("We did X"); + * } + * } + * } + * }</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) regardless of whether the try statement completes normally or abruptly. + * + * <p>Example of usage prior to Java SE7: + * + * <pre>{@code + * class MyClass { + * private static final Tracer tracer = Tracer.getTracer(); + * void DoWork() { + * Span span = tracer.spanBuilder(null, "MyRootSpan").startSpan(); + * try { + * span.addAnnotation("We did X"); + * } finally { + * span.close(); + * } + * } + * } + * }</pre> + * + * @return the newly created {@code Span}. + */ + public Span startSpan() { + return start(); + } + + // TODO(bdrutu): Add error_prone annotation @MustBeClosed when the 2.0.16 jar is fixed. + /** + * Starts a new 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. The scope is exited when the returned object is + * closed then 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 = Tracer.getTracer(); + * void doWork { + * // Create a Span as a child of the current Span. + * try (NonThrowingCloseable 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 = Tracer.getTracer(); + * void doWork { + * // Create a Span as a child of the current Span. + * NonThrowingCloseable 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. + */ + public NonThrowingCloseable startScopedSpan() { + return new ScopedSpanHandle(start(), contextSpanHandler); + } + + // Utility method to start a Span. + private Span start() { + return spanFactory.startSpan(parentSpanContext, hasRemoteParent, name, startSpanOption); + } +} diff --git a/core/src/main/java/com/google/instrumentation/trace/SpanFactory.java b/core/src/main/java/com/google/instrumentation/trace/SpanFactory.java index 80f2b61d..e331d9f4 100644 --- a/core/src/main/java/com/google/instrumentation/trace/SpanFactory.java +++ b/core/src/main/java/com/google/instrumentation/trace/SpanFactory.java @@ -21,26 +21,14 @@ import javax.annotation.Nullable; abstract class SpanFactory { /** * Creates and starts a new child {@link Span} (or root if parent is {@code null}), with parent - * being the designated {@code Span} and the given options. - * - * @param parent The parent of the returned {@code Span}. - * @param name The name of the returned {@code Span}. - * @param options The options for the start of the {@code Span}. - * @return A child {@code Span} that will have the name provided. - */ - abstract Span startSpan(@Nullable Span parent, String name, StartSpanOptions options); - - /** - * Creates and starts a new child {@link Span} (or root if parent is {@code null}), with parent * being the {@code Span} designated by the {@link SpanContext} and the given options. * - * <p>This must be used to create a {@code Span} when the parent is on a different process. - * - * @param remoteParent The remote parent of the returned {@code Span}. + * @param parent The parent of the returned {@code Span}. + * @param hasRemoteParent {@code true} if this is a child of a remote {@code Span}. * @param name The name of the returned {@code Span}. * @param options The options for the start of the {@code Span}. * @return A child {@code Span} that will have the name provided. */ - abstract Span startSpanWithRemoteParent( - @Nullable SpanContext remoteParent, String name, StartSpanOptions options); + abstract Span startSpan( + @Nullable SpanContext parent, boolean hasRemoteParent, String name, StartSpanOptions options); } diff --git a/core/src/main/java/com/google/instrumentation/trace/StartSpanOptions.java b/core/src/main/java/com/google/instrumentation/trace/StartSpanOptions.java index d0c6a6b8..11853582 100644 --- a/core/src/main/java/com/google/instrumentation/trace/StartSpanOptions.java +++ b/core/src/main/java/com/google/instrumentation/trace/StartSpanOptions.java @@ -13,15 +13,10 @@ package com.google.instrumentation.trace; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.instrumentation.common.Timestamp; -import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import javax.annotation.Nullable; @@ -30,45 +25,17 @@ import javax.annotation.Nullable; * overriding the {@link Timestamp start time}, the {@link Sampler sampler}, the parent links, and * option to record all the events even if the {@code Span} is not sampled. */ -public final class StartSpanOptions { - private static final StartSpanOptions DEFAULT_OPTIONS = builder().build(); - private final Timestamp startTime; - private final Sampler sampler; - // This object is an unmodifiable List. - private final List<Span> parentLinks; - private final Boolean recordEvents; - - /** - * Returns default {@code StartSpanOptions}. - * - * @return default {@code StartSpanOptions}. - */ - public static StartSpanOptions getDefault() { - return DEFAULT_OPTIONS; - } - - /** - * Returns a new {@link Builder} with default options. - * - * @return a new {@code Builder} with default options. - */ - public static Builder builder() { - return new Builder(); - } - - private StartSpanOptions( - @Nullable Timestamp startTime, - @Nullable Sampler sampler, - @Nullable List<Span> parentLinks, - @Nullable Boolean recordEvents) { - this.startTime = startTime; - this.sampler = sampler; - // Make parentLinks an unmodifiable list. - this.parentLinks = - parentLinks == null - ? Collections.<Span>emptyList() - : Collections.unmodifiableList(new ArrayList<Span>(parentLinks)); - this.recordEvents = recordEvents; +final class StartSpanOptions { + private Timestamp startTime; + private Sampler sampler; + private List<Span> parentLinks; + private Boolean recordEvents; + + StartSpanOptions() { + this.startTime = null; + this.sampler = null; + this.parentLinks = null; + this.recordEvents = null; } /** @@ -77,7 +44,7 @@ public final class StartSpanOptions { * @return start time to be used, or {@code null} if default. */ @Nullable - public Timestamp getStartTime() { + Timestamp getStartTime() { return startTime; } @@ -87,7 +54,7 @@ public final class StartSpanOptions { * @return the {@code Sampler} to be used, or {@code null} if default. */ @Nullable - public Sampler getSampler() { + Sampler getSampler() { return sampler; } @@ -96,9 +63,11 @@ public final class StartSpanOptions { * * @return the parent links to be set for the {@code Span}. */ - public List<Span> getParentLinks() { - // It is safe to directly return parentLinks because it is an unmodifiable list. - return parentLinks; + List<Span> getParentLinks() { + // Return an unmodifiable list. + return parentLinks == null + ? Collections.<Span>emptyList() + : Collections.unmodifiableList(parentLinks); } /** @@ -107,7 +76,7 @@ public final class StartSpanOptions { * @return the record events option setting. */ @Nullable - public Boolean getRecordEvents() { + Boolean getRecordEvents() { return recordEvents; } @@ -133,103 +102,19 @@ public final class StartSpanOptions { return Objects.hashCode(startTime, sampler, parentLinks, recordEvents); } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("startTime", startTime) - .add("sampler", sampler) - .add("parentLinks", parentLinks) - .add("recordEvents", recordEvents) - .toString(); + void setStartTime(@Nullable Timestamp startTime) { + this.startTime = startTime; } - /** - * Builder class for {@link StartSpanOptions}. - */ - public static final class Builder { - private Timestamp startTime; - private Sampler sampler; - private List<Span> parentLinks; - private Boolean recordEvents; - - private Builder() {} - - /** - * Sets the start time for the {@link Span}. - * - * @param startTime The start time for the {@code Span}. If {@code null} is used, then the - * current system time at the point at which {@link Tracer#startSpan} is called will be - * used. - * @return this. - */ - public Builder setStartTime(@Nullable Timestamp startTime) { - this.startTime = startTime; - return this; - } - - /** - * Sets the {@link Sampler} to use. If a {@code null} value is passed, the implementation will - * provide a default. - * - * @param sampler The {@code Sampler} to use when determining sampling for a {@code Span}. - * @return this. - */ - public Builder setSampler(@Nullable Sampler sampler) { - this.sampler = sampler; - return this; - } - - /** - * Adds one parent link. 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 parentLink The new {@code Span} parent link. - * @return this. - * @throws NullPointerException if {@code parentLink} is {@code null}. - */ - public Builder addParentLink(Span parentLink) { - if (parentLinks == null) { - parentLinks = new LinkedList<Span>(); - } - parentLinks.add(checkNotNull(parentLink, "parentLink")); - return this; - } - - /** - * Adds a {@code List} of parent links. See {@link #addParentLink}. - * - * @param parentLinks New links to be added. - * @return this. - * @throws NullPointerException if {@code parentLinks} is {@code null}. - */ - public Builder addParentLinks(List<Span> parentLinks) { - if (this.parentLinks == null) { - this.parentLinks = new LinkedList<Span>(); - } - this.parentLinks.addAll(checkNotNull(parentLinks, "parentLinks")); - return this; - } + void setSampler(@Nullable Sampler sampler) { + this.sampler = sampler; + } - /** - * Sets recordEvents. - * - * @param recordEvents New value determining if this {@code Span} should have events recorded. - * If a {@code null} value is passed, the implementation will provide a default. - * @return this. - */ - public Builder setRecordEvents(@Nullable Boolean recordEvents) { - this.recordEvents = recordEvents; - return this; - } + void setParentLinks(@Nullable List<Span> parentLinks) { + this.parentLinks = parentLinks; + } - /** - * Builds and returns a {@link StartSpanOptions} with the desired settings. - * - * @return a {@link StartSpanOptions} with the desired settings. - */ - public StartSpanOptions build() { - return new StartSpanOptions(startTime, sampler, parentLinks, recordEvents); - } + void setRecordEvents(@Nullable Boolean recordEvents) { + this.recordEvents = recordEvents; } } diff --git a/core/src/main/java/com/google/instrumentation/trace/Tracer.java b/core/src/main/java/com/google/instrumentation/trace/Tracer.java index 61f106c5..57b9a7ff 100644 --- a/core/src/main/java/com/google/instrumentation/trace/Tracer.java +++ b/core/src/main/java/com/google/instrumentation/trace/Tracer.java @@ -29,25 +29,34 @@ import javax.annotation.Nullable; * * <p>This can be used similarly to {@link java.util.logging.Logger}. * - * <p>Example usage: + * <p>Users may choose to use manual or automatic Context propagation. Because of that this class + * offers APIs to facilitate both usages. + * + * <p>Example usage with automatic context propagation: * * <pre>{@code * class MyClass { * private static final Tracer tracer = Tracer.getTracer(); * void DoWork() { - * try(NonThrowingCloseable ss = tracer.startScopedSpan("MyClass.DoWork")) { + * try(NonThrowingCloseable ss = tracer.spanBuilder("MyClass.DoWork").startScopedSpan) { * tracer.getCurrentSpan().addAnnotation("We did the work."); * } * } * } * }</pre> * - * <p>{@code Tracer} manages the following modules: + * <p>Example usage with manual context propagation: * - * <ul> - * <li>In process interaction between the {@code Span} and the Context. - * <li>{@code Span} creation. - * </ul> + * <pre>{@code + * class MyClass { + * private static final Tracer tracer = Tracer.getTracer(); + * void DoWork(Span parent) { + * try(NonThrowingCloseable ss = tracer.spanBuilder(parent, "MyClass.DoWork").startScopedSpan) { + * tracer.getCurrentSpan().addAnnotation("We did the work."); + * } + * } + * } + * }</pre> */ public final class Tracer { private static final Logger logger = Logger.getLogger(Tracer.class.getName()); @@ -75,7 +84,7 @@ public final class Tracer { * Gets the current Span from the current Context. * * <p>To install a {@link Span} to the current Context use {@link #withSpan(Span)} OR use {@link - * #startScopedSpan} methods to start a new {@code Span}. + * SpanBuilder#startScopedSpan} methods to start a new {@code Span}. * * <p>startSpan methods do NOT modify the current Context {@code Span}. * @@ -88,7 +97,6 @@ public final class Tracer { return currentSpan != null ? currentSpan : BlankSpan.INSTANCE; } - // TODO(bdrutu): Add error_prone annotation @MustBeClosed when the 2.0.16 jar is fixed. /** * Enters the scope of code where the given {@link Span} is in the current Context, and returns an * object that represents that scope. The scope is exited when the returned object is closed. @@ -134,7 +142,7 @@ public final class Tracer { * }</pre> * * @param span The {@link Span} to be set to the current Context. - * @return An object that defines a scope where the given {@link Span} will be set to the current + * @return an object that defines a scope where the given {@link Span} will be set to the current * Context. * @throws NullPointerException if span is null. */ @@ -143,190 +151,66 @@ public final class Tracer { } /** - * Creates and starts a new child {@link Span} as a child of to the current {@code Span} if any, - * otherwise creates a root Span with the default options. - * - * <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. The scope is exited when the returned object is - * closed then the previous Context is restored and the newly created {@code Span} is ended using - * {@link Span#end}. - * - * <p>Supports try-with-resource idiom. + * Returns a {@link SpanBuilder} to create and start a new child {@link Span} as a child of to the + * current {@code Span} if any, otherwise create a root Span with the default options. * - * <p>Example of usage: + * <p>See {@link SpanBuilder} for usage examples. * - * <pre>{@code - * private static Tracer tracer = Tracer.getTracer(); - * void doWork { - * // Create a Span as a child of the current Span. - * try (NonThrowingCloseable ss = tracer.startScopedSpan("my span")) { - * tracer.getCurrentSpan().addAnnotation("my annotation"); - * doSomeOtherWork(); // Here "span" is the current Span. - * } - * } - * }</pre> - * - * <p>Prior to Java SE 7, you can use a finally block to ensure that a resource is closed - * regardless of whether the try statement completes normally or abruptly. - * - * <p>Example of usage prior to Java SE7: - * - * <pre>{@code - * private static Tracer tracer = Tracer.getTracer(); - * void doWork { - * // Create a Span as a child of the current Span. - * NonThrowingCloseable ss = tracer.startScopedSpan("my span"); - * try { - * tracer.getCurrentSpan().addAnnotation("my annotation"); - * doSomeOtherWork(); // Here "span" is the current Span. - * } finally { - * ss.close(); - * } - * } - * }</pre> + * <p>This <b>must</b> be used to create a {@code Span} when automatic Context propagation is + * used. * * @param name The name of the returned Span. - * @return An object that defines a scope where the newly created child will be set to the current - * Context. + * @return a {@code SpanBuilder} to create and start a new {@code Span}. * @throws NullPointerException if name is null. */ - public NonThrowingCloseable startScopedSpan(String name) { - return new ScopedSpanHandle( - spanFactory.startSpan( - getCurrentSpan(), checkNotNull(name, "name"), StartSpanOptions.getDefault()), - contextSpanHandler); - } - - /** - * Creates and starts a new child {@link Span} as a child of to the current {@code Span} if any, - * otherwise creates a root Span with the given options. - * - * <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. The scope is exited when the returned object is - * closed then the previous Context is restored and the newly created {@code Span} is ended using - * {@link Span#end}. - * - * <p>Supports try-with-resource idiom. - * - * <p>See {@link #startScopedSpan(String)} for example to use. - * - * @param name The name of the returned Span. - * @param options The options for the start of the Span. - * @return An object that defines a scope where the newly created child will be set to the current - * Context. - * @throws NullPointerException if name or options is null. - */ - public NonThrowingCloseable startScopedSpan(String name, StartSpanOptions options) { - return new ScopedSpanHandle( - spanFactory.startSpan( - getCurrentSpan(), checkNotNull(name, "name"), checkNotNull(options, "options")), - contextSpanHandler); + public SpanBuilder spanBuilder(String name) { + return spanBuilder(contextSpanHandler.getCurrentSpan(), name); } /** - * Creates and starts a new child {@link Span} (or root if parent is null), with parent being the - * designated {@code Span} and the default options. + * Returns a {@link SpanBuilder} to create and start a new child {@link Span} (or root if parent + * is null), with parent being the designated {@code 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. The scope is exited when the returned object is - * closed then the previous Context is restored and the newly created {@code Span} is ended using - * {@link Span#end}. + * <p>See {@link SpanBuilder} for usage examples. * - * <p>Supports try-with-resource idiom. + * <p>This <b>must</b> be used to create a {@code Span} when manual Context propagation is used. * - * <p>See {@link #startScopedSpan(String)} for example to use. - * - * @param parent The parent of the returned Span. + * @param parent The parent of the returned Span. If null the {@code SpanBuilder} will build a + * root {@code Span}. * @param name The name of the returned Span. - * @return An object that defines a scope where the newly created child will be set to the current - * Context. + * @return a {@code SpanBuilder} to create and start a new {@code Span}. * @throws NullPointerException if name is null. */ - public NonThrowingCloseable startScopedSpan(@Nullable Span parent, String name) { - return new ScopedSpanHandle( - spanFactory.startSpan(parent, checkNotNull(name, "name"), StartSpanOptions.getDefault()), - contextSpanHandler); - } - - /** - * Creates and starts a new child {@link Span} (or root if parent is null), with parent being the - * designated {@code Span} and the given options. - * - * <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. The scope is exited when the returned object is - * closed then the previous Context is restored and the newly created {@code Span} is ended using - * {@link Span#end}. - * - * <p>Supports try-with-resource idiom. - * - * <p>See {@link #startScopedSpan(String)} for example to use. - * - * @param parent The parent of the returned Span. - * @param name The name of the returned Span. - * @param options The options for the start of the Span. - * @return An object that defines a scope where the newly created child will be set to the current - * Context. - * @throws NullPointerException if name or options is null. - */ - public NonThrowingCloseable startScopedSpan( - @Nullable Span parent, String name, StartSpanOptions options) { - return new ScopedSpanHandle( - spanFactory.startSpan(parent, checkNotNull(name, "name"), checkNotNull(options, "options")), - contextSpanHandler); + public SpanBuilder spanBuilder(@Nullable Span parent, String name) { + return new SpanBuilder( + spanFactory, + contextSpanHandler, + parent == null ? null : parent.getContext(), + /* hasRemoteParent = */ false, + checkNotNull(name, "name")); } /** - * Creates and starts a new child {@link Span} (or root if parent is null), with parent being the - * designated {@code Span} and the given options. + * Returns a {@link SpanBuilder} to create and start a new child {@link Span} (or root if parent + * is null), with parent being the {@link Span} designated by the {@link SpanContext}. * - * <p>Users must manually end the newly created {@code Span}. + * <p>See {@link SpanBuilder} for usage examples. * - * <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 = Tracer.getTracer(); - * void DoWork() { - * try (Span span = tracer.startSpan(null, "MyRootSpan", StartSpanOptions.getDefault())) { - * span.addAnnotation("We did X"); - * } - * } - * } - * }</pre> - * - * @param parent The parent of the returned Span. - * @param name The name of the returned Span. - * @param options The options for the start of the Span. - * @return A child span that will have the name provided. - * @throws NullPointerException if name or options is null. - */ - public Span startSpan(@Nullable Span parent, String name, StartSpanOptions options) { - return spanFactory.startSpan( - parent, checkNotNull(name, "name"), checkNotNull(options, "options")); - } - - /** - * Creates and starts a new child {@link Span} (or root if parent is null), with parent being the - * {@link Span} designated by the {@link SpanContext} and the given options. - * - * <p>This must be used to create a {@code Span} when the parent is on a different process. - * - * <p>Users must manually end the newly created {@code Span}. - * - * <p>Does not install the newly created {@code Span} to the current Context. + * <p>This <b>must</b> be used to create a {@code Span} when the parent is in a different process. + * This is only intended for use by RPC systems or similar. * * @param remoteParent The remote parent of the returned Span. * @param name The name of the returned Span. - * @param options The options for the start of the Span. - * @return A child span that will have the name provided. - * @throws NullPointerException if name or options is null. + * @return a {@code SpanBuilder} to create and start a new {@code Span}. + * @throws NullPointerException if name is null. */ - public Span startSpanWithRemoteParent( - @Nullable SpanContext remoteParent, String name, StartSpanOptions options) { - return spanFactory.startSpanWithRemoteParent( - remoteParent, checkNotNull(name, "name"), checkNotNull(options, "options")); + public SpanBuilder spanBuilderWithRemoteParent(@Nullable SpanContext remoteParent, String name) { + return new SpanBuilder( + spanFactory, + contextSpanHandler, + remoteParent, + /* hasRemoteParent = */ true, + checkNotNull(name, "name")); } @VisibleForTesting @@ -346,6 +230,7 @@ public final class Tracer { }; @Override + @Nullable public Span getCurrentSpan() { return null; } @@ -359,13 +244,11 @@ public final class Tracer { // No-op implementation of the SpanFactory private static final class NoopSpanFactory extends SpanFactory { @Override - public Span startSpan(@Nullable Span parent, String name, StartSpanOptions options) { - return BlankSpan.INSTANCE; - } - - @Override - public Span startSpanWithRemoteParent( - @Nullable SpanContext remoteParent, String name, StartSpanOptions options) { + public Span startSpan( + @Nullable SpanContext parent, + boolean hasRemoteParent, + String name, + StartSpanOptions options) { return BlankSpan.INSTANCE; } } diff --git a/core/src/test/java/com/google/instrumentation/trace/SpanBuilderTest.java b/core/src/test/java/com/google/instrumentation/trace/SpanBuilderTest.java new file mode 100644 index 00000000..6e91ca4d --- /dev/null +++ b/core/src/test/java/com/google/instrumentation/trace/SpanBuilderTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2017, Google Inc. + * 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 com.google.instrumentation.trace; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.instrumentation.common.NonThrowingCloseable; +import com.google.instrumentation.common.Timestamp; +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Unit tests for {@link Tracer}. */ +@RunWith(JUnit4.class) +public class SpanBuilderTest { + @Mock private ContextSpanHandler contextSpanHandler; + @Mock private Span span; + @Mock private SpanFactory spanFactory; + @Mock private NonThrowingCloseable withSpan; + + private static final SpanContext SPAN_CONTEXT = + new SpanContext(new TraceId(10, 20), new SpanId(30), TraceOptions.getDefault()); + private static final String SPAN_NAME = "MySpanName"; + private SpanBuilder spanBuilder; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + spanBuilder = + new SpanBuilder( + spanFactory, contextSpanHandler, SPAN_CONTEXT, false /* hasRemoteParent */, SPAN_NAME); + } + + @Test + public void startScopedSpanRoot() { + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); + try (NonThrowingCloseable ss = spanBuilder.becomeRoot().startScopedSpan()) {} + verify(withSpan).close(); + verify(span).end(same(EndSpanOptions.DEFAULT)); + } + + @Test + public void startScopedSpanRootWithOptions() { + StartSpanOptions startSpanOptions = new StartSpanOptions(); + startSpanOptions.setSampler(Samplers.neverSample()); + startSpanOptions.setStartTime(Timestamp.fromMillis(1234567L)); + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), same(SPAN_NAME), eq(startSpanOptions))) + .thenReturn(span); + when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); + try (NonThrowingCloseable ss = + spanBuilder + .becomeRoot() + .setSampler(Samplers.neverSample()) + .setStartTime(Timestamp.fromMillis(1234567L)) + .startScopedSpan()) {} + verify(withSpan).close(); + verify(span).end(same(EndSpanOptions.DEFAULT)); + } + + @Test + public void startRootSpan() { + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + try (Span rootSpan = spanBuilder.becomeRoot().startSpan()) { + assertThat(rootSpan).isEqualTo(span); + } + } + + @Test + public void startSpan_WithNullParent() { + spanBuilder = + new SpanBuilder( + spanFactory, contextSpanHandler, null, false /* hasRemoteParent */, SPAN_NAME); + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + try (Span rootSpan = spanBuilder.startSpan()) { + assertThat(rootSpan).isEqualTo(span); + } + } + + @Test + public void startRootSpanWithOptions() { + List<Span> parentList = Arrays.asList(BlankSpan.INSTANCE); + StartSpanOptions startSpanOptions = new StartSpanOptions(); + startSpanOptions.setParentLinks(parentList); + startSpanOptions.setSampler(Samplers.neverSample()); + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), same(SPAN_NAME), eq(startSpanOptions))) + .thenReturn(span); + try (Span rootSpan = + spanBuilder + .becomeRoot() + .setSampler(Samplers.neverSample()) + .setParentLinks(parentList) + .startSpan()) { + assertThat(rootSpan).isEqualTo(span); + } + } + + @Test + public void startChildSpan() { + when(spanFactory.startSpan( + same(SPAN_CONTEXT), eq(false), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + try (Span childSpan = spanBuilder.startSpan()) { + assertThat(childSpan).isEqualTo(span); + } + } + + @Test + public void startChildSpanWithOptions() { + StartSpanOptions startSpanOptions = new StartSpanOptions(); + startSpanOptions.setSampler(Samplers.neverSample()); + startSpanOptions.setRecordEvents(true); + when(spanFactory.startSpan( + same(SPAN_CONTEXT), eq(false), same(SPAN_NAME), eq(startSpanOptions))) + .thenReturn(span); + try (Span childSpan = + spanBuilder.setSampler(Samplers.neverSample()).setRecordEvents(true).startSpan()) { + assertThat(childSpan).isEqualTo(span); + } + } + + @Test + public void startSpanWitRemoteParent() { + spanBuilder = + new SpanBuilder( + spanFactory, contextSpanHandler, SPAN_CONTEXT, true /* hasRemoteParent */, SPAN_NAME); + when(spanFactory.startSpan( + same(SPAN_CONTEXT), eq(true), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + try (Span remoteChildSpan = spanBuilder.startSpan()) { + assertThat(remoteChildSpan).isEqualTo(span); + } + } + + @Test + public void startSpanWitRemoteParent_WithNullParent() { + spanBuilder = + new SpanBuilder( + spanFactory, contextSpanHandler, null, true /* hasRemoteParent */, SPAN_NAME); + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(true), same(SPAN_NAME), eq(new StartSpanOptions()))) + .thenReturn(span); + try (Span remoteChildSpan = spanBuilder.startSpan()) { + assertThat(remoteChildSpan).isEqualTo(span); + } + } +} diff --git a/core/src/test/java/com/google/instrumentation/trace/StartSpanOptionsTest.java b/core/src/test/java/com/google/instrumentation/trace/StartSpanOptionsTest.java index 4aa6a243..314789a8 100644 --- a/core/src/test/java/com/google/instrumentation/trace/StartSpanOptionsTest.java +++ b/core/src/test/java/com/google/instrumentation/trace/StartSpanOptionsTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.testing.EqualsTester; import com.google.instrumentation.common.Timestamp; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,21 +27,21 @@ import org.junit.runners.JUnit4; /** Unit tests for {@link StartSpanOptions}. */ @RunWith(JUnit4.class) public class StartSpanOptionsTest { - private final Span singleParent = BlankSpan.INSTANCE; - private final List<Span> parentList = Arrays.asList(BlankSpan.INSTANCE, BlankSpan.INSTANCE); + private final List<Span> singleParentList = Arrays.asList(BlankSpan.INSTANCE); @Test public void defaultOptions() { - assertThat(StartSpanOptions.getDefault().getStartTime()).isNull(); - assertThat(StartSpanOptions.getDefault().getSampler()).isNull(); - assertThat(StartSpanOptions.getDefault().getParentLinks().isEmpty()).isTrue(); - assertThat(StartSpanOptions.getDefault().getRecordEvents()).isNull(); + StartSpanOptions defaultOptions = new StartSpanOptions(); + assertThat(defaultOptions.getStartTime()).isNull(); + assertThat(defaultOptions.getSampler()).isNull(); + assertThat(defaultOptions.getParentLinks().isEmpty()).isTrue(); + assertThat(defaultOptions.getRecordEvents()).isNull(); } @Test public void setStartTime() { - StartSpanOptions options = - StartSpanOptions.builder().setStartTime(Timestamp.fromMillis(1234567L)).build(); + StartSpanOptions options = new StartSpanOptions(); + options.setStartTime(Timestamp.fromMillis(1234567L)); assertThat(options.getStartTime()).isEqualTo(Timestamp.fromMillis(1234567L)); assertThat(options.getSampler()).isNull(); assertThat(options.getParentLinks().isEmpty()).isTrue(); @@ -49,8 +50,8 @@ public class StartSpanOptionsTest { @Test public void setSampler() { - StartSpanOptions options = - StartSpanOptions.builder().setSampler(Samplers.neverSample()).build(); + StartSpanOptions options = new StartSpanOptions(); + options.setSampler(Samplers.neverSample()); assertThat(options.getStartTime()).isNull(); assertThat(options.getSampler()).isEqualTo(Samplers.neverSample()); assertThat(options.getParentLinks().isEmpty()).isTrue(); @@ -58,36 +59,39 @@ public class StartSpanOptionsTest { } @Test - public void addParentLink() { - StartSpanOptions options = StartSpanOptions.builder().addParentLink(singleParent).build(); + public void setParentLinks() { + StartSpanOptions options = new StartSpanOptions(); + options.setParentLinks(singleParentList); assertThat(options.getStartTime()).isNull(); assertThat(options.getSampler()).isNull(); - assertThat(options.getParentLinks().size()).isEqualTo(1); + assertThat(options.getParentLinks()).isEqualTo(singleParentList); assertThat(options.getRecordEvents()).isNull(); } - @Test(expected = NullPointerException.class) - public void addParentLink_Null() { - StartSpanOptions.builder().addParentLink(null).build(); + @Test + public void setParentLinks_EmptyList() { + StartSpanOptions options = new StartSpanOptions(); + options.setParentLinks(new LinkedList<Span>()); + assertThat(options.getStartTime()).isNull(); + assertThat(options.getSampler()).isNull(); + assertThat(options.getParentLinks().size()).isEqualTo(0); + assertThat(options.getRecordEvents()).isNull(); } @Test - public void addParentLinks() { - StartSpanOptions options = StartSpanOptions.builder().addParentLinks(parentList).build(); + public void setParentLinks_MultipleParents() { + StartSpanOptions options = new StartSpanOptions(); + options.setParentLinks(Arrays.asList(BlankSpan.INSTANCE, BlankSpan.INSTANCE)); assertThat(options.getStartTime()).isNull(); assertThat(options.getSampler()).isNull(); assertThat(options.getParentLinks().size()).isEqualTo(2); assertThat(options.getRecordEvents()).isNull(); } - @Test(expected = NullPointerException.class) - public void addParentLinks_Null() { - StartSpanOptions.builder().addParentLinks(null).build(); - } - @Test public void setRecordEvents() { - StartSpanOptions options = StartSpanOptions.builder().setRecordEvents(true).build(); + StartSpanOptions options = new StartSpanOptions(); + options.setRecordEvents(true); assertThat(options.getStartTime()).isNull(); assertThat(options.getSampler()).isNull(); assertThat(options.getParentLinks().isEmpty()).isTrue(); @@ -96,46 +100,33 @@ public class StartSpanOptionsTest { @Test public void setAllProperties() { - StartSpanOptions options = - StartSpanOptions.builder() - .setStartTime(Timestamp.fromMillis(1234567L)) - .setSampler(Samplers.neverSample()) - .setSampler(Samplers.alwaysSample()) // second SetSampler should apply - .addParentLink(singleParent) - .setRecordEvents(true) - .addParentLinks(parentList) - .build(); + StartSpanOptions options = new StartSpanOptions(); + options.setStartTime(Timestamp.fromMillis(1234567L)); + options.setSampler(Samplers.neverSample()); + options.setSampler(Samplers.alwaysSample()); // second SetSampler should apply + options.setRecordEvents(true); + options.setParentLinks(singleParentList); assertThat(options.getStartTime()).isEqualTo(Timestamp.fromMillis(1234567L)); assertThat(options.getSampler()).isEqualTo(Samplers.alwaysSample()); - assertThat(options.getParentLinks().size()).isEqualTo(3); + assertThat(options.getParentLinks()).isEqualTo(singleParentList); assertThat(options.getRecordEvents()).isTrue(); } @Test - public void startSpanOptions_ToString() { - StartSpanOptions options = - StartSpanOptions.builder() - .setStartTime(Timestamp.fromMillis(1234567L)) - .setSampler(Samplers.neverSample()) - .addParentLink(singleParent) - .setRecordEvents(true) - .build(); - assertThat(options.toString()).contains(Timestamp.fromMillis(1234567L).toString()); - assertThat(options.toString()).contains(Samplers.neverSample().toString()); - assertThat(options.toString()).contains(singleParent.toString()); - assertThat(options.toString()).contains("recordEvents=true"); - } - - @Test public void startSpanOptions_EqualsAndHashCode() { EqualsTester tester = new EqualsTester(); - tester.addEqualityGroup( - StartSpanOptions.builder().setStartTime(Timestamp.fromMillis(1234567L)).build(), - StartSpanOptions.builder().setStartTime(Timestamp.fromMillis(1234567L)).build()); - tester.addEqualityGroup( - StartSpanOptions.builder().setRecordEvents(true).build(), - StartSpanOptions.builder().setRecordEvents(true).build()); - tester.addEqualityGroup(StartSpanOptions.getDefault(), StartSpanOptions.builder().build()); + StartSpanOptions optionsWithStartTime1 = new StartSpanOptions(); + optionsWithStartTime1.setStartTime(Timestamp.fromMillis(1234567L)); + StartSpanOptions optionsWithStartTime2 = new StartSpanOptions(); + optionsWithStartTime2.setStartTime(Timestamp.fromMillis(1234567L)); + tester.addEqualityGroup(optionsWithStartTime1, optionsWithStartTime2); + StartSpanOptions optionsWithAlwaysSampler = new StartSpanOptions(); + optionsWithAlwaysSampler.setSampler(Samplers.alwaysSample()); + tester.addEqualityGroup(optionsWithAlwaysSampler); + StartSpanOptions optionsWithNeverSampler = new StartSpanOptions(); + optionsWithNeverSampler.setSampler(Samplers.neverSample()); + tester.addEqualityGroup(optionsWithNeverSampler); + tester.addEqualityGroup(new StartSpanOptions()); tester.testEquals(); } } diff --git a/core/src/test/java/com/google/instrumentation/trace/TracerTest.java b/core/src/test/java/com/google/instrumentation/trace/TracerTest.java index de67c6ec..4afbb93d 100644 --- a/core/src/test/java/com/google/instrumentation/trace/TracerTest.java +++ b/core/src/test/java/com/google/instrumentation/trace/TracerTest.java @@ -41,6 +41,11 @@ public class TracerTest { @Mock private Span span; @Mock private NonThrowingCloseable withSpan; + @Test + public void defaultGetCurrentSpan() { + assertThat(tracer.getCurrentSpan()).isEqualTo(BlankSpan.INSTANCE); + } + @Test(expected = NullPointerException.class) public void withSpan_NullSpan() { try (NonThrowingCloseable ws = tracer.withSpan(null)) {} @@ -54,96 +59,35 @@ public class TracerTest { } @Test(expected = NullPointerException.class) - public void startScopedSpanWithName_NullName() { - try (NonThrowingCloseable ss = tracer.startScopedSpan(null)) {} - } - - @Test - public void defaultStartScopedSpanWithName() { - try (NonThrowingCloseable ss = tracer.startScopedSpan("MySpanName")) { - assertThat(tracer.getCurrentSpan()).isEqualTo(BlankSpan.INSTANCE); - } - } - - @Test(expected = NullPointerException.class) - public void startScopedSpanWithNameAndOptions_NullName() { - try (NonThrowingCloseable ss = tracer.startScopedSpan(null, StartSpanOptions.getDefault())) {} - } - - @Test(expected = NullPointerException.class) - public void startScopedSpanWithNameAndOptions_NullOptions() { - try (NonThrowingCloseable ss = tracer.startScopedSpan("MySpanName", null)) {} - } - - @Test - public void defaultStartScopedSpanWithNameAndOptions() { - try (NonThrowingCloseable ss = - tracer.startScopedSpan("MySpanName", StartSpanOptions.getDefault())) { - assertThat(tracer.getCurrentSpan()).isEqualTo(BlankSpan.INSTANCE); - } - } - - @Test(expected = NullPointerException.class) - public void startScopedSpanWithParentAndName_NullName() { - try (NonThrowingCloseable ss = tracer.startScopedSpan((Span) null, null)) {} + public void spanBuilderWithName_NullName() { + assertThat(tracer.spanBuilder(null).startSpan()).isSameAs(BlankSpan.INSTANCE); } @Test - public void defaultStartScopedSpanWithParentAndName() { - try (NonThrowingCloseable ss = tracer.startScopedSpan(null, "MySpanName")) { - assertThat(tracer.getCurrentSpan()).isEqualTo(BlankSpan.INSTANCE); - } - } - - @Test(expected = NullPointerException.class) - public void startScopedSpanWithParentAndNameAndOptions_NullName() { - try (NonThrowingCloseable ss = - tracer.startScopedSpan(null, null, StartSpanOptions.getDefault())) {} - } - - @Test(expected = NullPointerException.class) - public void startScopedSpanWithParentAndNameAndOptions_NullOptions() { - try (NonThrowingCloseable ss = tracer.startScopedSpan(null, "MySpanName", null)) {} - } - - @Test - public void defaultStartScopedSpanWithParentAndNameAndOptions() { - try (NonThrowingCloseable ss = - tracer.startScopedSpan(null, "MySpanName", StartSpanOptions.getDefault())) { - assertThat(tracer.getCurrentSpan()).isEqualTo(BlankSpan.INSTANCE); - } + public void defaultSpanBuilderWithName() { + assertThat(tracer.spanBuilder("MySpanName").startSpan()).isSameAs(BlankSpan.INSTANCE); } @Test(expected = NullPointerException.class) - public void startSpan_NullName() { - tracer.startSpan(null, null, StartSpanOptions.getDefault()); - } - - @Test(expected = NullPointerException.class) - public void startSpan_NullOptions() { - tracer.startSpan(null, "MySpanName", null); + public void spanBuilderWithParentAndName_NullName() { + assertThat(tracer.spanBuilder(null, null).startSpan()).isSameAs(BlankSpan.INSTANCE); } @Test - public void defaultStartSpan() { - assertThat(tracer.startSpan(null, "MySpanName", StartSpanOptions.getDefault())) - .isEqualTo(BlankSpan.INSTANCE); - } - - @Test(expected = NullPointerException.class) - public void startSpanWithRemoteParent_NullName() { - tracer.startSpanWithRemoteParent(null, null, StartSpanOptions.getDefault()); + public void defaultSpanBuilderWithParentAndName() { + assertThat(tracer.spanBuilder(null, "MySpanName").startSpan()).isSameAs(BlankSpan.INSTANCE); } @Test(expected = NullPointerException.class) - public void startSpanWithRemoteParent_NullOptions() { - tracer.startSpanWithRemoteParent(null, "MySpanName", null); + public void spanBuilderWithRemoteParent_NullName() { + assertThat(tracer.spanBuilderWithRemoteParent(null, null).startSpan()) + .isSameAs(BlankSpan.INSTANCE); } @Test - public void defaultStartSpanWitRemoteParent() { - assertThat(tracer.startSpanWithRemoteParent(null, "MySpanName", StartSpanOptions.getDefault())) - .isEqualTo(BlankSpan.INSTANCE); + public void defaultSpanBuilderWitRemoteParent() { + assertThat(tracer.spanBuilderWithRemoteParent(null, "MySpanName").startSpan()) + .isSameAs(BlankSpan.INSTANCE); } @Test @@ -220,72 +164,61 @@ public class TracerTest { } @Test - public void startScopedSpanWithName() { + public void startScopedSpanRoot() { Tracer mockTracer = newTracerWithMocks(); when(contextSpanHandler.getCurrentSpan()).thenReturn(null); when(spanFactory.startSpan( - same(BlankSpan.INSTANCE), eq("MySpanName"), same(StartSpanOptions.getDefault()))) + isNull(SpanContext.class), eq(false), eq("MySpanName"), eq(new StartSpanOptions()))) .thenReturn(span); when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); - try (NonThrowingCloseable ss = mockTracer.startScopedSpan("MySpanName")) {} + try (NonThrowingCloseable ss = + mockTracer.spanBuilder("MySpanName").becomeRoot().startScopedSpan()) {} verify(withSpan).close(); verify(span).end(same(EndSpanOptions.DEFAULT)); } @Test - public void startScopedSpanWithNameAndOptions() { + public void startScopedSpanChild() { Tracer mockTracer = newTracerWithMocks(); - when(contextSpanHandler.getCurrentSpan()).thenReturn(null); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); + when(contextSpanHandler.getCurrentSpan()).thenReturn(BlankSpan.INSTANCE); when(spanFactory.startSpan( - same(BlankSpan.INSTANCE), eq("MySpanName"), same(startSpanOptions))) + same(BlankSpan.INSTANCE.getContext()), + eq(false), + eq("MySpanName"), + eq(new StartSpanOptions()))) .thenReturn(span); when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); - try (NonThrowingCloseable ss = mockTracer.startScopedSpan("MySpanName", startSpanOptions)) {} + try (NonThrowingCloseable ss = mockTracer.spanBuilder("MySpanName").startScopedSpan()) {} verify(withSpan).close(); verify(span).end(same(EndSpanOptions.DEFAULT)); } @Test - public void startScopedSpanWithParentAndName() { - Tracer mockTracer = newTracerWithMocks(); - when(spanFactory.startSpan(same(span), eq("MySpanName"), same(StartSpanOptions.getDefault()))) - .thenReturn(span); - when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); - try (NonThrowingCloseable ss = mockTracer.startScopedSpan(span, "MySpanName")) {} - verify(withSpan).close(); - verify(span).end(same(EndSpanOptions.DEFAULT)); - } - - @Test - public void startScopedSpanWithParentAndNameAndOptions() { + public void startRootSpan() { Tracer mockTracer = newTracerWithMocks(); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); - when(spanFactory.startSpan(same(span), eq("MySpanName"), same(startSpanOptions))) + when(spanFactory.startSpan( + isNull(SpanContext.class), eq(false), eq("MySpanName"), eq(new StartSpanOptions()))) .thenReturn(span); - when(contextSpanHandler.withSpan(same(span))).thenReturn(withSpan); - try (NonThrowingCloseable ss = - mockTracer.startScopedSpan(span, "MySpanName", startSpanOptions)) {} - verify(withSpan).close(); + try (Span rootSpan = + mockTracer.spanBuilder(BlankSpan.INSTANCE, "MySpanName").becomeRoot().startSpan()) { + assertThat(rootSpan).isEqualTo(span); + } verify(span).end(same(EndSpanOptions.DEFAULT)); } @Test public void startChildSpan() { Tracer mockTracer = newTracerWithMocks(); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); - when(spanFactory.startSpan(same(span), eq("MySpanName"), same(startSpanOptions))) - .thenReturn(span); - assertThat(mockTracer.startSpan(span, "MySpanName", startSpanOptions)).isEqualTo(span); - } - - @Test - public void startRootSpan() { - Tracer mockTracer = newTracerWithMocks(); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); - when(spanFactory.startSpan(isNull(Span.class), eq("MySpanName"), same(startSpanOptions))) + when(spanFactory.startSpan( + same(BlankSpan.INSTANCE.getContext()), + eq(false), + eq("MySpanName"), + eq(new StartSpanOptions()))) .thenReturn(span); - assertThat(mockTracer.startSpan(null, "MySpanName", startSpanOptions)).isEqualTo(span); + try (Span childSpan = mockTracer.spanBuilder(BlankSpan.INSTANCE, "MySpanName").startSpan()) { + assertThat(childSpan).isEqualTo(span); + } + verify(span).end(same(EndSpanOptions.DEFAULT)); } @Test @@ -293,23 +226,13 @@ public class TracerTest { Tracer mockTracer = newTracerWithMocks(); SpanContext spanContext = new SpanContext(new TraceId(10, 20), new SpanId(30), TraceOptions.getDefault()); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); - when(spanFactory.startSpanWithRemoteParent( - same(spanContext), eq("MySpanName"), same(startSpanOptions))) - .thenReturn(span); - assertThat(mockTracer.startSpanWithRemoteParent(spanContext, "MySpanName", startSpanOptions)) - .isEqualTo(span); - } - - @Test - public void startSpanWitRemoteParent_WithNullParent() { - Tracer mockTracer = newTracerWithMocks(); - StartSpanOptions startSpanOptions = StartSpanOptions.builder().build(); - when(spanFactory.startSpanWithRemoteParent( - isNull(SpanContext.class), eq("MySpanName"), same(startSpanOptions))) + when(spanFactory.startSpan( + same(spanContext), eq(true), eq("MySpanName"), eq(new StartSpanOptions()))) .thenReturn(span); - assertThat(mockTracer.startSpanWithRemoteParent(null, "MySpanName", startSpanOptions)) - .isEqualTo(span); + try (Span remoteChildSpan = + mockTracer.spanBuilderWithRemoteParent(spanContext, "MySpanName").startSpan()) { + assertThat(remoteChildSpan).isEqualTo(span); + } } private Tracer newTracerWithMocks() { |
