Skip to content

Commit

Permalink
Clean up docs and tests for PR 113, and add missing factory methods f…
Browse files Browse the repository at this point in the history
…or ScheduledExecutorServiceWithTracing
  • Loading branch information
nicmunroe committed Feb 19, 2020
1 parent 11d924c commit d07fc30
Show file tree
Hide file tree
Showing 15 changed files with 391 additions and 98 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,22 @@ CompletableFuture.supplyAsync(withTracing(() -> {
}));
```

* There's a `ScheduledExecutorServiceWithTracing` that extends `ExecutorServiceWithTracing` and implements
`ScheduledExecutorService`, for when you need a scheduler that supports automatic Wingtips tracing state propagation.

``` java
import static com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing.withTracing;

// ...

// Just an example - please use an appropriate ScheduledExecutorService for your use case.
ScheduledExecutorService scheduler = withTracing(Executors.newSingleThreadScheduledExecutor());

scheduler.schedule(() -> {
// Code that needs tracing/MDC wrapping goes here
}, 42, TimeUnit.SECONDS);
```

* This example shows how you might accomplish tasks in an environment where the tracing information is attached
to some request context, and you need to temporarily attach the tracing info in order to do something (e.g. log some
messages with tracing info automatically added using MDC):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.nike.wingtips.util.asynchelperwrapper.CallableWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.RunnableWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing;

import org.slf4j.MDC;

Expand All @@ -14,6 +15,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;

/**
* Helper class that provides static methods for dealing with async stuff in Wingtips, mainly providing easy ways
Expand Down Expand Up @@ -238,6 +240,26 @@ public static <U> Callable<U> callableWithTracing(Callable<U> callable,
public static ExecutorServiceWithTracing executorServiceWithTracing(ExecutorService delegate) {
return new ExecutorServiceWithTracing(delegate);
}

/**
* @return A {@link ScheduledExecutorService} that wraps the given delegate {@link ScheduledExecutorService} so
* that when {@link Runnable}s or {@link Callable}s are scheduled or executed through it they will automatically
* inherit the tracing state of the thread that called the {@link ScheduledExecutorService} method. Equivalent to
* calling: {@code new ScheduledExecutorServiceWithTracing(delegate)}.
*
* <p>WARNING: Keep in mind that you should avoid using a {@link ScheduledExecutorServiceWithTracing} when spinning
* off background threads that aren't tied to a specific trace, or in any other situation where an executed
* {@link Runnable}/{@link Callable} should *not* automatically inherit the calling thread's tracing state!
*
* @deprecated Please move to the Java 8 version of this class and method ({@code AsyncWingtipsHelper} or the static
* {@code AsyncWingtipsHelperStatic}) whenever possible.
*/
@Deprecated
public static ScheduledExecutorServiceWithTracing scheduledExecutorServiceWithTracing(
ScheduledExecutorService delegate
) {
return new ScheduledExecutorServiceWithTracing(delegate);
}

/**
* Links the given distributed tracing and logging MDC info to the current thread. Any existing tracing and MDC info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,37 @@

/**
* A wrapper around any {@link ScheduledExecutorService} instance that causes the current (method caller's) tracing
* state to hop
* threads when {@link Runnable}s or {@link Callable}s are supplied for execution. Simply supply the constructor with
* the delegate {@link ScheduledExecutorService} you want to wrap (often one from {@link java.util.concurrent.Executors}) and
* then treat it like any other {@link ScheduledExecutorService}. Shutdown and termination methods pass through to the
* delegate - {@link ScheduledExecutorServiceWithTracing} does not contain any state of its own.
*
* state to hop threads when {@link Runnable}s or {@link Callable}s are scheduled for execution. Simply supply the
* constructor with the delegate {@link ScheduledExecutorService} you want to wrap (often one from {@link
* java.util.concurrent.Executors}) and then treat it like any other {@link ScheduledExecutorService}. Shutdown and
* termination methods pass through to the delegate - {@link ScheduledExecutorServiceWithTracing} does not contain any
* state of its own.
*
* <p>Usage:
* <pre>
* // In some setup location, create the ScheduledExecutorWithTracing.
* ScheduledExecutorService tracingAwareScheduledExecutorService =
* // This is just an example - please use an appropriate ScheduledExecutorService for your use case.
* ScheduledExecutorService tracingAwareScheduler =
* new ScheduledExecutorWithTracing(Executors.newSingleThreadScheduledExecutor());
*
*
* // Elsewhere, setup the tracing state for the caller's thread.
* Tracer.getInstance().startRequestWithRootSpan("someRootSpan");
* TracingState callerThreadTracingState = TracingState.getCurrentThreadTracingState();
*
* // Tell the tracing-wrapped executor to execute a Runnable.
* tracingAwareExecutorService.execute(() -> {
* // Runnable's execution code goes here.
* // The important bit is that although we are no longer on the caller's thread, this Runnable's thread
* // will have the same tracing state as the caller. The following line will not throw an exception.
* // Tell the tracing-wrapped scheduler to schedule something.
* tracingAwareScheduler.schedule(() -> {
* // The scheduled Runnable/Callable execution code goes here.
* // The important bit is that although we are no longer on the caller's thread, this Runnable/Callable's thread
* // will have the same tracing state as the thread where .schedule(...) was called.
* // The following line will therefore complete successfully without throwing an assertion failure.
* assert callerThreadTracingState.equals(TracingState.getCurrentThreadTracingState());
* });
* }, 42, TimeUnit.SECONDS);
* </pre>
*
* Also note that standard executor/executor service methods like {@link
* java.util.concurrent.Executor#execute(Runnable)} will also propagate tracing state since this class is an extension
* of Wingtips' {@link ExecutorServiceWithTracing}.
*
* @author Biju Kunjummen
* @author Rafaela Breed
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.nike.wingtips.util.asynchelperwrapper.CallableWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.RunnableWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
Expand All @@ -25,11 +26,13 @@
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;

import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.callableWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.executorServiceWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.linkTracingToCurrentThread;
import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.runnableWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.scheduledExecutorServiceWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperJava7.unlinkTracingFromCurrentThread;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
Expand All @@ -45,12 +48,14 @@ public class AsyncWingtipsHelperJava7Test {
private Runnable runnableMock;
private Callable callableMock;
private ExecutorService executorServiceMock;
private ScheduledExecutorService scheduledExecutorServiceMock;

@Before
public void beforeMethod() {
runnableMock = mock(Runnable.class);
callableMock = mock(Callable.class);
executorServiceMock = mock(ExecutorService.class);
scheduledExecutorServiceMock = mock(ScheduledExecutorService.class);

resetTracing();
}
Expand Down Expand Up @@ -216,6 +221,15 @@ public void executorServiceWithTracing_works_as_expected() {
assertThat(Whitebox.getInternalState(result, "delegate")).isSameAs(executorServiceMock);
}

@Test
public void scheduledExecutorServiceWithTracing_works_as_expected() {
// when
ScheduledExecutorServiceWithTracing result = scheduledExecutorServiceWithTracing(scheduledExecutorServiceMock);

// then
assertThat(Whitebox.getInternalState(result, "delegate")).isSameAs(scheduledExecutorServiceMock);
}

@DataProvider(value = {
"true",
"false"
Expand Down
16 changes: 16 additions & 0 deletions wingtips-java8/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ CompletableFuture.supplyAsync(withTracing(() -> {
}));
```

* There's a `ScheduledExecutorServiceWithTracing` that extends `ExecutorServiceWithTracing` and implements
`ScheduledExecutorService`, for when you need a scheduler that supports automatic Wingtips tracing state propagation.

``` java
import static com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing.withTracing;

// ...

// Just an example - please use an appropriate ScheduledExecutorService for your use case.
ScheduledExecutorService scheduler = withTracing(Executors.newSingleThreadScheduledExecutor());

scheduler.schedule(() -> {
// Code that needs tracing/MDC wrapping goes here
}, 42, TimeUnit.SECONDS);
```

* This example shows how you might accomplish tasks in an environment where the tracing information is attached
to some request context, and you need to temporarily attach the tracing info in order to do something (e.g. log some
messages with tracing info automatically added using MDC):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.nike.wingtips.util.asynchelperwrapper.ExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.FunctionWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.PredicateWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.SupplierWithTracing;

import org.slf4j.MDC;
Expand All @@ -20,6 +21,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
Expand Down Expand Up @@ -470,6 +472,23 @@ default ExecutorServiceWithTracing executorServiceWithTracing(ExecutorService de
return AsyncWingtipsHelperJava7.executorServiceWithTracing(delegate);
}

/**
* @return A {@link ScheduledExecutorService} that wraps the given delegate {@link ScheduledExecutorService} so
* that when {@link Runnable}s or {@link Callable}s are scheduled or executed through it they will automatically
* inherit the tracing state of the thread that called the {@link ScheduledExecutorService} method. Equivalent to
* calling: {@code new ScheduledExecutorServiceWithTracing(delegate)}.
*
* <p>WARNING: Keep in mind that you should avoid using a {@link ScheduledExecutorServiceWithTracing} when spinning
* off background threads that aren't tied to a specific trace, or in any other situation where an executed
* {@link Runnable}/{@link Callable} should *not* automatically inherit the calling thread's tracing state!
*/
@SuppressWarnings("deprecation")
default ScheduledExecutorServiceWithTracing scheduledExecutorServiceWithTracing(
ScheduledExecutorService delegate
) {
return AsyncWingtipsHelperJava7.scheduledExecutorServiceWithTracing(delegate);
}

/**
* Links the given distributed tracing and logging MDC info to the current thread. Any existing tracing and MDC info
* on the current thread will be wiped out and overridden, so if you need to go back to them in the future you'll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.nike.wingtips.Span;
import com.nike.wingtips.Tracer;
import com.nike.wingtips.util.asynchelperwrapper.ExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing;

import org.slf4j.MDC;

Expand All @@ -13,6 +14,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
Expand Down Expand Up @@ -469,6 +471,22 @@ public static ExecutorServiceWithTracing executorServiceWithTracing(ExecutorServ
return DEFAULT_IMPL.executorServiceWithTracing(delegate);
}

/**
* @return A {@link ScheduledExecutorService} that wraps the given delegate {@link ScheduledExecutorService} so
* that when {@link Runnable}s or {@link Callable}s are scheduled or executed through it they will automatically
* inherit the tracing state of the thread that called the {@link ScheduledExecutorService} method. Equivalent to
* calling: {@code new ScheduledExecutorServiceWithTracing(delegate)}.
*
* <p>WARNING: Keep in mind that you should avoid using a {@link ScheduledExecutorServiceWithTracing} when spinning
* off background threads that aren't tied to a specific trace, or in any other situation where an executed
* {@link Runnable}/{@link Callable} should *not* automatically inherit the calling thread's tracing state!
*/
public static ScheduledExecutorServiceWithTracing scheduledExecutorServiceWithTracing(
ScheduledExecutorService delegate
) {
return DEFAULT_IMPL.scheduledExecutorServiceWithTracing(delegate);
}

/**
* Links the given distributed tracing and logging MDC info to the current thread. Any existing tracing and MDC info
* on the current thread will be wiped out and overridden, so if you need to go back to them in the future you'll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.nike.wingtips.util.asynchelperwrapper.FunctionWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.PredicateWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.RunnableWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.ScheduledExecutorServiceWithTracing;
import com.nike.wingtips.util.asynchelperwrapper.SupplierWithTracing;

import com.tngtech.java.junit.dataprovider.DataProvider;
Expand All @@ -32,6 +33,7 @@
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
Expand All @@ -51,6 +53,7 @@
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.linkTracingToCurrentThread;
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.predicateWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.runnableWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.scheduledExecutorServiceWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.supplierWithTracing;
import static com.nike.wingtips.util.AsyncWingtipsHelperStatic.unlinkTracingFromCurrentThread;
import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -74,6 +77,7 @@ public class AsyncWingtipsHelperTest {
private Predicate predicateMock;
private BiPredicate biPredicateMock;
private ExecutorService executorServiceMock;
private ScheduledExecutorService scheduledExecutorServiceMock;

@Before
public void beforeMethod() {
Expand All @@ -87,6 +91,7 @@ public void beforeMethod() {
predicateMock = mock(Predicate.class);
biPredicateMock = mock(BiPredicate.class);
executorServiceMock = mock(ExecutorService.class);
scheduledExecutorServiceMock = mock(ScheduledExecutorService.class);

resetTracing();
}
Expand Down Expand Up @@ -714,6 +719,22 @@ public void executorServiceWithTracing_works_as_expected(boolean useStaticMethod
assertThat(Whitebox.getInternalState(result, "delegate")).isSameAs(executorServiceMock);
}

@DataProvider(value = {
"true",
"false"
})
@Test
public void scheduledExecutorServiceWithTracing_works_as_expected(boolean useStaticMethod) {
// when
ScheduledExecutorServiceWithTracing result =
(useStaticMethod)
? scheduledExecutorServiceWithTracing(scheduledExecutorServiceMock)
: DEFAULT_IMPL.scheduledExecutorServiceWithTracing(scheduledExecutorServiceMock);

// then
assertThat(Whitebox.getInternalState(result, "delegate")).isSameAs(scheduledExecutorServiceMock);
}

@DataProvider(value = {
"true | true",
"true | false",
Expand Down
13 changes: 8 additions & 5 deletions wingtips-spring-boot2-webflux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ properties to set up the following Wingtips features:
`ZipkinHttpTagStrategy` and `SpringWebfluxServerRequestTagAdapter`. To modify the tag strategy and/or adapter, you
can set the `wingtips.server-side-span-tagging-strategy` and/or `wingtips.server-side-span-tagging-adapter`
application properties (see `WingtipsSpringBoot2WebfluxProperties` description below).
- Adds a hook that enables Wingtips tracing to span async boundaries when using [Project Reactors](https://projectreactor.io/)
`Mono` and `Flux` types along with `subscribeOn` and `publishOn` operators.
- Adds a hook for [Project Reactor](https://projectreactor.io/) that enables automatic propagation of Wingtips
tracing state across async boundaries when using `Mono` or `Flux` types along with `subscribeOn` and `publishOn`
operators. WARNING: The tracing state that will be propagated is the tracing state on the thread at the time
the `Mono` or `Flux` is _subscribed to_, not where the `Mono`/`Flux` is defined.

* **`WingtipsSpringBoot2WebfluxProperties`** - The Spring Boot
[@ConfigurationProperties](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties)
Expand All @@ -78,9 +80,10 @@ for a concrete example. The following properties are supported (all of them opti
- **`wingtips.wingtips-disabled`** - Disables the Wingtips `WingtipsSpringWebfluxWebFilter` filter if and
only if this property value is set to true. If false or missing then `WingtipsSpringWebfluxWebFilter` will be
registered normally.
- **`wingtips.reactor-enabled`** - Enables support for Wingtips tracing across Project reactors async boundaries.
The property is disabled by default.

- **`wingtips.reactor-enabled`** - Enables automatic propagation of Wingtips tracing state across async boundaries
when using [Project Reactor](https://projectreactor.io/) `Mono` or `Flux` types along with `subscribeOn` and
`publishOn` operators. WARNING: The tracing state that will be propagated is the tracing state on the thread at
the time the `Mono` or `Flux` is _subscribed to_, not where the `Mono`/`Flux` is defined.
- **`wingtips.user-id-header-keys`** - Used to specify the user ID header keys that Wingtips will look for on
incoming headers. See the `userIdHeaderKeys` parameter javadocs for the
`HttpRequestTracingUtils.fromRequestWithHeaders(...)` method for more info. This is optional - if not specified
Expand Down
Loading

0 comments on commit d07fc30

Please sign in to comment.