From 9afc380dbfe49095d030e0f1d062ca69b4ba76f8 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 16 Dec 2016 12:59:36 +0100 Subject: [PATCH 01/51] Moved 'active span' implementation from 'global tracer' library to new repository. --- .gitignore | 8 + pom.xml | 216 ++++++++++++++++++ .../contrib/activespan/ActiveSpanManager.java | 215 +++++++++++++++++ .../activespan/ThreadLocalSpanManager.java | 94 ++++++++ .../concurrent/SpanAwareCallable.java | 68 ++++++ .../concurrent/SpanAwareExecutorService.java | 101 ++++++++ .../concurrent/SpanAwareExecutors.java | 93 ++++++++ .../concurrent/SpanAwareRunnable.java | 62 +++++ .../contrib/activespan/tracer/ActiveSpan.java | 147 ++++++++++++ .../activespan/tracer/ActiveSpanBuilder.java | 101 ++++++++ .../activespan/tracer/ActiveSpanTracer.java | 51 +++++ .../opentracing-activespan.properties | 3 + 12 files changed, 1159 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java create mode 100644 src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java create mode 100644 src/main/resources/META-INF/opentracing-activespan.properties diff --git a/.gitignore b/.gitignore index 32858aa..62e8b89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ +# Build / maven artifacts *.class +target/ +pom.xml.versionsBackup # Mobile Tools for Java (J2ME) .mtj.tmp/ @@ -10,3 +13,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# IntelliJ project files +.idea/ +*.iml + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6db53df --- /dev/null +++ b/pom.xml @@ -0,0 +1,216 @@ + + + + 4.0.0 + + + io.opentracing.contrib.activespan + opentracing-activespan + 0.1.0-SNAPSHOT + jar + + + In-process active span propagation. + Library for propagating the active span accross threads. + https://github.com/opentracing-contrib/java-activespan + 2016 + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Sjoerd Talsma + sjoerd@talsma-ict.nl + https://github.com/sjoerdtalsma + Talsma ICT + + + + + https://github.com/opentracing-contrib/java-activespan/tree/master + scm:git:git://github.com/opentracing-contrib/java-activespan.git + scm:git:git@github.com:opentracing-contrib/java-activespan.git + + + + UTF-8 + UTF-8 + 1.6 + + 0.20.2 + + + + + + io.opentracing + opentracing-api + ${opentracing-api.version} + + + io.opentracing + opentracing-noop + ${opentracing-api.version} + + + + + io.opentracing + opentracing-mock + ${opentracing-api.version} + test + + + nl.talsmasoftware + context-propagation + 0.3.0 + test + + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + + + + + src/main/resources + true + + **/*.properties + + + + src/main/resources + + **/*.properties + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.1 + + + validate + + create + + + UNKNOWN + true + git.revision + {0,date,yyyy-MM-dd'T'HH:mm:ss.SSSZ} + build.timestamp + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + ${build.java.version} + ${build.java.version} + ${project.build.sourceEncoding} + true + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + generate-javadoc + + jar + + + nl.talsmasoftware.umldoclet.UMLDoclet + + nl.talsmasoftware + umldoclet + 1.0.8 + + ${project.reporting.outputEncoding} + true + + -umlIncludePackagePrivateClasses false + -umlImageFormat SVG + -umlCommand "hide empty fields" + -umlCommand "hide empty methods" + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.6 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + diff --git a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java new file mode 100644 index 0000000..26c252b --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java @@ -0,0 +1,215 @@ +package io.opentracing.contrib.activespan; + +import io.opentracing.NoopSpan; +import io.opentracing.Span; +import io.opentracing.contrib.activespan.concurrent.SpanAwareCallable; +import io.opentracing.contrib.activespan.concurrent.SpanAwareRunnable; + +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Manages the active {@link Span}.
+ * A {@link Span} becomes active in the current process after a call to {@link #activate(Span)} + * and can be deactivated again by calling {@link #deactivate(SpanDeactivator)}. + *

+ * The default implementation will use a {@link ThreadLocal ThreadLocal storage} to maintain the active {@link Span} + * and its parents. + *

+ * Custom implementations can be provided by: + *

    + *
  1. calling {@link #setActiveSpanManager(ActiveSpanManager)} programmatically, or
  2. + *
  3. defining a META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager service file + * containing the classname of the implementation
  4. + *
+ * + * @author Sjoerd Talsma + * @navassoc - activeSpan - io.opentracing.Span + */ +public abstract class ActiveSpanManager { + private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); + private static final AtomicReference INSTANCE = new AtomicReference(); + + /** + * Managed object to deactivate an activated {@link Span} with. + */ + public interface SpanDeactivator { + } + + /** + * Programmatically sets an ActiveSpanManager implementation as the singleton instance. + *

+ * Any previous manager is returned so it can be restored if necessary, for example in a testing situation. + * + * @param instance The overridden implementation to use for in-process span management. + * @return Any previous ActiveSpanManager that was initialized before, or null. + */ + public static ActiveSpanManager setActiveSpanManager(ActiveSpanManager instance) { + return INSTANCE.getAndSet(instance); + } + + /** + * Return the active {@link Span}. + * + * @return The active Span, or the NoopSpan if there is no active span. + */ + public static Span activeSpan() { + try { + Span activeSpan = getInstance().getActiveSpan(); + if (activeSpan != null) return activeSpan; + } catch (Exception activeSpanException) { + LOGGER.log(Level.WARNING, "Could not obtain active span.", activeSpanException); + } + return NoopSpan.INSTANCE; + } + + /** + * Makes span the active span within the running process. + *

+ * Any exception thrown by the {@link #setActiveSpan(Span) implementation} is logged and will return + * no {@link SpanDeactivator} (null) because tracing code must not break application functionality. + * + * @param span The span to become the active span. + * @return The object that will restore any currently active deactivated, or null. + * @see #activeSpan() + * @see #deactivate(SpanDeactivator) + */ + public static SpanDeactivator activate(Span span) { + try { + if (span == null) span = NoopSpan.INSTANCE; + return getInstance().setActiveSpan(span); + } catch (Exception activationException) { + LOGGER.log(Level.WARNING, "Could not activate {0}.", new Object[]{span, activationException}); + return null; + } + } + + /** + * Invokes the given {@link SpanDeactivator} which should normally* reactivate the parent of the active span + * within the running process. + *

+ * Any exception thrown by the implementation is logged and swallowed because tracing code must not break + * application functionality. + *

+ * *) should normally because the default stack unwinding algorithm is a little more intricate + * to deal with out-of-order deactivation. + * + * @param deactivator The deactivator that was received upon span activation. + * @see #activate(Span) + */ + public static void deactivate(SpanDeactivator deactivator) { + if (deactivator != null) try { + getInstance().deactivateSpan(deactivator); + } catch (Exception deactivationException) { + LOGGER.log(Level.WARNING, "Could not deactivate {0}.", new Object[]{deactivator, deactivationException}); + } + } + + /** + * Deactivates any active span including any active parents. + *

+ * This method allows boundary filters to deactivate all active spans + * before returning control over their Thread, which may end up back in some threadpool. + * + * @return true if any spans were deactivated, otherwise false. + */ + public static boolean deactivateAll() { + try { + return getInstance().deactivateAllSpans(); + } catch (Exception clearException) { + LOGGER.log(Level.WARNING, "Could not clear active spans.", clearException); + return false; + } + } + + /** + * Wraps the {@link Callable} to execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @param callable The callable to wrap. + * @param The return type of the wrapped call. + * @return The wrapped call executing with the active span of the scheduling process. + */ + public static SpanAwareCallable spanAware(Callable callable) { + return SpanAwareCallable.of(callable); + } + + /** + * Wraps the {@link Runnable} to execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @param runnable The runnable to wrap. + * @return The wrapped runnable executing with the active span of the scheduling process. + */ + public static SpanAwareRunnable spanAware(Runnable runnable) { + return SpanAwareRunnable.of(runnable); + } + + /** + * Implementation of the {@link #activeSpan()} method. + */ + protected abstract Span getActiveSpan(); + + /** + * Implementation of the {@link #activate(Span)} method. + */ + protected abstract SpanDeactivator setActiveSpan(Span span); + + /** + * Implementation of the {@link #deactivate(SpanDeactivator)} method. + */ + protected abstract void deactivateSpan(SpanDeactivator deactivator); + + /** + * Implementation of the {@link #deactivateAll()} method. + */ + protected abstract boolean deactivateAllSpans(); + + /** + * Loads a single service implementation from {@link ServiceLoader} or returns the + * {@link #setActiveSpanManager(ActiveSpanManager) explicitly configured} manager instance. + * + * @return The single implementation or the ThreadLocalSpanManager. + */ + private static ActiveSpanManager getInstance() { + ActiveSpanManager instance = INSTANCE.get(); + if (instance == null) { + ActiveSpanManager singleton = null; + + // Use the service instance from the ServiceLoader if there is exactly one implementation. + for (Iterator implementations = ServiceLoader + .load(ActiveSpanManager.class, ActiveSpanManager.class.getClassLoader()) + .iterator(); singleton == null && implementations.hasNext(); ) { + ActiveSpanManager implementation = implementations.next(); + if (implementation != null) { + LOGGER.log(Level.FINEST, "Service loaded: {0}.", implementation); + if (implementations.hasNext()) { // Don't load the next implementation but fall-back to default. + LOGGER.log(Level.WARNING, "More than one ActiveSpanManager service implementation found. " + + "Falling back to default implementation."); + break; + } else { + singleton = implementation; + } + } + } + + if (singleton == null) { + LOGGER.log(Level.FINEST, "No ActiveSpanManager service implementation found. " + + "Falling back to default implementation."); + singleton = new ThreadLocalSpanManager(); + } + + while (instance == null) { // deal with race condition (should rarely repeat) + INSTANCE.compareAndSet(null, singleton); + instance = INSTANCE.get(); + } + LOGGER.log(Level.FINE, "Singleton ActiveSpanManager implementation: {0}.", instance); + } + return instance; + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java new file mode 100644 index 0000000..fb4803e --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java @@ -0,0 +1,94 @@ +package io.opentracing.contrib.activespan; + +import io.opentracing.NoopSpan; +import io.opentracing.Span; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default ThreadLocal-based implementation of the {@link ActiveSpanManager} class that implements the following + * stack unwinding algorithm upon deactivation: + *

    + *
  1. If the deactivated managed span is not the active span, the active span is left alone.
  2. + *
  3. Otherwise, the first parent that is not yet deactivated is set as the new active span.
  4. + *
  5. If no active parents remain, the active span {@link ThreadLocal} is cleared.
  6. + *
  7. Consecutive deactivate() calls for already deactivated spans will be ignored.
  8. + *
+ * + * @author Sjoerd Talsma + */ +final class ThreadLocalSpanManager extends ActiveSpanManager { + + private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); + private static final ThreadLocal ACTIVE = new ThreadLocal(); + + ThreadLocalSpanManager() { + } + + public Span getActiveSpan() { + final ManagedSpan activeSpan = ACTIVE.get(); + return activeSpan != null ? activeSpan.span : NoopSpan.INSTANCE; + } + + public SpanDeactivator setActiveSpan(Span span) { + final ManagedSpan managedSpan = new ManagedSpan(span); + ACTIVE.set(managedSpan); + return managedSpan; + } + + public void deactivateSpan(SpanDeactivator deactivator) { + if (!(deactivator instanceof ManagedSpan)) { + throw new IllegalArgumentException( + "Cannot deactivate " + deactivator + ". It was not issued by this ActiveSpanManager!"); + } + ((ManagedSpan) deactivator).deactivate(); + } + + public boolean deactivateAllSpans() { + boolean deactivated = ACTIVE.get() != null; + ACTIVE.remove(); + return deactivated; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } + + private static final class ManagedSpan implements SpanDeactivator { + private final ManagedSpan parent = ACTIVE.get(); + private final Span span; + private final AtomicBoolean deactivated = new AtomicBoolean(false); + + private ManagedSpan(final Span span) { + this.span = span; + } + + private void deactivate() { + if (deactivated.compareAndSet(false, true)) { + ManagedSpan current = ACTIVE.get(); + if (this == current) { + while (current != null && current.deactivated.get()) { + current = current.parent; + } + if (current == null) ACTIVE.remove(); + else ACTIVE.set(current); + LOGGER.log(Level.FINER, "Deactivated {0} and restored active span to {1}.", + new Object[]{this, current}); + } else { + LOGGER.log(Level.FINE, "Deactivated {0} without affecting active span {1}.", + new Object[]{this, current}); + } + } else { + LOGGER.log(Level.FINEST, "No action needed, {0} was already deactivated.", this); + } + } + + @Override + public String toString() { + return "ManagedSpan{" + span + '}'; + } + } +} diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java new file mode 100644 index 0000000..9587156 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java @@ -0,0 +1,68 @@ +package io.opentracing.contrib.activespan.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; + +import java.util.concurrent.Callable; + +/** + * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @author Sjoerd Talsma + */ +public class SpanAwareCallable implements Callable { + + protected final Callable delegate; + private final Span activeSpanOfScheduler; + + protected SpanAwareCallable(Callable delegate, Span activeSpanOfScheduler) { + if (delegate == null) throw new NullPointerException("Callable delegate is ."); + this.delegate = delegate; + this.activeSpanOfScheduler = activeSpanOfScheduler; + } + + /** + * Creates a new callable that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @param delegate The delegate callable to execute (required, non-null). + * @param The result type of the call. + * @return The 'span aware' callable that will propagate the currently active span to the new thread. + * @see ActiveSpanManager#activeSpan() + */ + public static SpanAwareCallable of(Callable delegate) { + return new SpanAwareCallable(delegate, ActiveSpanManager.activeSpan()); + } + + /** + * This method allows the caller to override the active span in the new thread. + *

+ * Please note: it is not necessary to call this method with the + * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Callable) by default}. + * + * @param parentSpan The span to use as active parent in the new thread. + * @return A new runnable object that will propagate the parent span to another thread. + * @see #of(Callable) + */ + public SpanAwareCallable withActiveSpan(Span parentSpan) { + return new SpanAwareCallable(delegate, parentSpan); + } + + /** + * Performs the delegate call with the specified parent span. + * + * @return The result from the original call. + * @throws Exception if the original call threw an exception. + */ + public T call() throws Exception { + final SpanDeactivator parentContext = ActiveSpanManager.activate(activeSpanOfScheduler); + try { + return delegate.call(); + } finally { + ActiveSpanManager.deactivate(parentContext); + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java new file mode 100644 index 0000000..89396af --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java @@ -0,0 +1,101 @@ +package io.opentracing.contrib.activespan.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.activespan.ActiveSpanManager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.*; + +/** + * {@link ExecutorService} wrapper that will propagate the {@link ActiveSpanManager#activeSpan() active span} + * into the calls that are scheduled. + * + * @author Sjoerd Talsma + */ +public class SpanAwareExecutorService implements ExecutorService { + protected final ExecutorService delegate; + + protected SpanAwareExecutorService(ExecutorService delegate) { + if (delegate == null) throw new NullPointerException("Delegate executor service is ."); + this.delegate = delegate; + } + + public static ExecutorService wrap(final ExecutorService delegate) { + return delegate instanceof SpanAwareExecutorService ? (SpanAwareExecutorService) delegate + : new SpanAwareExecutorService(delegate); + } + + public void execute(Runnable command) { + delegate.execute(SpanAwareRunnable.of(command)); + } + + public Future submit(Runnable task) { + return delegate.submit(SpanAwareRunnable.of(task)); + } + + public Future submit(Runnable task, T result) { + return delegate.submit(SpanAwareRunnable.of(task), result); + } + + public Future submit(Callable task) { + return delegate.submit(SpanAwareCallable.of(task)); + } + + public List> invokeAll(Collection> tasks) throws InterruptedException { + return delegate.invokeAll(spanAwareTasks(tasks)); + } + + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate.invokeAll(spanAwareTasks(tasks), timeout, unit); + } + + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return delegate.invokeAny(spanAwareTasks(tasks)); + } + + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.invokeAny(spanAwareTasks(tasks), timeout, unit); + } + + /** + * Wraps {@link SpanAwareCallable} objects. + * + * @param tasks The tasks to be scheduled. + * @param The common type of all scheduled tasks. + * @return A new collection of 'span aware' callable objects that run with the active span of the scheduling service. + */ + protected Collection> spanAwareTasks(final Collection> tasks) { + if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); + final Collection> result = new ArrayList>(tasks.size()); + final Span activeSpan = ActiveSpanManager.activeSpan(); + for (Callable task : tasks) { + result.add(new SpanAwareCallable(task, activeSpan)); + } + return result; + } + + public void shutdown() { + delegate.shutdown(); + } + + public List shutdownNow() { + return delegate.shutdownNow(); + } + + public boolean isShutdown() { + return delegate.isShutdown(); + } + + public boolean isTerminated() { + return delegate.isTerminated(); + } + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate.awaitTermination(timeout, unit); + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java new file mode 100644 index 0000000..b910c1e --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java @@ -0,0 +1,93 @@ +package io.opentracing.contrib.activespan.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import static io.opentracing.contrib.activespan.concurrent.SpanAwareExecutorService.wrap; + +/** + * @author Sjoerd Talsma + * @navassoc - delegatesTo - Executors + */ +public final class SpanAwareExecutors { + + /** + * Private constructor to avoid instantiation of this utility class. + */ + private SpanAwareExecutors() { + throw new UnsupportedOperationException(); + } + + /** + * This method returns a {@link Executors#newFixedThreadPool(int) fixed threadpool} that propagates + * the active span into the started threads. + * + * @param nThreads the number of threads in the pool + * @return the newly created thread pool + * @see Executors#newFixedThreadPool(int) + */ + public static ExecutorService newFixedThreadPool(int nThreads) { + return wrap(Executors.newFixedThreadPool(nThreads)); + } + + /** + * This method returns a {@link Executors#newFixedThreadPool(int, ThreadFactory) fixed threadpool} that propagates + * the active span into the started threads. + * + * @param nThreads the number of threads in the pool + * @param threadFactory the factory to use when creating new threads + * @return the newly created thread pool + * @see Executors#newFixedThreadPool(int, ThreadFactory) + */ + public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); + } + + /** + * This method returns a {@link Executors#newSingleThreadExecutor() single-threaded executor} that propagates + * the active span into the started thread. + * + * @return the newly created single-theaded executor + * @see Executors#newSingleThreadExecutor() + */ + public static ExecutorService newSingleThreadExecutor() { + return wrap(Executors.newSingleThreadExecutor()); + } + + /** + * This method returns a {@link Executors#newSingleThreadExecutor(ThreadFactory) single-threaded executor} + * that propagates the active span into the started thread. + * + * @param threadFactory the factory to use when creating new threads + * @return the newly created single-theaded executor + * @see Executors#newSingleThreadExecutor(ThreadFactory) + */ + public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return wrap(Executors.newSingleThreadExecutor(threadFactory)); + } + + /** + * This method returns a {@link Executors#newCachedThreadPool() cached threadpool} that propagates + * the active span into the started threads. + * + * @return the newly created thread pool + * @see Executors#newCachedThreadPool() + */ + public static ExecutorService newCachedThreadPool() { + return wrap(Executors.newCachedThreadPool()); + } + + /** + * This method returns a {@link Executors#newCachedThreadPool(ThreadFactory) cached threadpool} that propagates + * the active span into the started threads. + * + * @param threadFactory the factory to use when creating new threads + * @return the newly created thread pool + * @see Executors#newCachedThreadPool(ThreadFactory) + */ + public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return wrap(Executors.newCachedThreadPool(threadFactory)); + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java new file mode 100644 index 0000000..db15d21 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java @@ -0,0 +1,62 @@ +package io.opentracing.contrib.activespan.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; + +/** + * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @author Sjoerd Talsma + */ +public class SpanAwareRunnable implements Runnable { + + protected final Runnable delegate; + private final Span parentSpan; + + protected SpanAwareRunnable(Runnable delegate, Span parentSpan) { + if (delegate == null) throw new NullPointerException("Runnable delegate is ."); + this.delegate = delegate; + this.parentSpan = parentSpan; + } + + /** + * Creates a new runnable that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * from the scheduling thread. + * + * @param delegate The delegate runnable to execute (required, non-null). + * @return The wrapped runnable that propagates the active span to another thread. + * @see ActiveSpanManager#activeSpan() + */ + public static SpanAwareRunnable of(Runnable delegate) { + return new SpanAwareRunnable(delegate, ActiveSpanManager.activeSpan()); + } + + /** + * This method allows the caller to override the active span in the new thread. + *

+ * Please note: it is not necessary to call this method with the + * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Runnable) by default}. + * + * @param parentSpan The span to use as active parent in the new thread. + * @return A new runnable object that will propagate the parent span to another thread. + * @see #of(Runnable) + */ + public SpanAwareRunnable withParent(Span parentSpan) { + return new SpanAwareRunnable(delegate, parentSpan); + } + + /** + * Performs the runnable action with the specified parent span. + */ + public void run() { + SpanDeactivator deactivator = ActiveSpanManager.activate(parentSpan); + try { + delegate.run(); + } finally { + ActiveSpanManager.deactivate(deactivator); + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java new file mode 100644 index 0000000..fe39ee8 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java @@ -0,0 +1,147 @@ +package io.opentracing.contrib.activespan.tracer; + +import io.opentracing.NoopSpan; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Implementation of an 'active span'.
+ * This active span will deactivate itself when it is {@link #finish() finished} or {@link #close() closed}.
+ * All other span functionality is forwareded to the delegate Span. + * + * @author Sjoerd Talsma + */ +final class ActiveSpan implements Span { + + protected Span delegate; + private final SpanDeactivator deactivator; + private final AtomicBoolean deactivated = new AtomicBoolean(false); + + ActiveSpan(Span delegate, SpanDeactivator deactivator) { + if (delegate == null) throw new NullPointerException("Delegate span was ."); + this.delegate = delegate; + this.deactivator = deactivator; + } + + /** + * Replaces the {@link #delegate} Span by a delegated-method result. + *

+ * For null or {@link NoopSpan} the active span builder short-circuits to the noop Span, + * effectively disabling further tracing. + * + * @param span The span returned from the delegate (normally '== delegate'). + * @return Either this re-wrapped ActiveSpan or the NoopSpan. + */ + protected Span rewrap(Span span) { + if (span == null || span instanceof NoopSpan) return NoopSpan.INSTANCE; + this.delegate = span; + return this; + } + + /** + * Deactivates this active span (only once). + */ + private void deactivate() { + if (deactivated.compareAndSet(false, true)) ActiveSpanManager.deactivate(deactivator); + } + + /** + * Finishes the delegate and deactivates this active span. + */ + public void finish() { + try { + delegate.finish(); + } finally { + this.deactivate(); + } + } + + /** + * Finishes the delegate and deactivates this active span. + */ + public void finish(long finishMicros) { + try { + delegate.finish(finishMicros); + } finally { + this.deactivate(); + } + } + + /** + * Finishes the delegate and deactivates this active span. + */ + public void close() { + try { + delegate.close(); + } finally { + this.deactivate(); + } + } + + // Default behaviour is forwarded to the delegate Span: + + public SpanContext context() { + return delegate.context(); + } + + public Span setTag(String key, String value) { + return rewrap(delegate.setTag(key, value)); + } + + public Span setTag(String key, boolean value) { + return rewrap(delegate.setTag(key, value)); + } + + public Span setTag(String key, Number value) { + return rewrap(delegate.setTag(key, value)); + } + + public Span log(Map fields) { + return rewrap(delegate.log(fields)); + } + + public Span log(long timestampMicroseconds, Map fields) { + return rewrap(delegate.log(timestampMicroseconds, fields)); + } + + public Span log(String event) { + return rewrap(delegate.log(event)); + } + + public Span log(long timestampMicroseconds, String event) { + return rewrap(delegate.log(timestampMicroseconds, event)); + } + + public Span setBaggageItem(String key, String value) { + return rewrap(delegate.setBaggageItem(key, value)); + } + + public String getBaggageItem(String key) { + return delegate.getBaggageItem(key); + } + + public Span setOperationName(String operationName) { + return rewrap(delegate.setOperationName(operationName)); + } + + @SuppressWarnings("deprecation") // We simply delegate this method as we're told. + public Span log(String eventName, Object payload) { + return rewrap(delegate.log(eventName, payload)); + } + + @SuppressWarnings("deprecation") // We simply delegate this method as we're told. + public Span log(long timestampMicroseconds, String eventName, Object payload) { + return rewrap(delegate.log(timestampMicroseconds, eventName, payload)); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{delegate=" + delegate + '}'; + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java new file mode 100644 index 0000000..a74fa30 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java @@ -0,0 +1,101 @@ +package io.opentracing.contrib.activespan.tracer; + +import io.opentracing.NoopSpanBuilder; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer.SpanBuilder; +import io.opentracing.contrib.activespan.ActiveSpanManager; + +import java.util.Map; + +/** + * {@link SpanBuilder} that forwards all methods to a delegate.
+ * Only the {@link #start()} method is overridden, {@link ActiveSpanManager#activate(Span) activating} + * the started {@link Span} and wrapping it in an {@link ActiveSpan} object.
+ * The {@link ActiveSpan} object {@link ActiveSpanManager.SpanDeactivator deactivates} the span automatically + * when it is {@link ActiveSpan#finish() finished} or {@link ActiveSpan#close() closed}. + * + * @author Sjoerd Talsma + * @see ActiveSpanManager#activate(Span) + * @see ActiveSpan#finish() + */ +final class ActiveSpanBuilder implements SpanBuilder { + + protected SpanBuilder delegate; + + ActiveSpanBuilder(SpanBuilder delegate) { + if (delegate == null) throw new NullPointerException("Delegate SpanBuilder was ."); + this.delegate = delegate; + } + + /** + * Replaces the {@link #delegate} SpanBuilder by a delegated-method result. + *

+ * For null or {@link NoopSpanBuilder} the active span builder short-circuits to the noop SpanBuilder, + * similar to the AbstractSpanBuilder implementation. + * + * @param spanBuilder The builder returned from the delegate (normally '== delegate'). + * @return Either this re-wrapped ActiveSpanBuilder or the NoopSpanBuilder. + */ + SpanBuilder rewrap(SpanBuilder spanBuilder) { + if (spanBuilder == null || spanBuilder instanceof NoopSpanBuilder) return NoopSpanBuilder.INSTANCE; + this.delegate = spanBuilder; + return this; + } + + /** + * Starts the built Span and {@link ActiveSpanManager#activate(Span) activates} it. + * + * @return a new 'active' Span that deactivates itself upon finish or close calls. + * @see ActiveSpan#finish() + * @see ActiveSpanManager#activate(Span) + */ + @Override + public Span start() { + // Return a new 'active' span that deactivates itself again when finished. + final Span newSpan = delegate.start(); + return new ActiveSpan(newSpan, ActiveSpanManager.activate(newSpan)); + } + + // All other methods are forwarded to the delegate SpanBuilder. + + public SpanBuilder asChildOf(SpanContext parent) { + if (parent instanceof ActiveSpanBuilder) { + parent = ((ActiveSpanBuilder) parent).delegate; + } + return rewrap(delegate.asChildOf(parent)); + } + + public SpanBuilder asChildOf(Span parent) { + if (parent instanceof ActiveSpan) parent = ((ActiveSpan) parent).delegate; + return rewrap(delegate.asChildOf(parent)); + } + + public SpanBuilder addReference(String referenceType, SpanContext referencedContext) { + if (referencedContext instanceof ActiveSpanBuilder) { + referencedContext = ((ActiveSpanBuilder) referencedContext).delegate; + } + return rewrap(delegate.addReference(referenceType, referencedContext)); + } + + public SpanBuilder withTag(String key, String value) { + return rewrap(delegate.withTag(key, value)); + } + + public SpanBuilder withTag(String key, boolean value) { + return rewrap(delegate.withTag(key, value)); + } + + public SpanBuilder withTag(String key, Number value) { + return rewrap(delegate.withTag(key, value)); + } + + public SpanBuilder withStartTimestamp(long microseconds) { + return rewrap(delegate.withStartTimestamp(microseconds)); + } + + public Iterable> baggageItems() { + return delegate.baggageItems(); + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java new file mode 100644 index 0000000..5a4da8c --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java @@ -0,0 +1,51 @@ +package io.opentracing.contrib.activespan.tracer; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.propagation.Format; + +/** + * Wrapper that forwards all calls to another {@link Tracer} implementation.
+ * {@link io.opentracing.Span Spans} created with this tracer are + * {@link ActiveSpanManager#setActiveSpan(Span) activated} when started and + * {@link ActiveSpanManager#deactivate(ActiveSpanManager.SpanDeactivator) deactivated} when finished. + *

+ * The {@link SpanBuilder} of this Tracer will short-circuit to the + * {@link io.opentracing.NoopSpanBuilder NoopSpanBuilder}. + * This means {@link io.opentracing.NoopSpan NoopSpan} + * instances will be activated or deactivated through this tracer. + * + * @author Sjoerd Talsma + */ +public class ActiveSpanTracer implements Tracer { + + protected final Tracer delegate; + + public ActiveSpanTracer(Tracer delegate) { + if (delegate == null) throw new NullPointerException("The delegate Tracer implementation is ."); + this.delegate = delegate; + } + + public void inject(SpanContext spanContext, Format format, C carrier) { + if (spanContext instanceof ActiveSpanBuilder) { // Weird that Builder extends Context! + spanContext = ((ActiveSpanBuilder) spanContext).delegate; + } + delegate.inject(spanContext, format, carrier); + } + + public SpanContext extract(Format format, C carrier) { + return delegate.extract(format, carrier); + } + + public SpanBuilder buildSpan(String operationName) { + return new ActiveSpanBuilder(delegate.buildSpan(operationName)); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{delegate=" + delegate + '}'; + } + +} diff --git a/src/main/resources/META-INF/opentracing-activespan.properties b/src/main/resources/META-INF/opentracing-activespan.properties new file mode 100644 index 0000000..29e7dce --- /dev/null +++ b/src/main/resources/META-INF/opentracing-activespan.properties @@ -0,0 +1,3 @@ +version=${project.version} +build.timestamp=${build.timestamp} +build.revision=${git.revision} From 6844cf7c5e53594e3d90589db9b4843c2cfad312 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 16 Dec 2016 13:41:35 +0100 Subject: [PATCH 02/51] Added an initial README draft. --- README.md | 43 ++++++++++++++++++- .../activespan/tracer/ActiveSpanTracer.java | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1109b07..477108d 100644 --- a/README.md +++ b/README.md @@ -1 +1,42 @@ -# java-activespan \ No newline at end of file +# java-activespan + +This library provides means to manage an _active span_ within the current process +and propagate it to other threads. + +## ActiveSpanManager + +The core of this library, this class provides + 1. The `activeSpan()` method to return the _active span_ in the current process. + If there is no active span, a `NoopSpan` is returned instead. + 2. Modifications to the _active span_ can be made through the `activate()` and `deactivate()` methods. + 3. Utility `spanAware()` methods to create _span aware_ `Runnable` and `Callable` instances + that execute with the active span from the scheduling thread. + +## ActiveSpanTracer + +This `Tracer` makes managing the _active span_ a little easier. + 1. It is a _wrapper_ that forwards all calls to another `Tracer` implementation. + 2. `Span` instances created with this tracer are automatically + _activated_ when started and _deactivated_ when finished. + 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`, + meaning any `NoopSpan` instances will **not** be activated or deactivated automatically. + +## SpanAwareExecutorService + +This `ExecutorService` wraps an existing threadpool and propagates the _active span_ +of the caller into the tasks it executes. +Convenience variants of the `Executors` JVM factory methods +are provided by the `SpanAwareExecutors` class. + +## Custom span managers + +It is possible to provide a custom implementation of `ActiveSpanManager`. +This may be useful if you already have a method in place to propagate contextual information +from one thread to another. Creating a custom manager allows you to piggyback the _active span_ on +your existing propagation mechanism. + +To enable a custom manager: + 1. Programmatically, call the `ActiveSpanManager.setActiveSpanManager()` with the new implementation. + 2. Using the standard Java `ServiceLoader`, bundle a + `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file with + the classname of the implementation. diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java index 5a4da8c..3806081 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java @@ -15,7 +15,7 @@ * The {@link SpanBuilder} of this Tracer will short-circuit to the * {@link io.opentracing.NoopSpanBuilder NoopSpanBuilder}. * This means {@link io.opentracing.NoopSpan NoopSpan} - * instances will be activated or deactivated through this tracer. + * instances will not be activated or deactivated through this tracer. * * @author Sjoerd Talsma */ From 1b9c95f415b57b15e7403be4cc98a2498373f519 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 16 Dec 2016 13:53:21 +0100 Subject: [PATCH 03/51] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 477108d..1cf73a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # java-activespan -This library provides means to manage an _active span_ within the current process +This library provides a way to manage an _active span_ within the current process and propagate it to other threads. ## ActiveSpanManager @@ -40,3 +40,7 @@ To enable a custom manager: 2. Using the standard Java `ServiceLoader`, bundle a `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file with the classname of the implementation. + +## Examples + +_TODO create examples_ From f4752c802f0ac438cef7b77b9817945c9a05e00c Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 16 Dec 2016 15:43:32 +0100 Subject: [PATCH 04/51] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1cf73a9..0f31267 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ are provided by the `SpanAwareExecutors` class. It is possible to provide a custom implementation of `ActiveSpanManager`. This may be useful if you already have a method in place to propagate contextual information -from one thread to another. Creating a custom manager allows you to piggyback the _active span_ on -your existing propagation mechanism. +from one thread to another. Creating a custom manager is one way to piggyback the _active span_ on +your existing propagation mechanism. Another way would be to call _activate_ and _deactivate_ +at the appropriate moments in your propagation mechanism. To enable a custom manager: 1. Programmatically, call the `ActiveSpanManager.setActiveSpanManager()` with the new implementation. From a4367be1c354d922e4224b98a19e65c99112d11c Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 19 Dec 2016 16:45:53 +0100 Subject: [PATCH 05/51] Mainly documentation changes. --- README.md | 2 +- .../contrib/activespan/ActiveSpanManager.java | 12 ++++++------ ...Callable.java => CallableWithActiveSpan.java} | 14 +++++++------- ...Runnable.java => RunnableWithActiveSpan.java} | 16 ++++++++-------- .../concurrent/SpanAwareExecutorService.java | 12 ++++++------ .../activespan/tracer/ActiveSpanBuilder.java | 6 +++++- .../activespan/tracer/ActiveSpanTracer.java | 4 ++-- 7 files changed, 35 insertions(+), 31 deletions(-) rename src/main/java/io/opentracing/contrib/activespan/concurrent/{SpanAwareCallable.java => CallableWithActiveSpan.java} (79%) rename src/main/java/io/opentracing/contrib/activespan/concurrent/{SpanAwareRunnable.java => RunnableWithActiveSpan.java} (74%) diff --git a/README.md b/README.md index 0f31267..4e8678f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The core of this library, this class provides 1. The `activeSpan()` method to return the _active span_ in the current process. If there is no active span, a `NoopSpan` is returned instead. 2. Modifications to the _active span_ can be made through the `activate()` and `deactivate()` methods. - 3. Utility `spanAware()` methods to create _span aware_ `Runnable` and `Callable` instances + 3. Utility `withActiveSpan()` methods to create `Runnable` and `Callable` instances that execute with the active span from the scheduling thread. ## ActiveSpanTracer diff --git a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java index 26c252b..eccd9ca 100644 --- a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java +++ b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java @@ -2,8 +2,8 @@ import io.opentracing.NoopSpan; import io.opentracing.Span; -import io.opentracing.contrib.activespan.concurrent.SpanAwareCallable; -import io.opentracing.contrib.activespan.concurrent.SpanAwareRunnable; +import io.opentracing.contrib.activespan.concurrent.CallableWithActiveSpan; +import io.opentracing.contrib.activespan.concurrent.RunnableWithActiveSpan; import java.util.Iterator; import java.util.ServiceLoader; @@ -134,8 +134,8 @@ public static boolean deactivateAll() { * @param The return type of the wrapped call. * @return The wrapped call executing with the active span of the scheduling process. */ - public static SpanAwareCallable spanAware(Callable callable) { - return SpanAwareCallable.of(callable); + public static CallableWithActiveSpan withActiveSpan(Callable callable) { + return CallableWithActiveSpan.of(callable); } /** @@ -145,8 +145,8 @@ public static SpanAwareCallable spanAware(Callable callable) { * @param runnable The runnable to wrap. * @return The wrapped runnable executing with the active span of the scheduling process. */ - public static SpanAwareRunnable spanAware(Runnable runnable) { - return SpanAwareRunnable.of(runnable); + public static RunnableWithActiveSpan withActiveSpan(Runnable runnable) { + return RunnableWithActiveSpan.of(runnable); } /** diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java similarity index 79% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java rename to src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java index 9587156..91b255d 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareCallable.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java @@ -12,12 +12,12 @@ * * @author Sjoerd Talsma */ -public class SpanAwareCallable implements Callable { +public class CallableWithActiveSpan implements Callable { protected final Callable delegate; private final Span activeSpanOfScheduler; - protected SpanAwareCallable(Callable delegate, Span activeSpanOfScheduler) { + protected CallableWithActiveSpan(Callable delegate, Span activeSpanOfScheduler) { if (delegate == null) throw new NullPointerException("Callable delegate is ."); this.delegate = delegate; this.activeSpanOfScheduler = activeSpanOfScheduler; @@ -32,8 +32,8 @@ protected SpanAwareCallable(Callable delegate, Span activeSpanOfScheduler) { * @return The 'span aware' callable that will propagate the currently active span to the new thread. * @see ActiveSpanManager#activeSpan() */ - public static SpanAwareCallable of(Callable delegate) { - return new SpanAwareCallable(delegate, ActiveSpanManager.activeSpan()); + public static CallableWithActiveSpan of(Callable delegate) { + return new CallableWithActiveSpan(delegate, ActiveSpanManager.activeSpan()); } /** @@ -42,12 +42,12 @@ public static SpanAwareCallable of(Callable delegate) { * Please note: it is not necessary to call this method with the * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Callable) by default}. * - * @param parentSpan The span to use as active parent in the new thread. + * @param activeSpan The span to become the active span when calling the delegate. * @return A new runnable object that will propagate the parent span to another thread. * @see #of(Callable) */ - public SpanAwareCallable withActiveSpan(Span parentSpan) { - return new SpanAwareCallable(delegate, parentSpan); + public CallableWithActiveSpan withActiveSpan(Span activeSpan) { + return new CallableWithActiveSpan(delegate, activeSpan); } /** diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java similarity index 74% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java rename to src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java index db15d21..f1fec93 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareRunnable.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java @@ -10,12 +10,12 @@ * * @author Sjoerd Talsma */ -public class SpanAwareRunnable implements Runnable { +public class RunnableWithActiveSpan implements Runnable { protected final Runnable delegate; private final Span parentSpan; - protected SpanAwareRunnable(Runnable delegate, Span parentSpan) { + protected RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { if (delegate == null) throw new NullPointerException("Runnable delegate is ."); this.delegate = delegate; this.parentSpan = parentSpan; @@ -29,8 +29,8 @@ protected SpanAwareRunnable(Runnable delegate, Span parentSpan) { * @return The wrapped runnable that propagates the active span to another thread. * @see ActiveSpanManager#activeSpan() */ - public static SpanAwareRunnable of(Runnable delegate) { - return new SpanAwareRunnable(delegate, ActiveSpanManager.activeSpan()); + public static RunnableWithActiveSpan of(Runnable delegate) { + return new RunnableWithActiveSpan(delegate, ActiveSpanManager.activeSpan()); } /** @@ -39,12 +39,12 @@ public static SpanAwareRunnable of(Runnable delegate) { * Please note: it is not necessary to call this method with the * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Runnable) by default}. * - * @param parentSpan The span to use as active parent in the new thread. - * @return A new runnable object that will propagate the parent span to another thread. + * @param activeSpan The span to become the active span when running the delegate. + * @return A new runnable object that will propagate the given span to another thread. * @see #of(Runnable) */ - public SpanAwareRunnable withParent(Span parentSpan) { - return new SpanAwareRunnable(delegate, parentSpan); + public RunnableWithActiveSpan withParent(Span activeSpan) { + return new RunnableWithActiveSpan(delegate, activeSpan); } /** diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java index 89396af..3459db2 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java @@ -28,19 +28,19 @@ public static ExecutorService wrap(final ExecutorService delegate) { } public void execute(Runnable command) { - delegate.execute(SpanAwareRunnable.of(command)); + delegate.execute(RunnableWithActiveSpan.of(command)); } public Future submit(Runnable task) { - return delegate.submit(SpanAwareRunnable.of(task)); + return delegate.submit(RunnableWithActiveSpan.of(task)); } public Future submit(Runnable task, T result) { - return delegate.submit(SpanAwareRunnable.of(task), result); + return delegate.submit(RunnableWithActiveSpan.of(task), result); } public Future submit(Callable task) { - return delegate.submit(SpanAwareCallable.of(task)); + return delegate.submit(CallableWithActiveSpan.of(task)); } public List> invokeAll(Collection> tasks) throws InterruptedException { @@ -62,7 +62,7 @@ public T invokeAny(Collection> tasks, long timeout, Ti } /** - * Wraps {@link SpanAwareCallable} objects. + * Wraps {@link CallableWithActiveSpan} objects. * * @param tasks The tasks to be scheduled. * @param The common type of all scheduled tasks. @@ -73,7 +73,7 @@ protected Collection> spanAwareTasks(final Collection< final Collection> result = new ArrayList>(tasks.size()); final Span activeSpan = ActiveSpanManager.activeSpan(); for (Callable task : tasks) { - result.add(new SpanAwareCallable(task, activeSpan)); + result.add(new CallableWithActiveSpan(task, activeSpan)); } return result; } diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java index a74fa30..3948c96 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java @@ -23,11 +23,15 @@ final class ActiveSpanBuilder implements SpanBuilder { protected SpanBuilder delegate; - ActiveSpanBuilder(SpanBuilder delegate) { + private ActiveSpanBuilder(SpanBuilder delegate) { if (delegate == null) throw new NullPointerException("Delegate SpanBuilder was ."); this.delegate = delegate; } + static SpanBuilder of(SpanBuilder spanBuilder) { + return spanBuilder instanceof NoopSpanBuilder ? spanBuilder : new ActiveSpanBuilder(spanBuilder); + } + /** * Replaces the {@link #delegate} SpanBuilder by a delegated-method result. *

diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java index 3806081..73907b0 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java @@ -9,7 +9,7 @@ /** * Wrapper that forwards all calls to another {@link Tracer} implementation.
* {@link io.opentracing.Span Spans} created with this tracer are - * {@link ActiveSpanManager#setActiveSpan(Span) activated} when started and + * {@link ActiveSpanManager#activate(Span) activated} when started and * {@link ActiveSpanManager#deactivate(ActiveSpanManager.SpanDeactivator) deactivated} when finished. *

* The {@link SpanBuilder} of this Tracer will short-circuit to the @@ -40,7 +40,7 @@ public SpanContext extract(Format format, C carrier) { } public SpanBuilder buildSpan(String operationName) { - return new ActiveSpanBuilder(delegate.buildSpan(operationName)); + return ActiveSpanBuilder.of(delegate.buildSpan(operationName)); } @Override From 8142a7735ff5a47eaf9730157f60a9d983ca51fa Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 21 Dec 2016 07:59:06 +0100 Subject: [PATCH 06/51] Update README.md --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4e8678f..2cc847c 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,22 @@ The core of this library, this class provides ## ActiveSpanTracer -This `Tracer` makes managing the _active span_ a little easier. +This `Tracer` makes managing the _active span_ easier. 1. It is a _wrapper_ that forwards all calls to another `Tracer` implementation. 2. `Span` instances created with this tracer are automatically _activated_ when started and _deactivated_ when finished. - 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`, - meaning any `NoopSpan` instances will **not** be activated or deactivated automatically. + 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`. + This means any `NoopSpan` instances created will have no influence on the active span. ## SpanAwareExecutorService This `ExecutorService` wraps an existing threadpool and propagates the _active span_ of the caller into the tasks it executes. -Convenience variants of the `Executors` JVM factory methods -are provided by the `SpanAwareExecutors` class. +Convenience variants of the java `Executors` factory methods are provided by the `SpanAwareExecutors` class: + - `SpanAwareExecutors.newFixedThreadPool(int)` + - `SpanAwareExecutors.newSingleThreadExecutor()` + - `SpanAwareExecutors.newCachedThreadPool()` + - Variants of the above with additional `ThreadFactory` argument. ## Custom span managers @@ -39,8 +42,8 @@ at the appropriate moments in your propagation mechanism. To enable a custom manager: 1. Programmatically, call the `ActiveSpanManager.setActiveSpanManager()` with the new implementation. 2. Using the standard Java `ServiceLoader`, bundle a - `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file with - the classname of the implementation. + `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file + containing the classname of the implementation. ## Examples From 07d0dc4e62c6767c2fd1e7ad8095151f26272525 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Tue, 3 Jan 2017 17:40:17 +0100 Subject: [PATCH 07/51] Simplification similar to global tracer java library. --- README.md | 35 ++- pom.xml | 86 ------- .../contrib/activespan/ActiveSpanManager.java | 229 ++++++------------ .../contrib/activespan/SpanDeactivator.java | 36 +++ .../activespan/ThreadLocalSpanManager.java | 45 ++-- .../concurrent/CallableWithActiveSpan.java | 12 +- .../concurrent/RunnableWithActiveSpan.java | 12 +- .../concurrent/SpanAwareExecutorService.java | 8 +- .../concurrent/SpanAwareExecutors.java | 30 +-- .../contrib/activespan/tracer/ActiveSpan.java | 6 +- .../activespan/tracer/ActiveSpanBuilder.java | 21 +- .../activespan/tracer/ActiveSpanTracer.java | 10 +- .../opentracing-activespan.properties | 3 - 13 files changed, 190 insertions(+), 343 deletions(-) create mode 100644 src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java delete mode 100644 src/main/resources/META-INF/opentracing-activespan.properties diff --git a/README.md b/README.md index 2cc847c..2a84678 100644 --- a/README.md +++ b/README.md @@ -8,29 +8,40 @@ and propagate it to other threads. The core of this library, this class provides 1. The `activeSpan()` method to return the _active span_ in the current process. If there is no active span, a `NoopSpan` is returned instead. - 2. Modifications to the _active span_ can be made through the `activate()` and `deactivate()` methods. - 3. Utility `withActiveSpan()` methods to create `Runnable` and `Callable` instances - that execute with the active span from the scheduling thread. + 2. The _active span_ can be set through the `activate()` method, + returning a `SpanDeactivator`. + 3. Method `clear()` to deactivate _all active spans_ in a process, including any active parents. + Not intended for 'regular' use, this method is available for boundary filters, + making sure all spans are cleared before the current process is repurposed by a threadpool. -## ActiveSpanTracer +## SpanDeactivator -This `Tracer` makes managing the _active span_ easier. - 1. It is a _wrapper_ that forwards all calls to another `Tracer` implementation. - 2. `Span` instances created with this tracer are automatically - _activated_ when started and _deactivated_ when finished. - 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`. - This means any `NoopSpan` instances created will have no influence on the active span. +Returned by `ActiveSpanManager.activate()` this object can `deactivate()` a +previously activated `Span` again. +It implements `Closeable` for easy use in try-with-resources blocks. + +## Concurrency -## SpanAwareExecutorService +### SpanAwareExecutorService This `ExecutorService` wraps an existing threadpool and propagates the _active span_ of the caller into the tasks it executes. +No `Spans` are started or finished automatically by this executor. Convenience variants of the java `Executors` factory methods are provided by the `SpanAwareExecutors` class: - `SpanAwareExecutors.newFixedThreadPool(int)` - `SpanAwareExecutors.newSingleThreadExecutor()` - `SpanAwareExecutors.newCachedThreadPool()` - Variants of the above with additional `ThreadFactory` argument. +### ActiveSpanTracer + +This `Tracer` makes managing the _active span_ easier. + 1. It is a _wrapper_ that forwards all calls to another `Tracer` implementation. + 2. `Span` instances created with this tracer are automatically + _activated_ when started and _deactivated_ when finished. + 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`. + This means any `NoopSpan` instances created do not automatically become the active span. + ## Custom span managers It is possible to provide a custom implementation of `ActiveSpanManager`. @@ -40,7 +51,7 @@ your existing propagation mechanism. Another way would be to call _activate_ and at the appropriate moments in your propagation mechanism. To enable a custom manager: - 1. Programmatically, call the `ActiveSpanManager.setActiveSpanManager()` with the new implementation. + 1. Programmatically, call the `ActiveSpanManager.set()` with the new implementation. 2. Using the standard Java `ServiceLoader`, bundle a `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file containing the classname of the implementation. diff --git a/pom.xml b/pom.xml index 6db53df..cf66644 100644 --- a/pom.xml +++ b/pom.xml @@ -44,12 +44,10 @@ UTF-8 UTF-8 1.6 - 0.20.2 - io.opentracing opentracing-api @@ -61,20 +59,12 @@ ${opentracing-api.version} - io.opentracing opentracing-mock ${opentracing-api.version} test - - nl.talsmasoftware - context-propagation - 0.3.0 - test - - junit junit @@ -96,42 +86,7 @@ - - - src/main/resources - true - - **/*.properties - - - - src/main/resources - - **/*.properties - - - - - org.codehaus.mojo - buildnumber-maven-plugin - 1.1 - - - validate - - create - - - UNKNOWN - true - git.revision - {0,date,yyyy-MM-dd'T'HH:mm:ss.SSSZ} - build.timestamp - - - - org.apache.maven.plugins maven-compiler-plugin @@ -166,50 +121,9 @@ jar - - nl.talsmasoftware.umldoclet.UMLDoclet - - nl.talsmasoftware - umldoclet - 1.0.8 - - ${project.reporting.outputEncoding} - true - - -umlIncludePackagePrivateClasses false - -umlImageFormat SVG - -umlCommand "hide empty fields" - -umlCommand "hide empty methods" - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - true - - ossrh - https://oss.sonatype.org/ - true - - diff --git a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java index eccd9ca..d3d2698 100644 --- a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java +++ b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java @@ -1,13 +1,9 @@ package io.opentracing.contrib.activespan; -import io.opentracing.NoopSpan; import io.opentracing.Span; -import io.opentracing.contrib.activespan.concurrent.CallableWithActiveSpan; -import io.opentracing.contrib.activespan.concurrent.RunnableWithActiveSpan; import java.util.Iterator; import java.util.ServiceLoader; -import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; @@ -15,29 +11,38 @@ /** * Manages the active {@link Span}.
* A {@link Span} becomes active in the current process after a call to {@link #activate(Span)} - * and can be deactivated again by calling {@link #deactivate(SpanDeactivator)}. - *

- * The default implementation will use a {@link ThreadLocal ThreadLocal storage} to maintain the active {@link Span} - * and its parents. - *

- * Custom implementations can be provided by: - *

    - *
  1. calling {@link #setActiveSpanManager(ActiveSpanManager)} programmatically, or
  2. - *
  3. defining a META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager service file - * containing the classname of the implementation
  4. - *
- * - * @author Sjoerd Talsma - * @navassoc - activeSpan - io.opentracing.Span + * and can be deactivated again by calling {@link SpanDeactivator#deactivate()}. */ public abstract class ActiveSpanManager { private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); private static final AtomicReference INSTANCE = new AtomicReference(); /** - * Managed object to deactivate an activated {@link Span} with. + * Returns the {@linkplain ActiveSpanManager} implementation. + *

+ * The default implementation will use a {@link ThreadLocal ThreadLocal storage} to maintain the active {@link Span} + * and its parents. + *

+ * Custom implementations can be provided by: + *

    + *
  1. calling {@link #set(ActiveSpanManager)} programmatically, or
  2. + *
  3. defining a META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager service file + * containing the classname of the implementation
  4. + *
+ * + * @return The ActiveSpanManager implementation. */ - public interface SpanDeactivator { + public static ActiveSpanManager get() { + ActiveSpanManager activeSpanManager = INSTANCE.get(); + if (activeSpanManager == null) { + ActiveSpanManager implementation = loadSingleSpiImplementation(); + while (activeSpanManager == null && implementation != null) { // handle rare race condition + INSTANCE.compareAndSet(null, implementation); + activeSpanManager = INSTANCE.get(); + } + LOGGER.log(Level.FINE, "ActiveSpanManager implementation: {0}.", activeSpanManager); + } + return activeSpanManager; } /** @@ -48,7 +53,7 @@ public interface SpanDeactivator { * @param instance The overridden implementation to use for in-process span management. * @return Any previous ActiveSpanManager that was initialized before, or null. */ - public static ActiveSpanManager setActiveSpanManager(ActiveSpanManager instance) { + public static ActiveSpanManager set(ActiveSpanManager instance) { return INSTANCE.getAndSet(instance); } @@ -56,160 +61,64 @@ public static ActiveSpanManager setActiveSpanManager(ActiveSpanManager instance) * Return the active {@link Span}. * * @return The active Span, or the NoopSpan if there is no active span. + * @see #activeSpan() */ - public static Span activeSpan() { - try { - Span activeSpan = getInstance().getActiveSpan(); - if (activeSpan != null) return activeSpan; - } catch (Exception activeSpanException) { - LOGGER.log(Level.WARNING, "Could not obtain active span.", activeSpanException); - } - return NoopSpan.INSTANCE; - } + public abstract Span activeSpan(); /** * Makes span the active span within the running process. - *

- * Any exception thrown by the {@link #setActiveSpan(Span) implementation} is logged and will return - * no {@link SpanDeactivator} (null) because tracing code must not break application functionality. * * @param span The span to become the active span. - * @return The object that will restore any currently active deactivated, or null. + * @return The deactivator to undo the activation. * @see #activeSpan() - * @see #deactivate(SpanDeactivator) */ - public static SpanDeactivator activate(Span span) { - try { - if (span == null) span = NoopSpan.INSTANCE; - return getInstance().setActiveSpan(span); - } catch (Exception activationException) { - LOGGER.log(Level.WARNING, "Could not activate {0}.", new Object[]{span, activationException}); - return null; - } - } + public abstract SpanDeactivator activate(Span span); /** - * Invokes the given {@link SpanDeactivator} which should normally* reactivate the parent of the active span - * within the running process. - *

- * Any exception thrown by the implementation is logged and swallowed because tracing code must not break - * application functionality. - *

- * *) should normally because the default stack unwinding algorithm is a little more intricate - * to deal with out-of-order deactivation. - * - * @param deactivator The deactivator that was received upon span activation. - * @see #activate(Span) - */ - public static void deactivate(SpanDeactivator deactivator) { - if (deactivator != null) try { - getInstance().deactivateSpan(deactivator); - } catch (Exception deactivationException) { - LOGGER.log(Level.WARNING, "Could not deactivate {0}.", new Object[]{deactivator, deactivationException}); - } - } - - /** - * Deactivates any active span including any active parents. + * Deactivates all active spans in the current process which includes any active parents. *

* This method allows boundary filters to deactivate all active spans - * before returning control over their Thread, which may end up back in some threadpool. + * before returning control over their Thread, which may end up back in some threadpool.
* - * @return true if any spans were deactivated, otherwise false. + * @see SpanDeactivator#deactivate() */ - public static boolean deactivateAll() { - try { - return getInstance().deactivateAllSpans(); - } catch (Exception clearException) { - LOGGER.log(Level.WARNING, "Could not clear active spans.", clearException); - return false; - } - } - - /** - * Wraps the {@link Callable} to execute with the {@link ActiveSpanManager#activeSpan() active span} - * from the scheduling thread. - * - * @param callable The callable to wrap. - * @param The return type of the wrapped call. - * @return The wrapped call executing with the active span of the scheduling process. - */ - public static CallableWithActiveSpan withActiveSpan(Callable callable) { - return CallableWithActiveSpan.of(callable); - } - - /** - * Wraps the {@link Runnable} to execute with the {@link ActiveSpanManager#activeSpan() active span} - * from the scheduling thread. - * - * @param runnable The runnable to wrap. - * @return The wrapped runnable executing with the active span of the scheduling process. - */ - public static RunnableWithActiveSpan withActiveSpan(Runnable runnable) { - return RunnableWithActiveSpan.of(runnable); - } - - /** - * Implementation of the {@link #activeSpan()} method. - */ - protected abstract Span getActiveSpan(); - - /** - * Implementation of the {@link #activate(Span)} method. - */ - protected abstract SpanDeactivator setActiveSpan(Span span); - - /** - * Implementation of the {@link #deactivate(SpanDeactivator)} method. - */ - protected abstract void deactivateSpan(SpanDeactivator deactivator); - - /** - * Implementation of the {@link #deactivateAll()} method. - */ - protected abstract boolean deactivateAllSpans(); - - /** - * Loads a single service implementation from {@link ServiceLoader} or returns the - * {@link #setActiveSpanManager(ActiveSpanManager) explicitly configured} manager instance. - * - * @return The single implementation or the ThreadLocalSpanManager. - */ - private static ActiveSpanManager getInstance() { - ActiveSpanManager instance = INSTANCE.get(); - if (instance == null) { - ActiveSpanManager singleton = null; - - // Use the service instance from the ServiceLoader if there is exactly one implementation. - for (Iterator implementations = ServiceLoader - .load(ActiveSpanManager.class, ActiveSpanManager.class.getClassLoader()) - .iterator(); singleton == null && implementations.hasNext(); ) { - ActiveSpanManager implementation = implementations.next(); - if (implementation != null) { - LOGGER.log(Level.FINEST, "Service loaded: {0}.", implementation); - if (implementations.hasNext()) { // Don't load the next implementation but fall-back to default. - LOGGER.log(Level.WARNING, "More than one ActiveSpanManager service implementation found. " + - "Falling back to default implementation."); - break; - } else { - singleton = implementation; - } - } - } - - if (singleton == null) { - LOGGER.log(Level.FINEST, "No ActiveSpanManager service implementation found. " + - "Falling back to default implementation."); - singleton = new ThreadLocalSpanManager(); + public abstract void clear(); + +// /** +// * Wraps the {@link Callable} to execute with the {@link ActiveSpanManager#activeSpan() active span} +// * from the scheduling thread. +// * +// * @param callable The callable to wrap. +// * @param The return type of the wrapped call. +// * @return The wrapped call executing with the active span of the scheduling process. +// */ +// public static CallableWithActiveSpan withActiveSpan(Callable callable) { +// return CallableWithActiveSpan.of(callable); +// } +// +// /** +// * Wraps the {@link Runnable} to execute with the {@link ActiveSpanManager#activeSpan() active span} +// * from the scheduling thread. +// * +// * @param runnable The runnable to wrap. +// * @return The wrapped runnable executing with the active span of the scheduling process. +// */ +// public static RunnableWithActiveSpan withActiveSpan(Runnable runnable) { +// return RunnableWithActiveSpan.of(runnable); +// } + + private static ActiveSpanManager loadSingleSpiImplementation() { + // Use the ServiceLoader to find the declared ActiveSpanManager implementation. + Iterator spiImplementations = + ServiceLoader.load(ActiveSpanManager.class, ActiveSpanManager.class.getClassLoader()).iterator(); + if (spiImplementations.hasNext()) { + ActiveSpanManager foundImplementation = spiImplementations.next(); + if (!spiImplementations.hasNext()) { + return foundImplementation; } - - while (instance == null) { // deal with race condition (should rarely repeat) - INSTANCE.compareAndSet(null, singleton); - instance = INSTANCE.get(); - } - LOGGER.log(Level.FINE, "Singleton ActiveSpanManager implementation: {0}.", instance); + LOGGER.log(Level.WARNING, "More than one ActiveSpanManager service implementation found. " + + "Falling back to default ThreadLocal implementation."); } - return instance; + return new ThreadLocalSpanManager(); } - } diff --git a/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java b/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java new file mode 100644 index 0000000..543940e --- /dev/null +++ b/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java @@ -0,0 +1,36 @@ +package io.opentracing.contrib.activespan; + +import io.opentracing.Span; + +import java.io.Closeable; + +/** + * To {@linkplain #deactivate() deactivate} a previously-activated {@link #getSpan() span} with. + */ +public interface SpanDeactivator extends Closeable { + + /** + * Either the active span or one that was the active span at some point. + * + * @return The contained span to be deactivated. + */ + Span getSpan(); + + /** + * Makes the {@link #getSpan() contained span} no longer the active span. + *

+ * Implementation notes: + *

    + *
  1. It is encouraged to restore the active span as it was before the contained span was activated + * (providing stack-like behaviour).
  2. + *
  3. It must be possible to repeatedly call deactivate without any side effects.
  4. + *
+ */ + void deactivate(); + + /** + * Alias for {@link #deactivate()} to allow easy deactivation from try-with-resources. + */ + void close(); + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java index fb4803e..b0c7830 100644 --- a/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java +++ b/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java @@ -11,13 +11,11 @@ * Default ThreadLocal-based implementation of the {@link ActiveSpanManager} class that implements the following * stack unwinding algorithm upon deactivation: *
    - *
  1. If the deactivated managed span is not the active span, the active span is left alone.
  2. - *
  3. Otherwise, the first parent that is not yet deactivated is set as the new active span.
  4. - *
  5. If no active parents remain, the active span {@link ThreadLocal} is cleared.
  6. + *
  7. If the deactivated managed span is not the ACTIVE span, the ACTIVE span is left alone.
  8. + *
  9. Otherwise, the first parent that is not yet deactivated is set as the new ACTIVE span.
  10. + *
  11. If no ACTIVE parents remain, the ACTIVE span {@link ThreadLocal} is cleared.
  12. *
  13. Consecutive deactivate() calls for already deactivated spans will be ignored.
  14. *
- * - * @author Sjoerd Talsma */ final class ThreadLocalSpanManager extends ActiveSpanManager { @@ -27,29 +25,22 @@ final class ThreadLocalSpanManager extends ActiveSpanManager { ThreadLocalSpanManager() { } - public Span getActiveSpan() { + @Override + public Span activeSpan() { final ManagedSpan activeSpan = ACTIVE.get(); return activeSpan != null ? activeSpan.span : NoopSpan.INSTANCE; } - public SpanDeactivator setActiveSpan(Span span) { - final ManagedSpan managedSpan = new ManagedSpan(span); + @Override + public SpanDeactivator activate(Span span) { + ManagedSpan managedSpan = new ManagedSpan(span); ACTIVE.set(managedSpan); return managedSpan; } - public void deactivateSpan(SpanDeactivator deactivator) { - if (!(deactivator instanceof ManagedSpan)) { - throw new IllegalArgumentException( - "Cannot deactivate " + deactivator + ". It was not issued by this ActiveSpanManager!"); - } - ((ManagedSpan) deactivator).deactivate(); - } - - public boolean deactivateAllSpans() { - boolean deactivated = ACTIVE.get() != null; + @Override + public void clear() { ACTIVE.remove(); - return deactivated; } @Override @@ -66,7 +57,12 @@ private ManagedSpan(final Span span) { this.span = span; } - private void deactivate() { + @Override + public Span getSpan() { + return span; + } + + public void deactivate() { if (deactivated.compareAndSet(false, true)) { ManagedSpan current = ACTIVE.get(); if (this == current) { @@ -75,10 +71,10 @@ private void deactivate() { } if (current == null) ACTIVE.remove(); else ACTIVE.set(current); - LOGGER.log(Level.FINER, "Deactivated {0} and restored active span to {1}.", + LOGGER.log(Level.FINER, "Deactivated {0} and restored ACTIVE span to {1}.", new Object[]{this, current}); } else { - LOGGER.log(Level.FINE, "Deactivated {0} without affecting active span {1}.", + LOGGER.log(Level.FINE, "Deactivated {0} without affecting ACTIVE span {1}.", new Object[]{this, current}); } } else { @@ -86,6 +82,11 @@ private void deactivate() { } } + @Override + public void close() { + deactivate(); + } + @Override public String toString() { return "ManagedSpan{" + span + '}'; diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java index 91b255d..6b4c3a3 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java @@ -2,15 +2,13 @@ import io.opentracing.Span; import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; +import io.opentracing.contrib.activespan.SpanDeactivator; import java.util.concurrent.Callable; /** * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} * from the scheduling thread. - * - * @author Sjoerd Talsma */ public class CallableWithActiveSpan implements Callable { @@ -33,7 +31,7 @@ protected CallableWithActiveSpan(Callable delegate, Span activeSpanOfSchedule * @see ActiveSpanManager#activeSpan() */ public static CallableWithActiveSpan of(Callable delegate) { - return new CallableWithActiveSpan(delegate, ActiveSpanManager.activeSpan()); + return new CallableWithActiveSpan(delegate, ActiveSpanManager.get().activeSpan()); } /** @@ -57,11 +55,11 @@ public CallableWithActiveSpan withActiveSpan(Span activeSpan) { * @throws Exception if the original call threw an exception. */ public T call() throws Exception { - final SpanDeactivator parentContext = ActiveSpanManager.activate(activeSpanOfScheduler); + final SpanDeactivator deactivator = ActiveSpanManager.get().activate(activeSpanOfScheduler); try { return delegate.call(); - } finally { - ActiveSpanManager.deactivate(parentContext); + } finally { // TODO: error handling (preferably using Guava's Closer) + deactivator.deactivate(); } } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java index f1fec93..46ad23d 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java @@ -2,13 +2,11 @@ import io.opentracing.Span; import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; +import io.opentracing.contrib.activespan.SpanDeactivator; /** * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} * from the scheduling thread. - * - * @author Sjoerd Talsma */ public class RunnableWithActiveSpan implements Runnable { @@ -30,7 +28,7 @@ protected RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { * @see ActiveSpanManager#activeSpan() */ public static RunnableWithActiveSpan of(Runnable delegate) { - return new RunnableWithActiveSpan(delegate, ActiveSpanManager.activeSpan()); + return new RunnableWithActiveSpan(delegate, ActiveSpanManager.get().activeSpan()); } /** @@ -51,11 +49,11 @@ public RunnableWithActiveSpan withParent(Span activeSpan) { * Performs the runnable action with the specified parent span. */ public void run() { - SpanDeactivator deactivator = ActiveSpanManager.activate(parentSpan); + SpanDeactivator deactivator = ActiveSpanManager.get().activate(parentSpan); try { delegate.run(); - } finally { - ActiveSpanManager.deactivate(deactivator); + } finally { // TODO: error handling (preferably using Guava's Closer) + deactivator.deactivate(); } } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java index 3459db2..2de97a2 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java @@ -11,8 +11,6 @@ /** * {@link ExecutorService} wrapper that will propagate the {@link ActiveSpanManager#activeSpan() active span} * into the calls that are scheduled. - * - * @author Sjoerd Talsma */ public class SpanAwareExecutorService implements ExecutorService { protected final ExecutorService delegate; @@ -22,7 +20,7 @@ protected SpanAwareExecutorService(ExecutorService delegate) { this.delegate = delegate; } - public static ExecutorService wrap(final ExecutorService delegate) { + public static SpanAwareExecutorService wrap(final ExecutorService delegate) { return delegate instanceof SpanAwareExecutorService ? (SpanAwareExecutorService) delegate : new SpanAwareExecutorService(delegate); } @@ -70,8 +68,8 @@ public T invokeAny(Collection> tasks, long timeout, Ti */ protected Collection> spanAwareTasks(final Collection> tasks) { if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); - final Collection> result = new ArrayList>(tasks.size()); - final Span activeSpan = ActiveSpanManager.activeSpan(); + Collection> result = new ArrayList>(tasks.size()); + Span activeSpan = ActiveSpanManager.get().activeSpan(); for (Callable task : tasks) { result.add(new CallableWithActiveSpan(task, activeSpan)); } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java index b910c1e..e0828d6 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java +++ b/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java @@ -4,12 +4,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; -import static io.opentracing.contrib.activespan.concurrent.SpanAwareExecutorService.wrap; - -/** - * @author Sjoerd Talsma - * @navassoc - delegatesTo - Executors - */ public final class SpanAwareExecutors { /** @@ -27,8 +21,8 @@ private SpanAwareExecutors() { * @return the newly created thread pool * @see Executors#newFixedThreadPool(int) */ - public static ExecutorService newFixedThreadPool(int nThreads) { - return wrap(Executors.newFixedThreadPool(nThreads)); + public static SpanAwareExecutorService newFixedThreadPool(int nThreads) { + return SpanAwareExecutorService.wrap(Executors.newFixedThreadPool(nThreads)); } /** @@ -40,8 +34,8 @@ public static ExecutorService newFixedThreadPool(int nThreads) { * @return the newly created thread pool * @see Executors#newFixedThreadPool(int, ThreadFactory) */ - public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); + public static SpanAwareExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return SpanAwareExecutorService.wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); } /** @@ -51,8 +45,8 @@ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory thr * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor() */ - public static ExecutorService newSingleThreadExecutor() { - return wrap(Executors.newSingleThreadExecutor()); + public static SpanAwareExecutorService newSingleThreadExecutor() { + return SpanAwareExecutorService.wrap(Executors.newSingleThreadExecutor()); } /** @@ -63,8 +57,8 @@ public static ExecutorService newSingleThreadExecutor() { * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor(ThreadFactory) */ - public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return wrap(Executors.newSingleThreadExecutor(threadFactory)); + public static SpanAwareExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return SpanAwareExecutorService.wrap(Executors.newSingleThreadExecutor(threadFactory)); } /** @@ -74,8 +68,8 @@ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactor * @return the newly created thread pool * @see Executors#newCachedThreadPool() */ - public static ExecutorService newCachedThreadPool() { - return wrap(Executors.newCachedThreadPool()); + public static SpanAwareExecutorService newCachedThreadPool() { + return SpanAwareExecutorService.wrap(Executors.newCachedThreadPool()); } /** @@ -86,8 +80,8 @@ public static ExecutorService newCachedThreadPool() { * @return the newly created thread pool * @see Executors#newCachedThreadPool(ThreadFactory) */ - public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { - return wrap(Executors.newCachedThreadPool(threadFactory)); + public static SpanAwareExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return SpanAwareExecutorService.wrap(Executors.newCachedThreadPool(threadFactory)); } } diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java index fe39ee8..c13860a 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java @@ -4,7 +4,7 @@ import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.ActiveSpanManager.SpanDeactivator; +import io.opentracing.contrib.activespan.SpanDeactivator; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -13,8 +13,6 @@ * Implementation of an 'active span'.
* This active span will deactivate itself when it is {@link #finish() finished} or {@link #close() closed}.
* All other span functionality is forwareded to the delegate Span. - * - * @author Sjoerd Talsma */ final class ActiveSpan implements Span { @@ -47,7 +45,7 @@ protected Span rewrap(Span span) { * Deactivates this active span (only once). */ private void deactivate() { - if (deactivated.compareAndSet(false, true)) ActiveSpanManager.deactivate(deactivator); + if (deactivated.compareAndSet(false, true)) deactivator.deactivate(); } /** diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java index 3948c96..42dfa11 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java @@ -5,6 +5,7 @@ import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.activespan.SpanDeactivator; import java.util.Map; @@ -12,10 +13,9 @@ * {@link SpanBuilder} that forwards all methods to a delegate.
* Only the {@link #start()} method is overridden, {@link ActiveSpanManager#activate(Span) activating} * the started {@link Span} and wrapping it in an {@link ActiveSpan} object.
- * The {@link ActiveSpan} object {@link ActiveSpanManager.SpanDeactivator deactivates} the span automatically + * The {@link ActiveSpan} object {@link SpanDeactivator deactivates} the span automatically * when it is {@link ActiveSpan#finish() finished} or {@link ActiveSpan#close() closed}. * - * @author Sjoerd Talsma * @see ActiveSpanManager#activate(Span) * @see ActiveSpan#finish() */ @@ -56,17 +56,14 @@ SpanBuilder rewrap(SpanBuilder spanBuilder) { */ @Override public Span start() { - // Return a new 'active' span that deactivates itself again when finished. - final Span newSpan = delegate.start(); - return new ActiveSpan(newSpan, ActiveSpanManager.activate(newSpan)); + Span newSpan = delegate.start(); + return new ActiveSpan(newSpan, ActiveSpanManager.get().activate(newSpan)); } // All other methods are forwarded to the delegate SpanBuilder. public SpanBuilder asChildOf(SpanContext parent) { - if (parent instanceof ActiveSpanBuilder) { - parent = ((ActiveSpanBuilder) parent).delegate; - } + if (parent instanceof ActiveSpanBuilder) parent = ((ActiveSpanBuilder) parent).delegate; return rewrap(delegate.asChildOf(parent)); } @@ -75,11 +72,9 @@ public SpanBuilder asChildOf(Span parent) { return rewrap(delegate.asChildOf(parent)); } - public SpanBuilder addReference(String referenceType, SpanContext referencedContext) { - if (referencedContext instanceof ActiveSpanBuilder) { - referencedContext = ((ActiveSpanBuilder) referencedContext).delegate; - } - return rewrap(delegate.addReference(referenceType, referencedContext)); + public SpanBuilder addReference(String referenceType, SpanContext context) { + if (context instanceof ActiveSpanBuilder) context = ((ActiveSpanBuilder) context).delegate; + return rewrap(delegate.addReference(referenceType, context)); } public SpanBuilder withTag(String key, String value) { diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java index 73907b0..14fba2b 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java @@ -4,20 +4,19 @@ import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.activespan.SpanDeactivator; import io.opentracing.propagation.Format; /** * Wrapper that forwards all calls to another {@link Tracer} implementation.
* {@link io.opentracing.Span Spans} created with this tracer are * {@link ActiveSpanManager#activate(Span) activated} when started and - * {@link ActiveSpanManager#deactivate(ActiveSpanManager.SpanDeactivator) deactivated} when finished. + * {@link SpanDeactivator#deactivate() deactivated} when finished. *

* The {@link SpanBuilder} of this Tracer will short-circuit to the * {@link io.opentracing.NoopSpanBuilder NoopSpanBuilder}. * This means {@link io.opentracing.NoopSpan NoopSpan} * instances will not be activated or deactivated through this tracer. - * - * @author Sjoerd Talsma */ public class ActiveSpanTracer implements Tracer { @@ -29,9 +28,8 @@ public ActiveSpanTracer(Tracer delegate) { } public void inject(SpanContext spanContext, Format format, C carrier) { - if (spanContext instanceof ActiveSpanBuilder) { // Weird that Builder extends Context! - spanContext = ((ActiveSpanBuilder) spanContext).delegate; - } + // Weird that Builder extends Context! + if (spanContext instanceof ActiveSpanBuilder) spanContext = ((ActiveSpanBuilder) spanContext).delegate; delegate.inject(spanContext, format, carrier); } diff --git a/src/main/resources/META-INF/opentracing-activespan.properties b/src/main/resources/META-INF/opentracing-activespan.properties deleted file mode 100644 index 29e7dce..0000000 --- a/src/main/resources/META-INF/opentracing-activespan.properties +++ /dev/null @@ -1,3 +0,0 @@ -version=${project.version} -build.timestamp=${build.timestamp} -build.revision=${git.revision} From c31ddaa61fea8ceb5f818816b680fd3bd76d1cc4 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Tue, 3 Jan 2017 17:44:10 +0100 Subject: [PATCH 08/51] Minor JavaDoc improvement. --- .../io/opentracing/contrib/activespan/SpanDeactivator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java b/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java index 543940e..212b14a 100644 --- a/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java +++ b/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java @@ -10,7 +10,7 @@ public interface SpanDeactivator extends Closeable { /** - * Either the active span or one that was the active span at some point. + * The span that is the active span or has been at some point. * * @return The contained span to be deactivated. */ @@ -23,7 +23,7 @@ public interface SpanDeactivator extends Closeable { *

    *
  1. It is encouraged to restore the active span as it was before the contained span was activated * (providing stack-like behaviour).
  2. - *
  3. It must be possible to repeatedly call deactivate without any side effects.
  4. + *
  5. It must be possible to repeatedly call deactivate without side effects.
  6. *
*/ void deactivate(); From 4d8b39fd8cbcc0b626f43a832f4b24cbfb120222 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 4 Jan 2017 13:44:46 +0100 Subject: [PATCH 09/51] Introduced SpanManager and ManagedSpan concepts for clarity. Maybe we should rename the git repository to java-spanmanager ? --- README.md | 84 +++++----- pom.xml | 10 +- .../contrib/activespan/ActiveSpanManager.java | 124 -------------- .../contrib/activespan/SpanDeactivator.java | 36 ---- .../activespan/ThreadLocalSpanManager.java | 95 ----------- .../contrib/activespan/tracer/ActiveSpan.java | 145 ---------------- .../activespan/tracer/ActiveSpanTracer.java | 49 ------ .../spanmanager/ActiveSpanManager.java | 150 +++++++++++++++++ .../contrib/spanmanager/ManagedSpan.java | 40 +++++ .../contrib/spanmanager/SpanManager.java | 39 +++++ .../spanmanager/ThreadLocalSpanManager.java | 97 +++++++++++ .../concurrent/CallableWithActiveSpan.java | 31 ++-- .../concurrent/RunnableWithActiveSpan.java | 33 ++-- .../concurrent/SpanAwareExecutorService.java | 8 +- .../concurrent/SpanAwareExecutors.java | 16 +- .../tracer/AutoReleasingManagedSpan.java | 158 ++++++++++++++++++ .../tracer/ManagedSpanBuilder.java} | 46 +++-- .../spanmanager/tracer/ManagedSpanTracer.java | 48 ++++++ 18 files changed, 654 insertions(+), 555 deletions(-) delete mode 100644 src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java delete mode 100644 src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java delete mode 100644 src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java delete mode 100644 src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java delete mode 100644 src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java rename src/main/java/io/opentracing/contrib/{activespan => spanmanager}/concurrent/CallableWithActiveSpan.java (63%) rename src/main/java/io/opentracing/contrib/{activespan => spanmanager}/concurrent/RunnableWithActiveSpan.java (57%) rename src/main/java/io/opentracing/contrib/{activespan => spanmanager}/concurrent/SpanAwareExecutorService.java (91%) rename src/main/java/io/opentracing/contrib/{activespan => spanmanager}/concurrent/SpanAwareExecutors.java (87%) create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java rename src/main/java/io/opentracing/contrib/{activespan/tracer/ActiveSpanBuilder.java => spanmanager/tracer/ManagedSpanBuilder.java} (56%) create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java diff --git a/README.md b/README.md index 2a84678..e80c70e 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,66 @@ # java-activespan -This library provides a way to manage an _active span_ within the current process -and propagate it to other threads. +This library provides a way to manage a _current span_ within and propagate it to other threads. -## ActiveSpanManager +## SpanManager + +The core of this library, this interface defines + 1. The `currentSpan()` method to return the _currently managed span_. + 2. The `manage(Span)` method to make the specified `Span` the currently managed span. + A `ManagedSpan` instance is returned to release this _currently managed span_. + 3. The `clear()` method to releases all managed spans including any parents. -The core of this library, this class provides - 1. The `activeSpan()` method to return the _active span_ in the current process. - If there is no active span, a `NoopSpan` is returned instead. - 2. The _active span_ can be set through the `activate()` method, - returning a `SpanDeactivator`. - 3. Method `clear()` to deactivate _all active spans_ in a process, including any active parents. - Not intended for 'regular' use, this method is available for boundary filters, - making sure all spans are cleared before the current process is repurposed by a threadpool. +## ManagedSpan -## SpanDeactivator +Obtained from the `SpanManager`, this object can be used to +release the _currently managed span_ again. + 1. The `getSpan()` method returns the managed `Span` object. + 2. The `release()` method 'unmanages' the span. + Implementors are encouraged to restore the managed span as it was before, + providing stack-like behaviour. + It must be possible to repeatedly call `release()` without side effects. + 3. `ManagedSpan` extends `Closeable` where `close()` is an alias for `release()` + so it can reliably be used in `try-with-resources` blocks. + **Please note:** _Closing a ManagedSpan does **not** automatically finish the actual span._ + (with the `ManagedSpanTracer` being an exception to this rule) -Returned by `ActiveSpanManager.activate()` this object can `deactivate()` a -previously activated `Span` again. -It implements `Closeable` for easy use in try-with-resources blocks. +## ActiveSpanManager + +Provides the `ActiveSpanManager.get()` method that returns the singleton _active span manager_. +Upon first use of this manager, it lazily determines which `SpanManager` implementation to use: + 1. If an explicitly configured `SpanManager` was provided via the `ActiveSpanManager.set()` method, + that will always take precedence over automatically resolved manager instances. + 2. A `SpanManager` implementation can be automatically provided using the java `ServiceLoader` through the + `META-INF/services/io.opentracing.contrib.spanmanager.SpanManager` service definition file. + The ActiveSpanManager class will not attempt to choose between implementations; + if more than one is found, a warning is logged and the default implementation is used: + 3. If no `SpanManager` is found, a default implementation is used, + providing `ThreadLocal` storage to manage the active span. ## Concurrency ### SpanAwareExecutorService -This `ExecutorService` wraps an existing threadpool and propagates the _active span_ +This `ExecutorService` wraps an existing threadpool and propagates the currently active span of the caller into the tasks it executes. -No `Spans` are started or finished automatically by this executor. -Convenience variants of the java `Executors` factory methods are provided by the `SpanAwareExecutors` class: +Please note: This ExecutorService does not automatically start or finish spans, +but merely propagates the _currently active_ span into the jobs being executed. + +### SpanAwareExecutors + +Factory-methods similar to standard java `Executors`: - `SpanAwareExecutors.newFixedThreadPool(int)` - `SpanAwareExecutors.newSingleThreadExecutor()` - `SpanAwareExecutors.newCachedThreadPool()` - Variants of the above with additional `ThreadFactory` argument. -### ActiveSpanTracer - -This `Tracer` makes managing the _active span_ easier. - 1. It is a _wrapper_ that forwards all calls to another `Tracer` implementation. - 2. `Span` instances created with this tracer are automatically - _activated_ when started and _deactivated_ when finished. - 3. The `SpanBuilder` of this tracer will short-circuit to the `NoopSpanBuilder`. - This means any `NoopSpan` instances created do not automatically become the active span. - -## Custom span managers - -It is possible to provide a custom implementation of `ActiveSpanManager`. -This may be useful if you already have a method in place to propagate contextual information -from one thread to another. Creating a custom manager is one way to piggyback the _active span_ on -your existing propagation mechanism. Another way would be to call _activate_ and _deactivate_ -at the appropriate moments in your propagation mechanism. +### ManagedSpanTracer -To enable a custom manager: - 1. Programmatically, call the `ActiveSpanManager.set()` with the new implementation. - 2. Using the standard Java `ServiceLoader`, bundle a - `META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager` service file - containing the classname of the implementation. +This convenience `Tracer` automates managing the _active span_: + 1. It is a wrapper that forwards all calls to another `Tracer` implementation. + 2. `Span` instances created with this tracer are managed when started + (automatically becoming the currently active span) + and released when finished. ## Examples diff --git a/pom.xml b/pom.xml index cf66644..55c1e0e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,14 +6,14 @@ 4.0.0 - io.opentracing.contrib.activespan - opentracing-activespan + io.opentracing.contrib.spanmanager + opentracing-spanmanager 0.1.0-SNAPSHOT jar - In-process active span propagation. - Library for propagating the active span accross threads. + SpanManager library. + Library for managing the currently active span and propagating it accross threads. https://github.com/opentracing-contrib/java-activespan 2016 @@ -44,7 +44,7 @@ UTF-8 UTF-8 1.6 - 0.20.2 + 0.20.7 diff --git a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java deleted file mode 100644 index d3d2698..0000000 --- a/src/main/java/io/opentracing/contrib/activespan/ActiveSpanManager.java +++ /dev/null @@ -1,124 +0,0 @@ -package io.opentracing.contrib.activespan; - -import io.opentracing.Span; - -import java.util.Iterator; -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Manages the active {@link Span}.
- * A {@link Span} becomes active in the current process after a call to {@link #activate(Span)} - * and can be deactivated again by calling {@link SpanDeactivator#deactivate()}. - */ -public abstract class ActiveSpanManager { - private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); - private static final AtomicReference INSTANCE = new AtomicReference(); - - /** - * Returns the {@linkplain ActiveSpanManager} implementation. - *

- * The default implementation will use a {@link ThreadLocal ThreadLocal storage} to maintain the active {@link Span} - * and its parents. - *

- * Custom implementations can be provided by: - *

    - *
  1. calling {@link #set(ActiveSpanManager)} programmatically, or
  2. - *
  3. defining a META-INF/services/io.opentracing.contrib.activespan.ActiveSpanManager service file - * containing the classname of the implementation
  4. - *
- * - * @return The ActiveSpanManager implementation. - */ - public static ActiveSpanManager get() { - ActiveSpanManager activeSpanManager = INSTANCE.get(); - if (activeSpanManager == null) { - ActiveSpanManager implementation = loadSingleSpiImplementation(); - while (activeSpanManager == null && implementation != null) { // handle rare race condition - INSTANCE.compareAndSet(null, implementation); - activeSpanManager = INSTANCE.get(); - } - LOGGER.log(Level.FINE, "ActiveSpanManager implementation: {0}.", activeSpanManager); - } - return activeSpanManager; - } - - /** - * Programmatically sets an ActiveSpanManager implementation as the singleton instance. - *

- * Any previous manager is returned so it can be restored if necessary, for example in a testing situation. - * - * @param instance The overridden implementation to use for in-process span management. - * @return Any previous ActiveSpanManager that was initialized before, or null. - */ - public static ActiveSpanManager set(ActiveSpanManager instance) { - return INSTANCE.getAndSet(instance); - } - - /** - * Return the active {@link Span}. - * - * @return The active Span, or the NoopSpan if there is no active span. - * @see #activeSpan() - */ - public abstract Span activeSpan(); - - /** - * Makes span the active span within the running process. - * - * @param span The span to become the active span. - * @return The deactivator to undo the activation. - * @see #activeSpan() - */ - public abstract SpanDeactivator activate(Span span); - - /** - * Deactivates all active spans in the current process which includes any active parents. - *

- * This method allows boundary filters to deactivate all active spans - * before returning control over their Thread, which may end up back in some threadpool.
- * - * @see SpanDeactivator#deactivate() - */ - public abstract void clear(); - -// /** -// * Wraps the {@link Callable} to execute with the {@link ActiveSpanManager#activeSpan() active span} -// * from the scheduling thread. -// * -// * @param callable The callable to wrap. -// * @param The return type of the wrapped call. -// * @return The wrapped call executing with the active span of the scheduling process. -// */ -// public static CallableWithActiveSpan withActiveSpan(Callable callable) { -// return CallableWithActiveSpan.of(callable); -// } -// -// /** -// * Wraps the {@link Runnable} to execute with the {@link ActiveSpanManager#activeSpan() active span} -// * from the scheduling thread. -// * -// * @param runnable The runnable to wrap. -// * @return The wrapped runnable executing with the active span of the scheduling process. -// */ -// public static RunnableWithActiveSpan withActiveSpan(Runnable runnable) { -// return RunnableWithActiveSpan.of(runnable); -// } - - private static ActiveSpanManager loadSingleSpiImplementation() { - // Use the ServiceLoader to find the declared ActiveSpanManager implementation. - Iterator spiImplementations = - ServiceLoader.load(ActiveSpanManager.class, ActiveSpanManager.class.getClassLoader()).iterator(); - if (spiImplementations.hasNext()) { - ActiveSpanManager foundImplementation = spiImplementations.next(); - if (!spiImplementations.hasNext()) { - return foundImplementation; - } - LOGGER.log(Level.WARNING, "More than one ActiveSpanManager service implementation found. " + - "Falling back to default ThreadLocal implementation."); - } - return new ThreadLocalSpanManager(); - } -} diff --git a/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java b/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java deleted file mode 100644 index 212b14a..0000000 --- a/src/main/java/io/opentracing/contrib/activespan/SpanDeactivator.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.opentracing.contrib.activespan; - -import io.opentracing.Span; - -import java.io.Closeable; - -/** - * To {@linkplain #deactivate() deactivate} a previously-activated {@link #getSpan() span} with. - */ -public interface SpanDeactivator extends Closeable { - - /** - * The span that is the active span or has been at some point. - * - * @return The contained span to be deactivated. - */ - Span getSpan(); - - /** - * Makes the {@link #getSpan() contained span} no longer the active span. - *

- * Implementation notes: - *

    - *
  1. It is encouraged to restore the active span as it was before the contained span was activated - * (providing stack-like behaviour).
  2. - *
  3. It must be possible to repeatedly call deactivate without side effects.
  4. - *
- */ - void deactivate(); - - /** - * Alias for {@link #deactivate()} to allow easy deactivation from try-with-resources. - */ - void close(); - -} diff --git a/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java deleted file mode 100644 index b0c7830..0000000 --- a/src/main/java/io/opentracing/contrib/activespan/ThreadLocalSpanManager.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.opentracing.contrib.activespan; - -import io.opentracing.NoopSpan; -import io.opentracing.Span; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Default ThreadLocal-based implementation of the {@link ActiveSpanManager} class that implements the following - * stack unwinding algorithm upon deactivation: - *
    - *
  1. If the deactivated managed span is not the ACTIVE span, the ACTIVE span is left alone.
  2. - *
  3. Otherwise, the first parent that is not yet deactivated is set as the new ACTIVE span.
  4. - *
  5. If no ACTIVE parents remain, the ACTIVE span {@link ThreadLocal} is cleared.
  6. - *
  7. Consecutive deactivate() calls for already deactivated spans will be ignored.
  8. - *
- */ -final class ThreadLocalSpanManager extends ActiveSpanManager { - - private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); - private static final ThreadLocal ACTIVE = new ThreadLocal(); - - ThreadLocalSpanManager() { - } - - @Override - public Span activeSpan() { - final ManagedSpan activeSpan = ACTIVE.get(); - return activeSpan != null ? activeSpan.span : NoopSpan.INSTANCE; - } - - @Override - public SpanDeactivator activate(Span span) { - ManagedSpan managedSpan = new ManagedSpan(span); - ACTIVE.set(managedSpan); - return managedSpan; - } - - @Override - public void clear() { - ACTIVE.remove(); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - - private static final class ManagedSpan implements SpanDeactivator { - private final ManagedSpan parent = ACTIVE.get(); - private final Span span; - private final AtomicBoolean deactivated = new AtomicBoolean(false); - - private ManagedSpan(final Span span) { - this.span = span; - } - - @Override - public Span getSpan() { - return span; - } - - public void deactivate() { - if (deactivated.compareAndSet(false, true)) { - ManagedSpan current = ACTIVE.get(); - if (this == current) { - while (current != null && current.deactivated.get()) { - current = current.parent; - } - if (current == null) ACTIVE.remove(); - else ACTIVE.set(current); - LOGGER.log(Level.FINER, "Deactivated {0} and restored ACTIVE span to {1}.", - new Object[]{this, current}); - } else { - LOGGER.log(Level.FINE, "Deactivated {0} without affecting ACTIVE span {1}.", - new Object[]{this, current}); - } - } else { - LOGGER.log(Level.FINEST, "No action needed, {0} was already deactivated.", this); - } - } - - @Override - public void close() { - deactivate(); - } - - @Override - public String toString() { - return "ManagedSpan{" + span + '}'; - } - } -} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java deleted file mode 100644 index c13860a..0000000 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpan.java +++ /dev/null @@ -1,145 +0,0 @@ -package io.opentracing.contrib.activespan.tracer; - -import io.opentracing.NoopSpan; -import io.opentracing.Span; -import io.opentracing.SpanContext; -import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.SpanDeactivator; - -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Implementation of an 'active span'.
- * This active span will deactivate itself when it is {@link #finish() finished} or {@link #close() closed}.
- * All other span functionality is forwareded to the delegate Span. - */ -final class ActiveSpan implements Span { - - protected Span delegate; - private final SpanDeactivator deactivator; - private final AtomicBoolean deactivated = new AtomicBoolean(false); - - ActiveSpan(Span delegate, SpanDeactivator deactivator) { - if (delegate == null) throw new NullPointerException("Delegate span was ."); - this.delegate = delegate; - this.deactivator = deactivator; - } - - /** - * Replaces the {@link #delegate} Span by a delegated-method result. - *

- * For null or {@link NoopSpan} the active span builder short-circuits to the noop Span, - * effectively disabling further tracing. - * - * @param span The span returned from the delegate (normally '== delegate'). - * @return Either this re-wrapped ActiveSpan or the NoopSpan. - */ - protected Span rewrap(Span span) { - if (span == null || span instanceof NoopSpan) return NoopSpan.INSTANCE; - this.delegate = span; - return this; - } - - /** - * Deactivates this active span (only once). - */ - private void deactivate() { - if (deactivated.compareAndSet(false, true)) deactivator.deactivate(); - } - - /** - * Finishes the delegate and deactivates this active span. - */ - public void finish() { - try { - delegate.finish(); - } finally { - this.deactivate(); - } - } - - /** - * Finishes the delegate and deactivates this active span. - */ - public void finish(long finishMicros) { - try { - delegate.finish(finishMicros); - } finally { - this.deactivate(); - } - } - - /** - * Finishes the delegate and deactivates this active span. - */ - public void close() { - try { - delegate.close(); - } finally { - this.deactivate(); - } - } - - // Default behaviour is forwarded to the delegate Span: - - public SpanContext context() { - return delegate.context(); - } - - public Span setTag(String key, String value) { - return rewrap(delegate.setTag(key, value)); - } - - public Span setTag(String key, boolean value) { - return rewrap(delegate.setTag(key, value)); - } - - public Span setTag(String key, Number value) { - return rewrap(delegate.setTag(key, value)); - } - - public Span log(Map fields) { - return rewrap(delegate.log(fields)); - } - - public Span log(long timestampMicroseconds, Map fields) { - return rewrap(delegate.log(timestampMicroseconds, fields)); - } - - public Span log(String event) { - return rewrap(delegate.log(event)); - } - - public Span log(long timestampMicroseconds, String event) { - return rewrap(delegate.log(timestampMicroseconds, event)); - } - - public Span setBaggageItem(String key, String value) { - return rewrap(delegate.setBaggageItem(key, value)); - } - - public String getBaggageItem(String key) { - return delegate.getBaggageItem(key); - } - - public Span setOperationName(String operationName) { - return rewrap(delegate.setOperationName(operationName)); - } - - @SuppressWarnings("deprecation") // We simply delegate this method as we're told. - public Span log(String eventName, Object payload) { - return rewrap(delegate.log(eventName, payload)); - } - - @SuppressWarnings("deprecation") // We simply delegate this method as we're told. - public Span log(long timestampMicroseconds, String eventName, Object payload) { - return rewrap(delegate.log(timestampMicroseconds, eventName, payload)); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{delegate=" + delegate + '}'; - } - -} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java b/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java deleted file mode 100644 index 14fba2b..0000000 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanTracer.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.opentracing.contrib.activespan.tracer; - -import io.opentracing.Span; -import io.opentracing.SpanContext; -import io.opentracing.Tracer; -import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.SpanDeactivator; -import io.opentracing.propagation.Format; - -/** - * Wrapper that forwards all calls to another {@link Tracer} implementation.
- * {@link io.opentracing.Span Spans} created with this tracer are - * {@link ActiveSpanManager#activate(Span) activated} when started and - * {@link SpanDeactivator#deactivate() deactivated} when finished. - *

- * The {@link SpanBuilder} of this Tracer will short-circuit to the - * {@link io.opentracing.NoopSpanBuilder NoopSpanBuilder}. - * This means {@link io.opentracing.NoopSpan NoopSpan} - * instances will not be activated or deactivated through this tracer. - */ -public class ActiveSpanTracer implements Tracer { - - protected final Tracer delegate; - - public ActiveSpanTracer(Tracer delegate) { - if (delegate == null) throw new NullPointerException("The delegate Tracer implementation is ."); - this.delegate = delegate; - } - - public void inject(SpanContext spanContext, Format format, C carrier) { - // Weird that Builder extends Context! - if (spanContext instanceof ActiveSpanBuilder) spanContext = ((ActiveSpanBuilder) spanContext).delegate; - delegate.inject(spanContext, format, carrier); - } - - public SpanContext extract(Format format, C carrier) { - return delegate.extract(format, carrier); - } - - public SpanBuilder buildSpan(String operationName) { - return ActiveSpanBuilder.of(delegate.buildSpan(operationName)); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{delegate=" + delegate + '}'; - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java new file mode 100644 index 0000000..509bc3a --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java @@ -0,0 +1,150 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.Span; + +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Manages the currently active {@link Span}.
+ * A {@link Span} becomes active in the current process after a call to {@link #manage(Span)} + * and can be deactivated again by calling {@link ManagedSpan#release()}. + */ +public final class ActiveSpanManager implements SpanManager { + private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); + + /** + * Singleton instance. + *

+ * Since we cannot prevent people using {@linkplain #get() ActiveSpanManager.get()} as a constant, + * this guarantees that references obtained before, during or after initialization + * all behave as if obtained after initialization once properly initialized.
+ * As a minor additional benefit it makes it harder to circumvent the {@link SpanManager} API. + */ + private static final ActiveSpanManager INSTANCE = new ActiveSpanManager(); + + /** + * The resolved {@link SpanManager} to delegate the implementation to.
+ * This can be either an {@link #set(SpanManager) explicitly set delegate} + * or the automatically resolved service implementation. + */ + private final AtomicReference activeSpanManager = new AtomicReference(); + + private ActiveSpanManager() { + } + + private SpanManager lazySpanManager() { + SpanManager instance = this.activeSpanManager.get(); + if (instance == null) { + final SpanManager singleton = loadSingleSpiImplementation(); + while (instance == null && singleton != null) { // handle rare race condition + this.activeSpanManager.compareAndSet(null, singleton); + instance = this.activeSpanManager.get(); + } + LOGGER.log(Level.INFO, "Using ActiveSpanManager implementation: {0}.", instance); + } + return instance; + } + + /** + * Returns the {@linkplain SpanManager} instance. + * Upon first use of any method, this manager lazily determines which actual {@link SpanManager} implementation + * to use: + *

    + *
  1. If an explicitly configured manager was provided via the {@link #set(SpanManager)} method, + * that will always take precedence over automatically resolved instances.
  2. + *
  3. An implementation can automatically be provided using the Java {@link ServiceLoader} through the + * META-INF/services/io.opentracing.contrib.spanmanager.SpanManager service definition file.
    + * The {@link ActiveSpanManager} will not attempt to choose between implementations; + * if more than one is found by the {@linkplain ServiceLoader service loader}, + * a warning is logged and tracing is disabled by falling back to the default implementation:
  4. + *
  5. If no tracer implementation is available, a default {@link ThreadLocal} based implementation is used.
  6. + *
+ * + * @return The active SpanManager. + */ + public static SpanManager get() { + return INSTANCE; + } + + /** + * Programmatically sets a SpanManager implementation as the singleton instance. + *

+ * The previous manager is returned so it can be: + *

    + *
  • {@link #clear() cleared} as it no longer is the active manager, or
  • + *
  • restored later, for example in a testing situation
  • + *
+ * + * @param spanManager The overridden implementation to use for in-process span management. + * @return The previous SpanManager, or null if there was none. + */ + public static SpanManager set(SpanManager spanManager) { + if (spanManager instanceof ActiveSpanManager) { + LOGGER.log(Level.FINE, "Attempted to set the ActiveSpanManager as delegate of itself."); + return INSTANCE.activeSpanManager.get(); // no-op, return 'previous' manager. + } + return INSTANCE.activeSpanManager.getAndSet(spanManager); + } + + /** + * Return the currently active {@link Span}. + * + * @return The currently active Span, or the NoopSpan if there is no active span. + * @see SpanManager#manage(Span) + */ + @Override + public Span currentSpan() { + return lazySpanManager().currentSpan(); + } + + /** + * Makes span the currently active span within the running process. + * + * @param span The span to become the active span. + * @return The managed object to release the currently active span with. + * @see SpanManager#currentSpan() + * @see ManagedSpan#release() + */ + @Override + public ManagedSpan manage(Span span) { + return lazySpanManager().manage(span); + } + + /** + * Releases all managed spans including any parents. + *

+ * This allows boundary filters to release all spans before relinquishing control over their Thread, + * which may end up repurposed by a threadpool. + * + * @see ManagedSpan#release() + */ + @Override + public void clear() { + lazySpanManager().clear(); + } + + /** + * Loads a single service implementation from {@link ServiceLoader}. + * + * @return The single service or a new ThreadLocalSpanManager. + */ + private static SpanManager loadSingleSpiImplementation() { + // Use the ServiceLoader to find the declared ActiveSpanManager implementation. + Iterator spiImplementations = + ServiceLoader.load(SpanManager.class, SpanManager.class.getClassLoader()).iterator(); + if (spiImplementations.hasNext()) { + SpanManager foundImplementation = spiImplementations.next(); + if (!spiImplementations.hasNext()) { + return foundImplementation; + } + LOGGER.log(Level.WARNING, "More than one SpanManager service implementation found. " + + "Falling back to default ThreadLocal implementation."); + } + return new ThreadLocalSpanManager(); + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java new file mode 100644 index 0000000..4b170b3 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java @@ -0,0 +1,40 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.Span; + +import java.io.Closeable; + +/** + * To {@linkplain #release() release} a {@link SpanManager#manage(Span) managed span} with. + *

+ * It must be possible to repeatedly call {@linkplain #release()} without side effects. + * + * @see SpanManager + */ +public interface ManagedSpan extends Closeable { + + /** + * The span that became the managed span at some point. + * + * @return The contained span to be released. + */ + Span getSpan(); + + /** + * Makes the {@link #getSpan() contained span} no longer the managed span. + *

+ * Implementation notes: + *

    + *
  1. It is encouraged to restore the managed span as it was before this span became managed + * (providing stack-like behaviour).
  2. + *
  3. It must be possible to repeatedly call release without side effects.
  4. + *
+ */ + void release(); + + /** + * Alias for {@link #release()} to allow easy use from try-with-resources. + */ + void close(); + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java new file mode 100644 index 0000000..c2b3384 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -0,0 +1,39 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.Span; + +/** + * Manager to {@link #manage(Span) manage} and {@link ManagedSpan#release() release} + * {@linkplain Span spans} and accessing the {@link #currentSpan() currently-managed span}. + */ +public interface SpanManager { + + /** + * Return the currently-managed {@link Span}. + * + * @return The current Span, or the NoopSpan if there is no managed span. + * @see SpanManager#manage(Span) + */ + Span currentSpan(); + + /** + * Makes span the current span within the running process. + * + * @param span The span to become the current span. + * @return A managed object to release the current span with. + * @see SpanManager#currentSpan() + * @see ManagedSpan#release() + */ + ManagedSpan manage(Span span); + + /** + * Releases all managed spans including any parents. + *

+ * This allows boundary filters to release all spans before relinquishing control over their Thread, + * which may end up repurposed by a threadpool. + * + * @see ManagedSpan#release() + */ + void clear(); + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java new file mode 100644 index 0000000..f721c4a --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java @@ -0,0 +1,97 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.NoopSpan; +import io.opentracing.Span; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default ThreadLocal-based implementation of {@link SpanManager} that implements + * the following stack unwinding algorithm upon deactivation: + *

    + *
  1. If the deactivated span is not the managed span, the managed span is left alone.
  2. + *
  3. Otherwise, the first parent that is not yet deactivated is set as the new managed span.
  4. + *
  5. If no managed parents remain, the managed span {@link ThreadLocal} is cleared.
  6. + *
  7. Consecutive release() calls for already-deactivated spans will be ignored.
  8. + *
+ */ +final class ThreadLocalSpanManager implements SpanManager { + + private static final Logger LOGGER = Logger.getLogger(ThreadLocalSpanManager.class.getName()); + private static final ThreadLocal MANAGED = new ThreadLocal(); + + ThreadLocalSpanManager() { + } + + @Override + public Span currentSpan() { + final ManagedSpan managedSpan = MANAGED.get(); + return managedSpan != null ? managedSpan.span : NoopSpan.INSTANCE; + } + + @Override + public io.opentracing.contrib.spanmanager.ManagedSpan manage(Span span) { + ManagedSpan managedSpan = new ManagedSpan(span); + MANAGED.set(managedSpan); + return managedSpan; + } + + @Override + public void clear() { + MANAGED.remove(); + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } + + private static final class ManagedSpan implements io.opentracing.contrib.spanmanager.ManagedSpan { + private final ManagedSpan parent = MANAGED.get(); + private final Span span; + private final AtomicBoolean deactivated = new AtomicBoolean(false); + + private ManagedSpan(final Span span) { + this.span = span; + } + + @Override + public Span getSpan() { + return span; + } + + /** + * Please see {@link ThreadLocalSpanManager outer class description} for the stack-unwinding documentation. + */ + public void release() { + if (deactivated.compareAndSet(false, true)) { + ManagedSpan current = MANAGED.get(); + if (this == current) { + while (current != null && current.deactivated.get()) { + current = current.parent; + } + if (current == null) MANAGED.remove(); + else MANAGED.set(current); + LOGGER.log(Level.FINER, "Deactivated {0} and restored managed span to {1}.", new Object[]{this, current}); + } else { + LOGGER.log(Level.FINE, "Deactivated {0} without affecting managed span {1}.", new Object[]{this, current}); + } + } else { + LOGGER.log(Level.FINEST, "No action needed, {0} was already deactivated.", this); + } + } + + @Override + public void close() { + release(); + } + + @Override + public String toString() { + return "ManagedSpan{" + span + '}'; + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java similarity index 63% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java index 6b4c3a3..a599e71 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/CallableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java @@ -1,13 +1,13 @@ -package io.opentracing.contrib.activespan.concurrent; +package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.SpanDeactivator; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.ManagedSpan; import java.util.concurrent.Callable; /** - * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. */ public class CallableWithActiveSpan implements Callable { @@ -22,26 +22,27 @@ protected CallableWithActiveSpan(Callable delegate, Span activeSpanOfSchedule } /** - * Creates a new callable that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * Creates a new callable that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. * * @param delegate The delegate callable to execute (required, non-null). * @param The result type of the call. * @return The 'span aware' callable that will propagate the currently active span to the new thread. - * @see ActiveSpanManager#activeSpan() + * @see ActiveSpanManager#currentSpan() */ public static CallableWithActiveSpan of(Callable delegate) { - return new CallableWithActiveSpan(delegate, ActiveSpanManager.get().activeSpan()); + return new CallableWithActiveSpan(delegate, ActiveSpanManager.get().currentSpan()); } /** - * This method allows the caller to override the active span in the new thread. + * This method allows the caller to override the active span for the new thread. *

- * Please note: it is not necessary to call this method with the - * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Callable) by default}. + * Please note: it is not necessary to call this method with the + * {@link ActiveSpanManager#currentSpan() currently active span} since + * that will be used {@link #of(Callable) by default}. * * @param activeSpan The span to become the active span when calling the delegate. - * @return A new runnable object that will propagate the parent span to another thread. + * @return A new runnable object that will propagate the active span to the thread executing the call. * @see #of(Callable) */ public CallableWithActiveSpan withActiveSpan(Span activeSpan) { @@ -49,17 +50,17 @@ public CallableWithActiveSpan withActiveSpan(Span activeSpan) { } /** - * Performs the delegate call with the specified parent span. + * Performs the delegate call with the specified active span. * * @return The result from the original call. * @throws Exception if the original call threw an exception. */ public T call() throws Exception { - final SpanDeactivator deactivator = ActiveSpanManager.get().activate(activeSpanOfScheduler); + final ManagedSpan managedSpan = ActiveSpanManager.get().manage(activeSpanOfScheduler); try { return delegate.call(); - } finally { // TODO: error handling (preferably using Guava's Closer) - deactivator.deactivate(); + } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) + managedSpan.release(); } } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java similarity index 57% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java index 46ad23d..9ec6f02 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/RunnableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java @@ -1,44 +1,45 @@ -package io.opentracing.contrib.activespan.concurrent; +package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.SpanDeactivator; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.ManagedSpan; /** - * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. */ public class RunnableWithActiveSpan implements Runnable { protected final Runnable delegate; - private final Span parentSpan; + private final Span activeSpanOfScheduler; protected RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { if (delegate == null) throw new NullPointerException("Runnable delegate is ."); this.delegate = delegate; - this.parentSpan = parentSpan; + this.activeSpanOfScheduler = parentSpan; } /** - * Creates a new runnable that will execute with the {@link ActiveSpanManager#activeSpan() active span} + * Creates a new runnable that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. * * @param delegate The delegate runnable to execute (required, non-null). - * @return The wrapped runnable that propagates the active span to another thread. - * @see ActiveSpanManager#activeSpan() + * @return The wrapped runnable that propagates the currently active span to another thread. + * @see ActiveSpanManager#currentSpan() */ public static RunnableWithActiveSpan of(Runnable delegate) { - return new RunnableWithActiveSpan(delegate, ActiveSpanManager.get().activeSpan()); + return new RunnableWithActiveSpan(delegate, ActiveSpanManager.get().currentSpan()); } /** * This method allows the caller to override the active span in the new thread. *

- * Please note: it is not necessary to call this method with the - * {@link ActiveSpanManager#activeSpan() current active span} as that will be used {@link #of(Runnable) by default}. + * Please note: it is not necessary to call this method with the + * {@link ActiveSpanManager#currentSpan() currently active span} since + * that will be used {@link #of(Runnable) by default}. * * @param activeSpan The span to become the active span when running the delegate. - * @return A new runnable object that will propagate the given span to another thread. + * @return A new runnable object that will propagate the given span to the executing thread. * @see #of(Runnable) */ public RunnableWithActiveSpan withParent(Span activeSpan) { @@ -49,11 +50,11 @@ public RunnableWithActiveSpan withParent(Span activeSpan) { * Performs the runnable action with the specified parent span. */ public void run() { - SpanDeactivator deactivator = ActiveSpanManager.get().activate(parentSpan); + ManagedSpan managedSpan = ActiveSpanManager.get().manage(activeSpanOfScheduler); try { delegate.run(); - } finally { // TODO: error handling (preferably using Guava's Closer) - deactivator.deactivate(); + } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) + managedSpan.release(); } } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java similarity index 91% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java index 2de97a2..648bfbd 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java @@ -1,7 +1,7 @@ -package io.opentracing.contrib.activespan.concurrent; +package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.activespan.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; import java.util.ArrayList; import java.util.Collection; @@ -9,7 +9,7 @@ import java.util.concurrent.*; /** - * {@link ExecutorService} wrapper that will propagate the {@link ActiveSpanManager#activeSpan() active span} + * {@link ExecutorService} wrapper that propagates the {@link ActiveSpanManager#currentSpan() currently active span} * into the calls that are scheduled. */ public class SpanAwareExecutorService implements ExecutorService { @@ -69,7 +69,7 @@ public T invokeAny(Collection> tasks, long timeout, Ti protected Collection> spanAwareTasks(final Collection> tasks) { if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); Collection> result = new ArrayList>(tasks.size()); - Span activeSpan = ActiveSpanManager.get().activeSpan(); + Span activeSpan = ActiveSpanManager.get().currentSpan(); for (Callable task : tasks) { result.add(new CallableWithActiveSpan(task, activeSpan)); } diff --git a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java similarity index 87% rename from src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java index e0828d6..1a330a8 100644 --- a/src/main/java/io/opentracing/contrib/activespan/concurrent/SpanAwareExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java @@ -1,9 +1,21 @@ -package io.opentracing.contrib.activespan.concurrent; +package io.opentracing.contrib.spanmanager.concurrent; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +/** + * Factory-methods similar to standard java {@link Executors}: + *

    + *
  • {@link #newFixedThreadPool(int)}
  • + *
  • {@link #newSingleThreadExecutor()}
  • + *
  • {@link #newCachedThreadPool()}
  • + *
  • Variants of the above with additional {@link ThreadFactory} argument: + * {@link #newFixedThreadPool(int, ThreadFactory)}, + * {@link #newSingleThreadExecutor(ThreadFactory)}, + * {@link #newCachedThreadPool(ThreadFactory)} + *
  • + *
+ */ public final class SpanAwareExecutors { /** diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java new file mode 100644 index 0000000..2ce7da9 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java @@ -0,0 +1,158 @@ +package io.opentracing.contrib.spanmanager.tracer; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.contrib.spanmanager.ManagedSpan; + +import java.util.Map; + +/** + * A self-releasing managed span. + *

+ * This managed span will release itself when it is {@link #finish() finished} or {@link #close() closed}.
+ * All other span functionality is forwareded to the actual managed Span. + */ +final class AutoReleasingManagedSpan implements ManagedSpan, Span { + + private final ManagedSpan managedSpan; + + AutoReleasingManagedSpan(ManagedSpan managedSpan) { + if (managedSpan == null) throw new NullPointerException("Managed span was ."); + this.managedSpan = managedSpan; + } + + @Override + public Span getSpan() { + return managedSpan.getSpan(); + } + + /** + * Releases this currently active ManagedSpan. + */ + @Override + public void release() { + managedSpan.release(); + } + + /** + * Finishes the delegate and releases this currently active ManagedSpan. + */ + @Override + public void finish() { + try { + getSpan().finish(); + } finally { + this.release(); + } + } + + /** + * Finishes the delegate and releases this currently active ManagedSpan. + */ + @Override + public void finish(long finishMicros) { + try { + getSpan().finish(finishMicros); + } finally { + this.release(); + } + } + + /** + * Finishes the delegate and releases this currently active ManagedSpan. + */ + @Override + public void close() { + try { + getSpan().close(); + } finally { + this.release(); + } + } + + // Default behaviour is forwarded to the actual managed Span: + + @Override + public SpanContext context() { + return getSpan().context(); + } + + @Override + public Span setTag(String key, String value) { + getSpan().setTag(key, value); + return this; + } + + @Override + public Span setTag(String key, boolean value) { + getSpan().setTag(key, value); + return this; + } + + @Override + public Span setTag(String key, Number value) { + getSpan().setTag(key, value); + return this; + } + + @Override + public Span log(Map fields) { + getSpan().log(fields); + return this; + } + + @Override + public Span log(long timestampMicroseconds, Map fields) { + getSpan().log(timestampMicroseconds, fields); + return this; + } + + @Override + public Span log(String event) { + getSpan().log(event); + return this; + } + + @Override + public Span log(long timestampMicroseconds, String event) { + getSpan().log(timestampMicroseconds, event); + return this; + } + + @Override + public Span setBaggageItem(String key, String value) { + getSpan().setBaggageItem(key, value); + return this; + } + + @Override + public String getBaggageItem(String key) { + return getSpan().getBaggageItem(key); + } + + @Override + public Span setOperationName(String operationName) { + getSpan().setOperationName(operationName); + return this; + } + + @SuppressWarnings("deprecation") // We simply delegate this method as we're told. + @Override + public Span log(String eventName, Object payload) { + getSpan().log(eventName, payload); + return this; + } + + @SuppressWarnings("deprecation") // We simply delegate this method as we're told. + @Override + public Span log(long timestampMicroseconds, String eventName, Object payload) { + getSpan().log(timestampMicroseconds, eventName, payload); + return this; + } + + @Override + public String toString() { + return getClass().getSimpleName() + '{' + managedSpan + '}'; + } + +} diff --git a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java similarity index 56% rename from src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java rename to src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index 42dfa11..28047f4 100644 --- a/src/main/java/io/opentracing/contrib/activespan/tracer/ActiveSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -1,37 +1,33 @@ -package io.opentracing.contrib.activespan.tracer; +package io.opentracing.contrib.spanmanager.tracer; import io.opentracing.NoopSpanBuilder; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; -import io.opentracing.contrib.activespan.ActiveSpanManager; -import io.opentracing.contrib.activespan.SpanDeactivator; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.ManagedSpan; import java.util.Map; /** * {@link SpanBuilder} that forwards all methods to a delegate.
- * Only the {@link #start()} method is overridden, {@link ActiveSpanManager#activate(Span) activating} - * the started {@link Span} and wrapping it in an {@link ActiveSpan} object.
- * The {@link ActiveSpan} object {@link SpanDeactivator deactivates} the span automatically - * when it is {@link ActiveSpan#finish() finished} or {@link ActiveSpan#close() closed}. + * The {@link #start()} method is overridden to automatically {@link ActiveSpanManager#manage(Span) manage} + * the started {@link Span}, wrapping it in an {@link AutoReleasingManagedSpan} object.
+ * The {@link AutoReleasingManagedSpan} object {@link ManagedSpan#release() releases} the span automatically + * when it is {@link Span#finish() finished} or {@link Span#close() closed}. * - * @see ActiveSpanManager#activate(Span) - * @see ActiveSpan#finish() + * @see ActiveSpanManager#manage(Span) + * @see AutoReleasingManagedSpan#finish() */ -final class ActiveSpanBuilder implements SpanBuilder { +final class ManagedSpanBuilder implements SpanBuilder { protected SpanBuilder delegate; - private ActiveSpanBuilder(SpanBuilder delegate) { + ManagedSpanBuilder(SpanBuilder delegate) { if (delegate == null) throw new NullPointerException("Delegate SpanBuilder was ."); this.delegate = delegate; } - static SpanBuilder of(SpanBuilder spanBuilder) { - return spanBuilder instanceof NoopSpanBuilder ? spanBuilder : new ActiveSpanBuilder(spanBuilder); - } - /** * Replaces the {@link #delegate} SpanBuilder by a delegated-method result. *

@@ -42,38 +38,38 @@ static SpanBuilder of(SpanBuilder spanBuilder) { * @return Either this re-wrapped ActiveSpanBuilder or the NoopSpanBuilder. */ SpanBuilder rewrap(SpanBuilder spanBuilder) { - if (spanBuilder == null || spanBuilder instanceof NoopSpanBuilder) return NoopSpanBuilder.INSTANCE; - this.delegate = spanBuilder; + if (spanBuilder != null) this.delegate = spanBuilder; return this; } /** - * Starts the built Span and {@link ActiveSpanManager#activate(Span) activates} it. + * Starts the built Span and {@link ActiveSpanManager#manage(Span) activates} it. * - * @return a new 'active' Span that deactivates itself upon finish or close calls. - * @see ActiveSpan#finish() - * @see ActiveSpanManager#activate(Span) + * @return a new 'currently active' Span that deactivates itself upon finish or close calls. + * @see ActiveSpanManager#manage(Span) + * @see AutoReleasingManagedSpan#release() */ @Override public Span start() { Span newSpan = delegate.start(); - return new ActiveSpan(newSpan, ActiveSpanManager.get().activate(newSpan)); + // TODO: Do we want to make NoopSpan instances managed? + return new AutoReleasingManagedSpan(ActiveSpanManager.get().manage(newSpan)); } // All other methods are forwarded to the delegate SpanBuilder. public SpanBuilder asChildOf(SpanContext parent) { - if (parent instanceof ActiveSpanBuilder) parent = ((ActiveSpanBuilder) parent).delegate; + if (parent instanceof ManagedSpanBuilder) parent = ((ManagedSpanBuilder) parent).delegate; return rewrap(delegate.asChildOf(parent)); } public SpanBuilder asChildOf(Span parent) { - if (parent instanceof ActiveSpan) parent = ((ActiveSpan) parent).delegate; + if (parent instanceof ManagedSpan) parent = ((ManagedSpan) parent).getSpan(); return rewrap(delegate.asChildOf(parent)); } public SpanBuilder addReference(String referenceType, SpanContext context) { - if (context instanceof ActiveSpanBuilder) context = ((ActiveSpanBuilder) context).delegate; + if (context instanceof ManagedSpanBuilder) context = ((ManagedSpanBuilder) context).delegate; return rewrap(delegate.addReference(referenceType, context)); } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java new file mode 100644 index 0000000..c050347 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -0,0 +1,48 @@ +package io.opentracing.contrib.spanmanager.tracer; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.ManagedSpan; +import io.opentracing.propagation.Format; + +/** + * Convenience {@link Tracer} that automates managing the {@linkplain ActiveSpanManager#currentSpan() active span}: + *

    + *
  1. It is a wrapper that forwards all calls to another {@link Tracer} implementation.
  2. + *
  3. {@link Span} instances created with this tracer are {@link ActiveSpanManager#manage(Span) managed} when started + * (automatically becoming the {@link ActiveSpanManager#currentSpan() currently active span}) + *
  4. + *
  5. and {@link ManagedSpan#release() released} when finished.
  6. + *
+ */ +public class ManagedSpanTracer implements Tracer { + + protected final Tracer delegate; + + public ManagedSpanTracer(Tracer delegate) { + if (delegate == null) throw new NullPointerException("The delegate Tracer implementation is ."); + this.delegate = delegate; + } + + public void inject(SpanContext spanContext, Format format, C carrier) { + // Weird that Builder extends Context! + if (spanContext instanceof ManagedSpanBuilder) spanContext = ((ManagedSpanBuilder) spanContext).delegate; + delegate.inject(spanContext, format, carrier); + } + + public SpanContext extract(Format format, C carrier) { + return delegate.extract(format, carrier); + } + + public SpanBuilder buildSpan(String operationName) { + return new ManagedSpanBuilder(delegate.buildSpan(operationName)); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{delegate=" + delegate + '}'; + } + +} From 4021d3afc2603d6e6ab695d1538df9f6d84b620d Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 4 Jan 2017 13:48:18 +0100 Subject: [PATCH 10/51] Typo in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e80c70e..ee44a25 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # java-activespan -This library provides a way to manage a _current span_ within and propagate it to other threads. +This library provides a way to manage a _current span_ and propagate it to other threads. ## SpanManager From 3dcb3ab6ca968655b9997ccc34fe19551e4c9a97 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 4 Jan 2017 14:03:39 +0100 Subject: [PATCH 11/51] Removed dot from pom.xml name+description. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 55c1e0e..acab460 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ jar - SpanManager library. - Library for managing the currently active span and propagating it accross threads. + SpanManager library + Library for managing the currently active span and propagating it accross threads https://github.com/opentracing-contrib/java-activespan 2016 From ea1db2b276e7a057ced6aa8447906846bfb63fda Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 4 Jan 2017 17:24:53 +0100 Subject: [PATCH 12/51] Removed unnecessary code from concurrent package. --- .../concurrent/CallableWithActiveSpan.java | 34 ++----------- .../concurrent/RunnableWithActiveSpan.java | 33 ++---------- .../concurrent/SpanAwareExecutorService.java | 51 ++++++++++--------- 3 files changed, 32 insertions(+), 86 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java index a599e71..194c150 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java @@ -10,45 +10,17 @@ * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. */ -public class CallableWithActiveSpan implements Callable { +final class CallableWithActiveSpan implements Callable { - protected final Callable delegate; + private final Callable delegate; private final Span activeSpanOfScheduler; - protected CallableWithActiveSpan(Callable delegate, Span activeSpanOfScheduler) { + CallableWithActiveSpan(Callable delegate, Span activeSpanOfScheduler) { if (delegate == null) throw new NullPointerException("Callable delegate is ."); this.delegate = delegate; this.activeSpanOfScheduler = activeSpanOfScheduler; } - /** - * Creates a new callable that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} - * from the scheduling thread. - * - * @param delegate The delegate callable to execute (required, non-null). - * @param The result type of the call. - * @return The 'span aware' callable that will propagate the currently active span to the new thread. - * @see ActiveSpanManager#currentSpan() - */ - public static CallableWithActiveSpan of(Callable delegate) { - return new CallableWithActiveSpan(delegate, ActiveSpanManager.get().currentSpan()); - } - - /** - * This method allows the caller to override the active span for the new thread. - *

- * Please note: it is not necessary to call this method with the - * {@link ActiveSpanManager#currentSpan() currently active span} since - * that will be used {@link #of(Callable) by default}. - * - * @param activeSpan The span to become the active span when calling the delegate. - * @return A new runnable object that will propagate the active span to the thread executing the call. - * @see #of(Callable) - */ - public CallableWithActiveSpan withActiveSpan(Span activeSpan) { - return new CallableWithActiveSpan(delegate, activeSpan); - } - /** * Performs the delegate call with the specified active span. * diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java index 9ec6f02..3b87499 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java @@ -8,44 +8,17 @@ * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} * from the scheduling thread. */ -public class RunnableWithActiveSpan implements Runnable { +final class RunnableWithActiveSpan implements Runnable { - protected final Runnable delegate; + private final Runnable delegate; private final Span activeSpanOfScheduler; - protected RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { + RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { if (delegate == null) throw new NullPointerException("Runnable delegate is ."); this.delegate = delegate; this.activeSpanOfScheduler = parentSpan; } - /** - * Creates a new runnable that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} - * from the scheduling thread. - * - * @param delegate The delegate runnable to execute (required, non-null). - * @return The wrapped runnable that propagates the currently active span to another thread. - * @see ActiveSpanManager#currentSpan() - */ - public static RunnableWithActiveSpan of(Runnable delegate) { - return new RunnableWithActiveSpan(delegate, ActiveSpanManager.get().currentSpan()); - } - - /** - * This method allows the caller to override the active span in the new thread. - *

- * Please note: it is not necessary to call this method with the - * {@link ActiveSpanManager#currentSpan() currently active span} since - * that will be used {@link #of(Runnable) by default}. - * - * @param activeSpan The span to become the active span when running the delegate. - * @return A new runnable object that will propagate the given span to the executing thread. - * @see #of(Runnable) - */ - public RunnableWithActiveSpan withParent(Span activeSpan) { - return new RunnableWithActiveSpan(delegate, activeSpan); - } - /** * Performs the runnable action with the specified parent span. */ diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java index 648bfbd..978d358 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java @@ -26,54 +26,37 @@ public static SpanAwareExecutorService wrap(final ExecutorService delegate) { } public void execute(Runnable command) { - delegate.execute(RunnableWithActiveSpan.of(command)); + delegate.execute(spanAware(command)); } public Future submit(Runnable task) { - return delegate.submit(RunnableWithActiveSpan.of(task)); + return delegate.submit(spanAware(task)); } public Future submit(Runnable task, T result) { - return delegate.submit(RunnableWithActiveSpan.of(task), result); + return delegate.submit(spanAware(task), result); } public Future submit(Callable task) { - return delegate.submit(CallableWithActiveSpan.of(task)); + return delegate.submit(spanAware(task)); } public List> invokeAll(Collection> tasks) throws InterruptedException { - return delegate.invokeAll(spanAwareTasks(tasks)); + return delegate.invokeAll(spanAware(tasks)); } public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { - return delegate.invokeAll(spanAwareTasks(tasks), timeout, unit); + return delegate.invokeAll(spanAware(tasks), timeout, unit); } public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(spanAwareTasks(tasks)); + return delegate.invokeAny(spanAware(tasks)); } public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(spanAwareTasks(tasks), timeout, unit); - } - - /** - * Wraps {@link CallableWithActiveSpan} objects. - * - * @param tasks The tasks to be scheduled. - * @param The common type of all scheduled tasks. - * @return A new collection of 'span aware' callable objects that run with the active span of the scheduling service. - */ - protected Collection> spanAwareTasks(final Collection> tasks) { - if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); - Collection> result = new ArrayList>(tasks.size()); - Span activeSpan = ActiveSpanManager.get().currentSpan(); - for (Callable task : tasks) { - result.add(new CallableWithActiveSpan(task, activeSpan)); - } - return result; + return delegate.invokeAny(spanAware(tasks), timeout, unit); } public void shutdown() { @@ -96,4 +79,22 @@ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedE return delegate.awaitTermination(timeout, unit); } + private static Runnable spanAware(Runnable runnable) { + return new RunnableWithActiveSpan(runnable, ActiveSpanManager.get().currentSpan()); + } + + private static Callable spanAware(Callable callable) { + return new CallableWithActiveSpan(callable, ActiveSpanManager.get().currentSpan()); + } + + private static Collection> spanAware(final Collection> tasks) { + if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); + Span activeSpan = ActiveSpanManager.get().currentSpan(); + Collection> result = new ArrayList>(tasks.size()); + for (Callable task : tasks) { + result.add(new CallableWithActiveSpan(task, activeSpan)); + } + return result; + } + } From 1d4aa87e434d01d0f8d6950c1465101bdd0a8e75 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 5 Jan 2017 11:55:14 +0100 Subject: [PATCH 13/51] More clarification + simplification where possible. --- README.md | 81 +++++----- .../spanmanager/ActiveSpanManager.java | 128 +++++++--------- .../contrib/spanmanager/SpanManager.java | 5 +- .../spanmanager/ThreadLocalSpanManager.java | 31 ++-- .../concurrent/CallableWithActiveSpan.java | 7 +- .../concurrent/RunnableWithActiveSpan.java | 7 +- .../concurrent/SpanAwareExecutorService.java | 100 ------------ .../SpanPropagatingExecutorService.java | 142 ++++++++++++++++++ ...ors.java => SpanPropagatingExecutors.java} | 31 ++-- .../tracer/AutoReleasingManagedSpan.java | 18 +-- .../tracer/ManagedSpanBuilder.java | 26 ++-- .../spanmanager/tracer/ManagedSpanTracer.java | 17 ++- 12 files changed, 303 insertions(+), 290 deletions(-) delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java rename src/main/java/io/opentracing/contrib/spanmanager/concurrent/{SpanAwareExecutors.java => SpanPropagatingExecutors.java} (68%) diff --git a/README.md b/README.md index ee44a25..c2f5c44 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,56 @@ # java-activespan -This library provides a way to manage a _current span_ and propagate it to other threads. - -## SpanManager - -The core of this library, this interface defines - 1. The `currentSpan()` method to return the _currently managed span_. - 2. The `manage(Span)` method to make the specified `Span` the currently managed span. - A `ManagedSpan` instance is returned to release this _currently managed span_. - 3. The `clear()` method to releases all managed spans including any parents. - -## ManagedSpan - -Obtained from the `SpanManager`, this object can be used to -release the _currently managed span_ again. - 1. The `getSpan()` method returns the managed `Span` object. - 2. The `release()` method 'unmanages' the span. - Implementors are encouraged to restore the managed span as it was before, - providing stack-like behaviour. - It must be possible to repeatedly call `release()` without side effects. - 3. `ManagedSpan` extends `Closeable` where `close()` is an alias for `release()` - so it can reliably be used in `try-with-resources` blocks. - **Please note:** _Closing a ManagedSpan does **not** automatically finish the actual span._ - (with the `ManagedSpanTracer` being an exception to this rule) +This library provides a way to manage spans and propagate them to other threads. ## ActiveSpanManager -Provides the `ActiveSpanManager.get()` method that returns the singleton _active span manager_. -Upon first use of this manager, it lazily determines which `SpanManager` implementation to use: - 1. If an explicitly configured `SpanManager` was provided via the `ActiveSpanManager.set()` method, - that will always take precedence over automatically resolved manager instances. - 2. A `SpanManager` implementation can be automatically provided using the java `ServiceLoader` through the - `META-INF/services/io.opentracing.contrib.spanmanager.SpanManager` service definition file. - The ActiveSpanManager class will not attempt to choose between implementations; - if more than one is found, a warning is logged and the default implementation is used: - 3. If no `SpanManager` is found, a default implementation is used, - providing `ThreadLocal` storage to manage the active span. +Static utility to access the _currently active span_. + 1. The `activeSpan()` method returns the _active span_ in the current process. + If there is no active span, a `NoopSpan` is returned instead. + 2. This _active span_ can be set through the `activate(Span)` method, + returning a `ManagedSpan` for later deactivation. + 3. `clear()` provides unconditional cleanup of _all active spans_ for the current process. + 4. A custom implementation can be registered by calling `register(SpanManager)`. + _see section 'Custom span managers'_ ## Concurrency -### SpanAwareExecutorService +### SpanPropagatingExecutorService -This `ExecutorService` wraps an existing threadpool and propagates the currently active span -of the caller into the tasks it executes. -Please note: This ExecutorService does not automatically start or finish spans, -but merely propagates the _currently active_ span into the jobs being executed. +This `ExecutorService` propagates the _active span_ from the caller into each call that is executed. +Please Note: The active span is merely propagated as-is. +It is explicitly **not** finished by the calls. -### SpanAwareExecutors +### SpanPropagatingExecutors -Factory-methods similar to standard java `Executors`: - - `SpanAwareExecutors.newFixedThreadPool(int)` - - `SpanAwareExecutors.newSingleThreadExecutor()` - - `SpanAwareExecutors.newCachedThreadPool()` +Provides factory-methods similar to standard java `Executors`: + - `SpanPropagatingExecutors.newFixedThreadPool(int)` + - `SpanPropagatingExecutors.newSingleThreadExecutor()` + - `SpanPropagatingExecutors.newCachedThreadPool()` - Variants of the above with additional `ThreadFactory` argument. ### ManagedSpanTracer This convenience `Tracer` automates managing the _active span_: 1. It is a wrapper that forwards all calls to another `Tracer` implementation. - 2. `Span` instances created with this tracer are managed when started - (automatically becoming the currently active span) - and released when finished. + 2. Spans created with this tracer are automatically _activated_ when started, + 3. and automatically _released_ when they finish. + +## Custom span managers + +It is possible to provide a custom implementation for `ActiveSpanManager`. +This may be useful if you already have a way to propagate contextual information +from one thread to another. Creating a custom manager is one way to piggyback the _active span_ on +your existing propagation mechanism. + +The _active_ SpanManager is resolved as follows: + 1. The last-registered span manager always takes precedence. + 2. If no manager was registered, one is looked up from the `ServiceLoader`. + The ActiveSpanManager will not attempt to choose between implementations: + 3. If no single implementation is found, a default implementation will be used. + ## Examples _TODO create examples_ + diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java index 509bc3a..ea195bf 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java @@ -1,5 +1,6 @@ package io.opentracing.contrib.spanmanager; +import io.opentracing.NoopSpan; import io.opentracing.Span; import java.util.Iterator; @@ -9,121 +10,98 @@ import java.util.logging.Logger; /** - * Manages the currently active {@link Span}.
- * A {@link Span} becomes active in the current process after a call to {@link #manage(Span)} - * and can be deactivated again by calling {@link ManagedSpan#release()}. + * Static utility to access the currently active {@link Span}. + *

+ * The {@link SpanManager} responsible for managing the currently active span is lazily resolved: + *

    + *
  1. The last-{@link #register(SpanManager) registered} span manager always takes precedence.
  2. + *
  3. If no manager was registered, one is looked up from the {@link ServiceLoader}.
    + * The ActiveSpanManager will not attempt to choose between implementations:
  4. + *
  5. If no single implementation is found, a default implementation will be used.
  6. + *
+ *

+ * The {@linkplain ThreadLocalSpanManager default implementation} uses {@link ThreadLocal} storage, + * maintaining a stack-like structure of linked managed spans. + * + * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutors + * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutorService */ -public final class ActiveSpanManager implements SpanManager { +public final class ActiveSpanManager { private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); /** - * Singleton instance. + * The resolved {@link SpanManager} to delegate the implementation to. *

- * Since we cannot prevent people using {@linkplain #get() ActiveSpanManager.get()} as a constant, - * this guarantees that references obtained before, during or after initialization - * all behave as if obtained after initialization once properly initialized.
- * As a minor additional benefit it makes it harder to circumvent the {@link SpanManager} API. + * This can be an {@link #register(SpanManager) explicitly registered delegate} + * or an {@link #loadSingleSpiImplementation() automatically resolved SpanManager service}, + * (or null during initialization). */ - private static final ActiveSpanManager INSTANCE = new ActiveSpanManager(); - - /** - * The resolved {@link SpanManager} to delegate the implementation to.
- * This can be either an {@link #set(SpanManager) explicitly set delegate} - * or the automatically resolved service implementation. - */ - private final AtomicReference activeSpanManager = new AtomicReference(); + private static final AtomicReference ACTIVE_SPAN_MANAGER = new AtomicReference(); private ActiveSpanManager() { + throw new UnsupportedOperationException(); } - private SpanManager lazySpanManager() { - SpanManager instance = this.activeSpanManager.get(); - if (instance == null) { - final SpanManager singleton = loadSingleSpiImplementation(); - while (instance == null && singleton != null) { // handle rare race condition - this.activeSpanManager.compareAndSet(null, singleton); - instance = this.activeSpanManager.get(); + private static SpanManager lazySpanManager() { + SpanManager spanManager = ACTIVE_SPAN_MANAGER.get(); + if (spanManager == null) { + final SpanManager resolved = loadSingleSpiImplementation(); + while (spanManager == null && resolved != null) { // handle rare race condition + ACTIVE_SPAN_MANAGER.compareAndSet(null, resolved); + spanManager = ACTIVE_SPAN_MANAGER.get(); } - LOGGER.log(Level.INFO, "Using ActiveSpanManager implementation: {0}.", instance); + LOGGER.log(Level.INFO, "Using SpanManager implementation: {0}.", spanManager); } - return instance; - } - - /** - * Returns the {@linkplain SpanManager} instance. - * Upon first use of any method, this manager lazily determines which actual {@link SpanManager} implementation - * to use: - *

    - *
  1. If an explicitly configured manager was provided via the {@link #set(SpanManager)} method, - * that will always take precedence over automatically resolved instances.
  2. - *
  3. An implementation can automatically be provided using the Java {@link ServiceLoader} through the - * META-INF/services/io.opentracing.contrib.spanmanager.SpanManager service definition file.
    - * The {@link ActiveSpanManager} will not attempt to choose between implementations; - * if more than one is found by the {@linkplain ServiceLoader service loader}, - * a warning is logged and tracing is disabled by falling back to the default implementation:
  4. - *
  5. If no tracer implementation is available, a default {@link ThreadLocal} based implementation is used.
  6. - *
- * - * @return The active SpanManager. - */ - public static SpanManager get() { - return INSTANCE; + return spanManager; } /** - * Programmatically sets a SpanManager implementation as the singleton instance. + * Explicitly configures a SpanManager to back the behaviour of the {@link ActiveSpanManager}. *

* The previous manager is returned so it can be: *

    - *
  • {@link #clear() cleared} as it no longer is the active manager, or
  • + *
  • {@link SpanManager#clear() cleared} as it no longer is the active manager, or
  • *
  • restored later, for example in a testing situation
  • *
* - * @param spanManager The overridden implementation to use for in-process span management. - * @return The previous SpanManager, or null if there was none. + * @param spanManager Manager for in-process span propagation. + * @return The previously active SpanManager, or null if there was none. */ - public static SpanManager set(SpanManager spanManager) { - if (spanManager instanceof ActiveSpanManager) { - LOGGER.log(Level.FINE, "Attempted to set the ActiveSpanManager as delegate of itself."); - return INSTANCE.activeSpanManager.get(); // no-op, return 'previous' manager. - } - return INSTANCE.activeSpanManager.getAndSet(spanManager); + public static SpanManager register(SpanManager spanManager) { + SpanManager previous = ACTIVE_SPAN_MANAGER.getAndSet(spanManager); + LOGGER.log(Level.INFO, "Registered ActiveSpanManager {0} (previously {1}).", new Object[]{spanManager, previous}); + return previous; } /** - * Return the currently active {@link Span}. + * Return the active {@link Span} in the current process. * * @return The currently active Span, or the NoopSpan if there is no active span. - * @see SpanManager#manage(Span) + * @see SpanManager#currentSpan() */ - @Override - public Span currentSpan() { - return lazySpanManager().currentSpan(); + public static Span activeSpan() { + Span activeSpan = lazySpanManager().currentSpan(); + return activeSpan != null ? activeSpan : NoopSpan.INSTANCE; } /** - * Makes span the currently active span within the running process. + * Makes span the active span for the current process. * - * @param span The span to become the active span. - * @return The managed object to release the currently active span with. - * @see SpanManager#currentSpan() + * @param span The span to become the new active span. + * @return The managed object to release the new active span with. + * @see SpanManager#manage(Span) * @see ManagedSpan#release() */ - @Override - public ManagedSpan manage(Span span) { + public static ManagedSpan activate(Span span) { return lazySpanManager().manage(span); } /** - * Releases all managed spans including any parents. - *

- * This allows boundary filters to release all spans before relinquishing control over their Thread, - * which may end up repurposed by a threadpool. + * Unconditional cleanup of all active spans for the current process. * - * @see ManagedSpan#release() + * @see SpanManager#clear() */ - @Override - public void clear() { + public static void clear() { lazySpanManager().clear(); } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index c2b3384..a05b412 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -27,9 +27,10 @@ public interface SpanManager { ManagedSpan manage(Span span); /** - * Releases all managed spans including any parents. + * Unconditional cleanup of all managed spans including any parents. *

- * This allows boundary filters to release all spans before relinquishing control over their Thread, + * This allows boundary filters to release all active spans + * before relinquishing control over their process, * which may end up repurposed by a threadpool. * * @see ManagedSpan#release() diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java index f721c4a..871b683 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java @@ -8,13 +8,15 @@ import java.util.logging.Logger; /** - * Default ThreadLocal-based implementation of {@link SpanManager} that implements - * the following stack unwinding algorithm upon deactivation: + * Default {@link SpanManager} implementation using {@link ThreadLocal} storage, + * maintaining a stack-like structure of linked managed spans. + *

+ * The linked managed spans provide the following stack unwinding algorithm: *

    - *
  1. If the deactivated span is not the managed span, the managed span is left alone.
  2. - *
  3. Otherwise, the first parent that is not yet deactivated is set as the new managed span.
  4. - *
  5. If no managed parents remain, the managed span {@link ThreadLocal} is cleared.
  6. - *
  7. Consecutive release() calls for already-deactivated spans will be ignored.
  8. + *
  9. If the released span is not the managed span, the managed span is left alone.
  10. + *
  11. Otherwise, the first parent that is not yet released is set as the new managed span.
  12. + *
  13. If no managed parents remain, the managed span is cleared.
  14. + *
  15. Consecutive release() calls for already-released spans will be ignored.
  16. *
*/ final class ThreadLocalSpanManager implements SpanManager { @@ -27,13 +29,13 @@ final class ThreadLocalSpanManager implements SpanManager { @Override public Span currentSpan() { - final ManagedSpan managedSpan = MANAGED.get(); + ManagedSpan managedSpan = MANAGED.get(); return managedSpan != null ? managedSpan.span : NoopSpan.INSTANCE; } @Override public io.opentracing.contrib.spanmanager.ManagedSpan manage(Span span) { - ManagedSpan managedSpan = new ManagedSpan(span); + ManagedSpan managedSpan = new ManagedSpan(span, MANAGED.get()); MANAGED.set(managedSpan); return managedSpan; } @@ -49,11 +51,12 @@ public String toString() { } private static final class ManagedSpan implements io.opentracing.contrib.spanmanager.ManagedSpan { - private final ManagedSpan parent = MANAGED.get(); + private final ManagedSpan parent; private final Span span; - private final AtomicBoolean deactivated = new AtomicBoolean(false); + private final AtomicBoolean released = new AtomicBoolean(false); - private ManagedSpan(final Span span) { + private ManagedSpan(Span span, ManagedSpan parent) { + this.parent = parent; this.span = span; } @@ -66,10 +69,10 @@ public Span getSpan() { * Please see {@link ThreadLocalSpanManager outer class description} for the stack-unwinding documentation. */ public void release() { - if (deactivated.compareAndSet(false, true)) { + if (released.compareAndSet(false, true)) { ManagedSpan current = MANAGED.get(); if (this == current) { - while (current != null && current.deactivated.get()) { + while (current != null && current.released.get()) { current = current.parent; } if (current == null) MANAGED.remove(); @@ -79,7 +82,7 @@ public void release() { LOGGER.log(Level.FINE, "Deactivated {0} without affecting managed span {1}.", new Object[]{this, current}); } } else { - LOGGER.log(Level.FINEST, "No action needed, {0} was already deactivated.", this); + LOGGER.log(Level.FINEST, "No action needed, {0} was already released.", this); } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java index 194c150..6b29b93 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java @@ -7,8 +7,9 @@ import java.util.concurrent.Callable; /** - * {@link Callable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} - * from the scheduling thread. + * {@link Callable} wrapper that will execute with a custom active span specified from the scheduling thread. + * + * @see ActiveSpanManager */ final class CallableWithActiveSpan implements Callable { @@ -28,7 +29,7 @@ final class CallableWithActiveSpan implements Callable { * @throws Exception if the original call threw an exception. */ public T call() throws Exception { - final ManagedSpan managedSpan = ActiveSpanManager.get().manage(activeSpanOfScheduler); + final ManagedSpan managedSpan = ActiveSpanManager.activate(activeSpanOfScheduler); try { return delegate.call(); } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java index 3b87499..99c45e6 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java @@ -5,8 +5,9 @@ import io.opentracing.contrib.spanmanager.ManagedSpan; /** - * {@link Runnable} wrapper that will execute with the {@link ActiveSpanManager#currentSpan() currently active span} - * from the scheduling thread. + * {@link Runnable} wrapper that will execute with a custom active span specified from the scheduling thread. + * + * @see ActiveSpanManager */ final class RunnableWithActiveSpan implements Runnable { @@ -23,7 +24,7 @@ final class RunnableWithActiveSpan implements Runnable { * Performs the runnable action with the specified parent span. */ public void run() { - ManagedSpan managedSpan = ActiveSpanManager.get().manage(activeSpanOfScheduler); + ManagedSpan managedSpan = ActiveSpanManager.activate(activeSpanOfScheduler); try { delegate.run(); } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java deleted file mode 100644 index 978d358..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutorService.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.opentracing.contrib.spanmanager.concurrent; - -import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.*; - -/** - * {@link ExecutorService} wrapper that propagates the {@link ActiveSpanManager#currentSpan() currently active span} - * into the calls that are scheduled. - */ -public class SpanAwareExecutorService implements ExecutorService { - protected final ExecutorService delegate; - - protected SpanAwareExecutorService(ExecutorService delegate) { - if (delegate == null) throw new NullPointerException("Delegate executor service is ."); - this.delegate = delegate; - } - - public static SpanAwareExecutorService wrap(final ExecutorService delegate) { - return delegate instanceof SpanAwareExecutorService ? (SpanAwareExecutorService) delegate - : new SpanAwareExecutorService(delegate); - } - - public void execute(Runnable command) { - delegate.execute(spanAware(command)); - } - - public Future submit(Runnable task) { - return delegate.submit(spanAware(task)); - } - - public Future submit(Runnable task, T result) { - return delegate.submit(spanAware(task), result); - } - - public Future submit(Callable task) { - return delegate.submit(spanAware(task)); - } - - public List> invokeAll(Collection> tasks) throws InterruptedException { - return delegate.invokeAll(spanAware(tasks)); - } - - public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException { - return delegate.invokeAll(spanAware(tasks), timeout, unit); - } - - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(spanAware(tasks)); - } - - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(spanAware(tasks), timeout, unit); - } - - public void shutdown() { - delegate.shutdown(); - } - - public List shutdownNow() { - return delegate.shutdownNow(); - } - - public boolean isShutdown() { - return delegate.isShutdown(); - } - - public boolean isTerminated() { - return delegate.isTerminated(); - } - - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return delegate.awaitTermination(timeout, unit); - } - - private static Runnable spanAware(Runnable runnable) { - return new RunnableWithActiveSpan(runnable, ActiveSpanManager.get().currentSpan()); - } - - private static Callable spanAware(Callable callable) { - return new CallableWithActiveSpan(callable, ActiveSpanManager.get().currentSpan()); - } - - private static Collection> spanAware(final Collection> tasks) { - if (tasks == null) throw new NullPointerException("Collection of scheduled tasks is ."); - Span activeSpan = ActiveSpanManager.get().currentSpan(); - Collection> result = new ArrayList>(tasks.size()); - for (Callable task : tasks) { - result.add(new CallableWithActiveSpan(task, activeSpan)); - } - return result; - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java new file mode 100644 index 0000000..41ad18f --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -0,0 +1,142 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.ActiveSpanManager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.*; + +/** + * Propagates the {@link ActiveSpanManager#activeSpan() active span} from the caller into each call that is executed. + *

+ * Note: The active span is merely propagated. It is explicitly not finished by the calls. + */ +public class SpanPropagatingExecutorService implements ExecutorService { + private final ExecutorService delegate; + + private SpanPropagatingExecutorService(ExecutorService delegate) { + if (delegate == null) throw new NullPointerException("Delegate executor service is ."); + this.delegate = delegate; + } + + /** + * Wraps the delegate ExecutorService to propagate the {@link ActiveSpanManager#activeSpan() active span} + * of callers into the executed calls. + * + * @param delegate The executorservice to forward calls to. + * @return An ExecutorService that propagates active spans from callers into executed calls. + */ + public static SpanPropagatingExecutorService wrap(final ExecutorService delegate) { + if (delegate instanceof SpanPropagatingExecutorService) return (SpanPropagatingExecutorService) delegate; + return new SpanPropagatingExecutorService(delegate); + } + + /** + * Propagates the {@link ActiveSpanManager#activeSpan() currently active span} into the runnable + * and performs cleanup afterwards. + *

+ * Note: The activeSpan is merely propagated. + * The specified span is explicitly not finished by the runnable. + * + * @param runnable The runnable to be executed. + * @param activeSpan The span to be propagated. + * @return The wrapped runnable to execute with the active span. + */ + public static Runnable runnableWithActiveSpan(Runnable runnable, Span activeSpan) { + return new RunnableWithActiveSpan(runnable, activeSpan); + } + + /** + * Propagates the {@link ActiveSpanManager#activeSpan() currently active span} into the callable + * and performs cleanup afterwards. + *

+ * Note: The activeSpan is merely propagated. + * The specified span is explicitly not finished by the callable. + * + * @param The callable result type. + * @param callable The callable to be executed. + * @param activeSpan The span to be propagated. + * @return The wrapped callable to execute with the active span. + */ + public static Callable callableWithActiveSpan(Callable callable, Span activeSpan) { + return new CallableWithActiveSpan(callable, activeSpan); + } + + @Override + public void execute(Runnable command) { + delegate.execute(runnableWithActiveSpan(command, ActiveSpanManager.activeSpan())); + } + + @Override + public Future submit(Runnable task) { + return delegate.submit(runnableWithActiveSpan(task, ActiveSpanManager.activeSpan())); + } + + @Override + public Future submit(Runnable task, T result) { + return delegate.submit(runnableWithActiveSpan(task, ActiveSpanManager.activeSpan()), result); + } + + @Override + public Future submit(Callable task) { + return delegate.submit(callableWithActiveSpan(task, ActiveSpanManager.activeSpan())); + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + return delegate.invokeAll(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan())); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate.invokeAll(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan()), timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return delegate.invokeAny(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan())); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.invokeAny(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan()), timeout, unit); + } + + @Override + public void shutdown() { + delegate.shutdown(); + } + + @Override + public List shutdownNow() { + return delegate.shutdownNow(); + } + + @Override + public boolean isShutdown() { + return delegate.isShutdown(); + } + + @Override + public boolean isTerminated() { + return delegate.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate.awaitTermination(timeout, unit); + } + + private static Collection> tasksWithActiveSpan( + Collection> tasks, Span activeSpan) { + if (tasks == null) throw new NullPointerException("Collection of tasks is ."); + Collection> result = new ArrayList>(tasks.size()); + for (Callable task : tasks) result.add(callableWithActiveSpan(task, activeSpan)); + return result; + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java similarity index 68% rename from src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index 1a330a8..a5ce298 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanAwareExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -1,5 +1,6 @@ package io.opentracing.contrib.spanmanager.concurrent; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -15,13 +16,15 @@ * {@link #newCachedThreadPool(ThreadFactory)} * * + * + * @see SpanPropagatingExecutorService#wrap(ExecutorService) */ -public final class SpanAwareExecutors { +public final class SpanPropagatingExecutors { /** * Private constructor to avoid instantiation of this utility class. */ - private SpanAwareExecutors() { + private SpanPropagatingExecutors() { throw new UnsupportedOperationException(); } @@ -33,8 +36,8 @@ private SpanAwareExecutors() { * @return the newly created thread pool * @see Executors#newFixedThreadPool(int) */ - public static SpanAwareExecutorService newFixedThreadPool(int nThreads) { - return SpanAwareExecutorService.wrap(Executors.newFixedThreadPool(nThreads)); + public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { + return SpanPropagatingExecutorService.wrap(Executors.newFixedThreadPool(nThreads)); } /** @@ -46,8 +49,8 @@ public static SpanAwareExecutorService newFixedThreadPool(int nThreads) { * @return the newly created thread pool * @see Executors#newFixedThreadPool(int, ThreadFactory) */ - public static SpanAwareExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return SpanAwareExecutorService.wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); + public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return SpanPropagatingExecutorService.wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); } /** @@ -57,8 +60,8 @@ public static SpanAwareExecutorService newFixedThreadPool(int nThreads, ThreadFa * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor() */ - public static SpanAwareExecutorService newSingleThreadExecutor() { - return SpanAwareExecutorService.wrap(Executors.newSingleThreadExecutor()); + public static SpanPropagatingExecutorService newSingleThreadExecutor() { + return SpanPropagatingExecutorService.wrap(Executors.newSingleThreadExecutor()); } /** @@ -69,8 +72,8 @@ public static SpanAwareExecutorService newSingleThreadExecutor() { * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor(ThreadFactory) */ - public static SpanAwareExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return SpanAwareExecutorService.wrap(Executors.newSingleThreadExecutor(threadFactory)); + public static SpanPropagatingExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return SpanPropagatingExecutorService.wrap(Executors.newSingleThreadExecutor(threadFactory)); } /** @@ -80,8 +83,8 @@ public static SpanAwareExecutorService newSingleThreadExecutor(ThreadFactory thr * @return the newly created thread pool * @see Executors#newCachedThreadPool() */ - public static SpanAwareExecutorService newCachedThreadPool() { - return SpanAwareExecutorService.wrap(Executors.newCachedThreadPool()); + public static SpanPropagatingExecutorService newCachedThreadPool() { + return SpanPropagatingExecutorService.wrap(Executors.newCachedThreadPool()); } /** @@ -92,8 +95,8 @@ public static SpanAwareExecutorService newCachedThreadPool() { * @return the newly created thread pool * @see Executors#newCachedThreadPool(ThreadFactory) */ - public static SpanAwareExecutorService newCachedThreadPool(ThreadFactory threadFactory) { - return SpanAwareExecutorService.wrap(Executors.newCachedThreadPool(threadFactory)); + public static SpanPropagatingExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return SpanPropagatingExecutorService.wrap(Executors.newCachedThreadPool(threadFactory)); } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java index 2ce7da9..dfe3824 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java @@ -7,10 +7,10 @@ import java.util.Map; /** - * A self-releasing managed span. + * A {@link Span} that automatically {@link #release() releases} + * when {@link #finish() finished} or {@link #close() closed}. *

- * This managed span will release itself when it is {@link #finish() finished} or {@link #close() closed}.
- * All other span functionality is forwareded to the actual managed Span. + * All other methods are forwarded to the actual managed Span. */ final class AutoReleasingManagedSpan implements ManagedSpan, Span { @@ -35,38 +35,38 @@ public void release() { } /** - * Finishes the delegate and releases this currently active ManagedSpan. + * {@link Span#finish() Finishes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. */ @Override public void finish() { try { getSpan().finish(); } finally { - this.release(); + release(); } } /** - * Finishes the delegate and releases this currently active ManagedSpan. + * {@link Span#finish(long) Finishes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. */ @Override public void finish(long finishMicros) { try { getSpan().finish(finishMicros); } finally { - this.release(); + release(); } } /** - * Finishes the delegate and releases this currently active ManagedSpan. + * {@link Span#close() Closes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. */ @Override public void close() { try { getSpan().close(); } finally { - this.release(); + release(); } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index 28047f4..f850712 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -1,6 +1,5 @@ package io.opentracing.contrib.spanmanager.tracer; -import io.opentracing.NoopSpanBuilder; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; @@ -10,13 +9,13 @@ import java.util.Map; /** - * {@link SpanBuilder} that forwards all methods to a delegate.
- * The {@link #start()} method is overridden to automatically {@link ActiveSpanManager#manage(Span) manage} - * the started {@link Span}, wrapping it in an {@link AutoReleasingManagedSpan} object.
- * The {@link AutoReleasingManagedSpan} object {@link ManagedSpan#release() releases} the span automatically - * when it is {@link Span#finish() finished} or {@link Span#close() closed}. + * {@link SpanBuilder} that automatically {@link ActiveSpanManager#activate(Span) activates} newly started spans. + *

+ * The activated ManagedSpan is wrapped in an {@linkplain AutoReleasingManagedSpan} + * to automatically release when finished.
+ * All other methods are forwarded to the delegate span builder. * - * @see ActiveSpanManager#manage(Span) + * @see ActiveSpanManager * @see AutoReleasingManagedSpan#finish() */ final class ManagedSpanBuilder implements SpanBuilder { @@ -30,12 +29,9 @@ final class ManagedSpanBuilder implements SpanBuilder { /** * Replaces the {@link #delegate} SpanBuilder by a delegated-method result. - *

- * For null or {@link NoopSpanBuilder} the active span builder short-circuits to the noop SpanBuilder, - * similar to the AbstractSpanBuilder implementation. * * @param spanBuilder The builder returned from the delegate (normally '== delegate'). - * @return Either this re-wrapped ActiveSpanBuilder or the NoopSpanBuilder. + * @return This re-wrapped ActiveSpanBuilder. */ SpanBuilder rewrap(SpanBuilder spanBuilder) { if (spanBuilder != null) this.delegate = spanBuilder; @@ -43,17 +39,15 @@ SpanBuilder rewrap(SpanBuilder spanBuilder) { } /** - * Starts the built Span and {@link ActiveSpanManager#manage(Span) activates} it. + * Starts the built Span and {@link ActiveSpanManager#activate(Span) activates} it. * * @return a new 'currently active' Span that deactivates itself upon finish or close calls. - * @see ActiveSpanManager#manage(Span) + * @see ActiveSpanManager#activate(Span) * @see AutoReleasingManagedSpan#release() */ @Override public Span start() { - Span newSpan = delegate.start(); - // TODO: Do we want to make NoopSpan instances managed? - return new AutoReleasingManagedSpan(ActiveSpanManager.get().manage(newSpan)); + return new AutoReleasingManagedSpan(ActiveSpanManager.activate(delegate.start())); } // All other methods are forwarded to the delegate SpanBuilder. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index c050347..3d5b158 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -8,16 +8,17 @@ import io.opentracing.propagation.Format; /** - * Convenience {@link Tracer} that automates managing the {@linkplain ActiveSpanManager#currentSpan() active span}: + * Convenience {@link Tracer} that automates managing the {@linkplain ActiveSpanManager#activeSpan() active span}: *

    *
  1. It is a wrapper that forwards all calls to another {@link Tracer} implementation.
  2. - *
  3. {@link Span} instances created with this tracer are {@link ActiveSpanManager#manage(Span) managed} when started - * (automatically becoming the {@link ActiveSpanManager#currentSpan() currently active span}) - *
  4. - *
  5. and {@link ManagedSpan#release() released} when finished.
  6. + *
  7. {@linkplain Span Spans} created with this tracer are + * automatically {@link ActiveSpanManager#activate(Span) activated} when started,
  8. + *
  9. and automatically {@link ManagedSpan#release() released} when they finish.
  10. *
+ * + * @see ActiveSpanManager */ -public class ManagedSpanTracer implements Tracer { +public final class ManagedSpanTracer implements Tracer { protected final Tracer delegate; @@ -27,7 +28,7 @@ public ManagedSpanTracer(Tracer delegate) { } public void inject(SpanContext spanContext, Format format, C carrier) { - // Weird that Builder extends Context! + // Weird that SpanBuilder extends Context! if (spanContext instanceof ManagedSpanBuilder) spanContext = ((ManagedSpanBuilder) spanContext).delegate; delegate.inject(spanContext, format, carrier); } @@ -42,7 +43,7 @@ public SpanBuilder buildSpan(String operationName) { @Override public String toString() { - return getClass().getSimpleName() + "{delegate=" + delegate + '}'; + return "ManagedSpanTracer{" + delegate + '}'; } } From b3b64f20daf6c0f8bd8188e96f4c12d6b999aeaa Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 5 Jan 2017 12:56:56 +0100 Subject: [PATCH 14/51] Documentation and internal variables. --- pom.xml | 2 +- .../spanmanager/ActiveSpanManager.java | 67 ++++++++++--------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/pom.xml b/pom.xml index acab460..758e5ac 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ SpanManager library Library for managing the currently active span and propagating it accross threads https://github.com/opentracing-contrib/java-activespan - 2016 + 2017 diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java index ea195bf..5c2c00a 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java @@ -10,18 +10,18 @@ import java.util.logging.Logger; /** - * Static utility to access the currently active {@link Span}. + * Static utility to access the active {@link Span}. *

- * The {@link SpanManager} responsible for managing the currently active span is lazily resolved: + * The {@link SpanManager} responsible for managing the active span is lazily resolved: *

    - *
  1. The last-{@link #register(SpanManager) registered} span manager always takes precedence.
  2. - *
  3. If no manager was registered, one is looked up from the {@link ServiceLoader}.
    + *
  4. The last-{@link #register(SpanManager) registered} SpanManager always takes precedence.
  5. + *
  6. If no SpanManager was registered, one is looked up from the {@link ServiceLoader}.
    * The ActiveSpanManager will not attempt to choose between implementations:
  7. - *
  8. If no single implementation is found, a default implementation will be used.
  9. + *
  10. If no single implementation is found, the default SpanManager will be used.
  11. *
*

- * The {@linkplain ThreadLocalSpanManager default implementation} uses {@link ThreadLocal} storage, - * maintaining a stack-like structure of linked managed spans. + * The {@linkplain ThreadLocalSpanManager default SpanManager} uses {@link ThreadLocal} storage, + * maintaining a stack-like structure of linked {@link ManagedSpan managed spans}. * * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutors * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutorService @@ -32,77 +32,84 @@ public final class ActiveSpanManager { /** * The resolved {@link SpanManager} to delegate the implementation to. *

- * This can be an {@link #register(SpanManager) explicitly registered delegate} - * or an {@link #loadSingleSpiImplementation() automatically resolved SpanManager service}, + * This can be an {@link #register(SpanManager) explicitly registered} or + * the {@link #loadSingleSpiImplementation() automatically resolved} SpanManager * (or null during initialization). */ - private static final AtomicReference ACTIVE_SPAN_MANAGER = new AtomicReference(); + private static final AtomicReference ACTIVE_SPANMANAGER = new AtomicReference(); private ActiveSpanManager() { throw new UnsupportedOperationException(); } - private static SpanManager lazySpanManager() { - SpanManager spanManager = ACTIVE_SPAN_MANAGER.get(); - if (spanManager == null) { + /** + * Lazily resolves the active SpanManager. + *

+ * The active SpanManager can be {@link #register(SpanManager) re-configured} at any time. + * + * @return The active span manager (non-null). + */ + private static SpanManager activeSpanManager() { + SpanManager activeSpanManager = ACTIVE_SPANMANAGER.get(); + if (activeSpanManager == null) { final SpanManager resolved = loadSingleSpiImplementation(); - while (spanManager == null && resolved != null) { // handle rare race condition - ACTIVE_SPAN_MANAGER.compareAndSet(null, resolved); - spanManager = ACTIVE_SPAN_MANAGER.get(); + while (activeSpanManager == null && resolved != null) { // handle rare race condition + ACTIVE_SPANMANAGER.compareAndSet(null, resolved); + activeSpanManager = ACTIVE_SPANMANAGER.get(); } - LOGGER.log(Level.INFO, "Using SpanManager implementation: {0}.", spanManager); + LOGGER.log(Level.INFO, "Using ActiveSpanManager: {0}.", activeSpanManager); } - return spanManager; + return activeSpanManager; } /** - * Explicitly configures a SpanManager to back the behaviour of the {@link ActiveSpanManager}. + * Explicitly configures a SpanManager to back the behaviour of the {@link ActiveSpanManager} methods. *

* The previous manager is returned so it can be: *

    *
  • {@link SpanManager#clear() cleared} as it no longer is the active manager, or
  • - *
  • restored later, for example in a testing situation
  • + *
  • restored later, for example in a testing situation.
  • *
* * @param spanManager Manager for in-process span propagation. * @return The previously active SpanManager, or null if there was none. */ public static SpanManager register(SpanManager spanManager) { - SpanManager previous = ACTIVE_SPAN_MANAGER.getAndSet(spanManager); + SpanManager previous = ACTIVE_SPANMANAGER.getAndSet(spanManager); LOGGER.log(Level.INFO, "Registered ActiveSpanManager {0} (previously {1}).", new Object[]{spanManager, previous}); return previous; } /** - * Return the active {@link Span} in the current process. + * The {@link SpanManager#currentSpan()} from the active SpanManager. * - * @return The currently active Span, or the NoopSpan if there is no active span. + * @return The active Span, or the NoopSpan if there is no active span. * @see SpanManager#currentSpan() */ public static Span activeSpan() { - Span activeSpan = lazySpanManager().currentSpan(); + Span activeSpan = activeSpanManager().currentSpan(); return activeSpan != null ? activeSpan : NoopSpan.INSTANCE; } /** - * Makes span the active span for the current process. + * {@link SpanManager#manage(Span) Makes span the current span} of the active SpanManager. * * @param span The span to become the new active span. - * @return The managed object to release the new active span with. + * @return The ManagedSpan to release the new active span with. * @see SpanManager#manage(Span) * @see ManagedSpan#release() */ public static ManagedSpan activate(Span span) { - return lazySpanManager().manage(span); + return activeSpanManager().manage(span); } /** - * Unconditional cleanup of all active spans for the current process. + * Unconditional {@link SpanManager#clear() cleanup} of all active spans from the active SpanManager. * * @see SpanManager#clear() */ public static void clear() { - lazySpanManager().clear(); + activeSpanManager().clear(); } /** @@ -119,7 +126,7 @@ private static SpanManager loadSingleSpiImplementation() { if (!spiImplementations.hasNext()) { return foundImplementation; } - LOGGER.log(Level.WARNING, "More than one SpanManager service implementation found. " + + LOGGER.log(Level.WARNING, "More than one SpanManager service found. " + "Falling back to default ThreadLocal implementation."); } return new ThreadLocalSpanManager(); From 8bd180189536b0197147ccca83cf74eec692cbb0 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 5 Jan 2017 13:17:45 +0100 Subject: [PATCH 15/51] Documentation fixes. --- README.md | 11 ++++++----- .../contrib/spanmanager/ActiveSpanManager.java | 10 ++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c2f5c44..2c66460 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Static utility to access the _currently active span_. 2. This _active span_ can be set through the `activate(Span)` method, returning a `ManagedSpan` for later deactivation. 3. `clear()` provides unconditional cleanup of _all active spans_ for the current process. - 4. A custom implementation can be registered by calling `register(SpanManager)`. + 4. A custom SpanManager can be registered by calling `register(SpanManager)`. _see section 'Custom span managers'_ ## Concurrency @@ -32,9 +32,10 @@ Provides factory-methods similar to standard java `Executors`: ### ManagedSpanTracer This convenience `Tracer` automates managing the _active span_: - 1. It is a wrapper that forwards all calls to another `Tracer` implementation. - 2. Spans created with this tracer are automatically _activated_ when started, - 3. and automatically _released_ when they finish. + 1. It wraps another `Tracer`. + 2. `Spans` created with this tracer are: + - automatically _activated_ when started, and + - automatically _released_ when finished. ## Custom span managers @@ -47,7 +48,7 @@ The _active_ SpanManager is resolved as follows: 1. The last-registered span manager always takes precedence. 2. If no manager was registered, one is looked up from the `ServiceLoader`. The ActiveSpanManager will not attempt to choose between implementations: - 3. If no single implementation is found, a default implementation will be used. + 3. If no single implementation is found, the default SpanManager will be used. ## Examples diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java index 5c2c00a..5ea634e 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java @@ -45,9 +45,11 @@ private ActiveSpanManager() { /** * Lazily resolves the active SpanManager. *

- * The active SpanManager can be {@link #register(SpanManager) re-configured} at any time. + * This indirection guarantees a valid SpanManager is available before, during and after + * application initialization.
+ * It also means the active SpanManager can be {@link #register(SpanManager) re-configured} at any time. * - * @return The active span manager (non-null). + * @return The active SpanManager (non-null). */ private static SpanManager activeSpanManager() { SpanManager activeSpanManager = ACTIVE_SPANMANAGER.get(); @@ -65,13 +67,13 @@ private static SpanManager activeSpanManager() { /** * Explicitly configures a SpanManager to back the behaviour of the {@link ActiveSpanManager} methods. *

- * The previous manager is returned so it can be: + * The previous SpanManager is returned so it can be: *

    *
  • {@link SpanManager#clear() cleared} as it no longer is the active manager, or
  • *
  • restored later, for example in a testing situation.
  • *
* - * @param spanManager Manager for in-process span propagation. + * @param spanManager New SpanManager for active span propagation. * @return The previously active SpanManager, or null if there was none. */ public static SpanManager register(SpanManager spanManager) { From c1f1742c497cadc6cd0be5dacb32f95e19a01de0 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 5 Jan 2017 13:22:29 +0100 Subject: [PATCH 16/51] Readme fixes. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2c66460..1813631 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# java-activespan +# java-spanmanager This library provides a way to manage spans and propagate them to other threads. ## ActiveSpanManager -Static utility to access the _currently active span_. - 1. The `activeSpan()` method returns the _active span_ in the current process. +Static utility to manage the _active span_: + 1. `activeSpan()` returns the _active span_ in the current process. If there is no active span, a `NoopSpan` is returned instead. 2. This _active span_ can be set through the `activate(Span)` method, returning a `ManagedSpan` for later deactivation. From 141ab99a8f3eb0ee1e0f51d0487800001686d83e Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Sun, 8 Jan 2017 14:19:30 +0100 Subject: [PATCH 17/51] Cleaned up code and added three README examples. --- README.md | 125 +++++++++++++-- .../spanmanager/ActiveSpanManager.java | 137 ---------------- ...anManager.java => DefaultSpanManager.java} | 42 ++--- .../spanmanager/GlobalSpanManager.java | 151 ++++++++++++++++++ .../contrib/spanmanager/ManagedSpan.java | 40 ----- .../contrib/spanmanager/SpanManager.java | 36 +++++ .../concurrent/CallableWithActiveSpan.java | 40 ----- .../concurrent/CallableWithCurrentSpan.java | 41 +++++ .../concurrent/RunnableWithActiveSpan.java | 35 ---- .../concurrent/RunnableWithCurrentSpan.java | 36 +++++ .../SpanPropagatingExecutorService.java | 65 ++++---- .../concurrent/SpanPropagatingExecutors.java | 14 +- .../tracer/AutoReleasingManagedSpan.java | 14 +- .../tracer/ManagedSpanBuilder.java | 16 +- .../spanmanager/tracer/ManagedSpanTracer.java | 20 ++- 15 files changed, 465 insertions(+), 347 deletions(-) delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java rename src/main/java/io/opentracing/contrib/spanmanager/{ThreadLocalSpanManager.java => DefaultSpanManager.java} (62%) create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java diff --git a/README.md b/README.md index 1813631..287fded 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,63 @@ This library provides a way to manage spans and propagate them to other threads. -## ActiveSpanManager +## SpanManager -Static utility to manage the _active span_: - 1. `activeSpan()` returns the _active span_ in the current process. - If there is no active span, a `NoopSpan` is returned instead. - 2. This _active span_ can be set through the `activate(Span)` method, - returning a `ManagedSpan` for later deactivation. - 3. `clear()` provides unconditional cleanup of _all active spans_ for the current process. - 4. A custom SpanManager can be registered by calling `register(SpanManager)`. - _see section 'Custom span managers'_ +Interface that defines _current_ span management: + 1. `manage(span)` makes the given span the _current_ managed span. + Returns a `ManagedSpan` containing a `release()` method + to later 'unmanage' the span with. + 2. `currentSpan()` returns the _current_ managed span, + or the `NoopSpan` if no span is managed. + 3. `clear()` provides unconditional cleanup of _all managed spans_ for the current process. + +## GlobalSpanManager + +Provides the `GlobalSpanManager.get()` method that returns the singleton _global span manager_. + +When the SpanManager is needed it is lazily looked up using the following rules: + 1. The SpanManager from the last `register(spanManager)` call always takes precedence. + 2. If no SpanManager was registered, one is looked up from the `ServiceLoader`. + The GlobalSpanManager will not attempt to choose between implementations: + 3. If no single implementation is found, the `DefaultSpanManager` will be used. + +## DefaultSpanManager + +A _default_ SpanManager maintaining a `Stack`-like `ThreadLocal` storage of _linked managed spans_. + +Releasing a _linked managed span_ uses the following algorithm: + 1. If the released span is not the _managed_ span, the _current managed_ span is left alone. + 2. Otherwise, the first parent that is not yet released is set as the new managed span. + 3. If no managed parents remain, the managed span is cleared. + 4. Consecutive `release()` calls for already-released spans will be ignored. ## Concurrency ### SpanPropagatingExecutorService -This `ExecutorService` propagates the _active span_ from the caller into each call that is executed. -Please Note: The active span is merely propagated as-is. -It is explicitly **not** finished by the calls. +This `ExecutorService` _propagates the current span_ +from the caller into each call that is executed. +The current span is obtained from the `GlobalSpanManager`. + + +_Please Note:_ The active span is merely _propagated_ (as-is). +It is explicitly **not** finished when the calls end, +nor will new spans be automatically related to the propagated span. ### SpanPropagatingExecutors -Provides factory-methods similar to standard java `Executors`: +Contains factory-methods similar to standard java `Executors`: - `SpanPropagatingExecutors.newFixedThreadPool(int)` - `SpanPropagatingExecutors.newSingleThreadExecutor()` - `SpanPropagatingExecutors.newCachedThreadPool()` - Variants of the above with additional `ThreadFactory` argument. -### ManagedSpanTracer +## ManagedSpanTracer -This convenience `Tracer` automates managing the _active span_: +This convenience `Tracer` automates managing the _current span_: 1. It wraps another `Tracer`. 2. `Spans` created with this tracer are: - - automatically _activated_ when started, and + - automatically _managed_ when started, and - automatically _released_ when finished. ## Custom span managers @@ -53,5 +77,72 @@ The _active_ SpanManager is resolved as follows: ## Examples -_TODO create examples_ +### Manually propagating any Span into a background thread + +To propagate a `Span` into a new `Thread` can be accomplished as follows: + +```java + final Span someSpan = ... + Thread thread = new Thread() { + @Override + public void run() { + try (ManagedSpan parent = GlobalSpanManager.get().manage(someSpan)) { + // ...regular traced background process... + assert parent == GlobalSpanManager.get().currentSpan(); + try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(parent.context()).start()) { + // ...traced as child of the propagated span... + } + } + } + }; + +``` + +### Threadpool that propagates currentSpan() into threads + +```java + class TracedCall implements Callable { + @Override + public String call() { + Span parent = GlobalSpanManager.get().currentSpan(); + try (Span newSpan = tracer.buildSpan("someCall").asChildOf(parent.context).start()) { + return "New span: " + newSpan + ", parent: " + parent; + } + } + } + + class Caller { + static ExecutorService propagatingThreadpool = SpanPropagatingExecutorService.of(anyThreadpool()); + + void run() { + // ...code that sets the current Span somewhere: + try (ManagedSpan current = GlobalSpanManager.get().manage(someSpan)) { + + // scheduling the traced call: + Future result = propagatingThreadpool.submit(new TracedCall()); + + } + } + } + +``` + +### Threadpool with 'managed span' tracer + +```java + + class Caller { + static Tracer tracer = new ManagedSpanTracer(anyTracer()); + + void run() { + try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == currentSpan() + + // Scheduling the traced call: + Future result = propagatingThreadpool.submit(new TracedCall()); + + } // ((ManagedSpan) parent).release() // Performed by ManagedSpanTracer + } + } + +``` diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java deleted file mode 100644 index 5ea634e..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/ActiveSpanManager.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.opentracing.contrib.spanmanager; - -import io.opentracing.NoopSpan; -import io.opentracing.Span; - -import java.util.Iterator; -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Static utility to access the active {@link Span}. - *

- * The {@link SpanManager} responsible for managing the active span is lazily resolved: - *

    - *
  1. The last-{@link #register(SpanManager) registered} SpanManager always takes precedence.
  2. - *
  3. If no SpanManager was registered, one is looked up from the {@link ServiceLoader}.
    - * The ActiveSpanManager will not attempt to choose between implementations:
  4. - *
  5. If no single implementation is found, the default SpanManager will be used.
  6. - *
- *

- * The {@linkplain ThreadLocalSpanManager default SpanManager} uses {@link ThreadLocal} storage, - * maintaining a stack-like structure of linked {@link ManagedSpan managed spans}. - * - * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutors - * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutorService - */ -public final class ActiveSpanManager { - private static final Logger LOGGER = Logger.getLogger(ActiveSpanManager.class.getName()); - - /** - * The resolved {@link SpanManager} to delegate the implementation to. - *

- * This can be an {@link #register(SpanManager) explicitly registered} or - * the {@link #loadSingleSpiImplementation() automatically resolved} SpanManager - * (or null during initialization). - */ - private static final AtomicReference ACTIVE_SPANMANAGER = new AtomicReference(); - - private ActiveSpanManager() { - throw new UnsupportedOperationException(); - } - - /** - * Lazily resolves the active SpanManager. - *

- * This indirection guarantees a valid SpanManager is available before, during and after - * application initialization.
- * It also means the active SpanManager can be {@link #register(SpanManager) re-configured} at any time. - * - * @return The active SpanManager (non-null). - */ - private static SpanManager activeSpanManager() { - SpanManager activeSpanManager = ACTIVE_SPANMANAGER.get(); - if (activeSpanManager == null) { - final SpanManager resolved = loadSingleSpiImplementation(); - while (activeSpanManager == null && resolved != null) { // handle rare race condition - ACTIVE_SPANMANAGER.compareAndSet(null, resolved); - activeSpanManager = ACTIVE_SPANMANAGER.get(); - } - LOGGER.log(Level.INFO, "Using ActiveSpanManager: {0}.", activeSpanManager); - } - return activeSpanManager; - } - - /** - * Explicitly configures a SpanManager to back the behaviour of the {@link ActiveSpanManager} methods. - *

- * The previous SpanManager is returned so it can be: - *

    - *
  • {@link SpanManager#clear() cleared} as it no longer is the active manager, or
  • - *
  • restored later, for example in a testing situation.
  • - *
- * - * @param spanManager New SpanManager for active span propagation. - * @return The previously active SpanManager, or null if there was none. - */ - public static SpanManager register(SpanManager spanManager) { - SpanManager previous = ACTIVE_SPANMANAGER.getAndSet(spanManager); - LOGGER.log(Level.INFO, "Registered ActiveSpanManager {0} (previously {1}).", new Object[]{spanManager, previous}); - return previous; - } - - /** - * The {@link SpanManager#currentSpan()} from the active SpanManager. - * - * @return The active Span, or the NoopSpan if there is no active span. - * @see SpanManager#currentSpan() - */ - public static Span activeSpan() { - Span activeSpan = activeSpanManager().currentSpan(); - return activeSpan != null ? activeSpan : NoopSpan.INSTANCE; - } - - /** - * {@link SpanManager#manage(Span) Makes span the current span} of the active SpanManager. - * - * @param span The span to become the new active span. - * @return The ManagedSpan to release the new active span with. - * @see SpanManager#manage(Span) - * @see ManagedSpan#release() - */ - public static ManagedSpan activate(Span span) { - return activeSpanManager().manage(span); - } - - /** - * Unconditional {@link SpanManager#clear() cleanup} of all active spans from the active SpanManager. - * - * @see SpanManager#clear() - */ - public static void clear() { - activeSpanManager().clear(); - } - - /** - * Loads a single service implementation from {@link ServiceLoader}. - * - * @return The single service or a new ThreadLocalSpanManager. - */ - private static SpanManager loadSingleSpiImplementation() { - // Use the ServiceLoader to find the declared ActiveSpanManager implementation. - Iterator spiImplementations = - ServiceLoader.load(SpanManager.class, SpanManager.class.getClassLoader()).iterator(); - if (spiImplementations.hasNext()) { - SpanManager foundImplementation = spiImplementations.next(); - if (!spiImplementations.hasNext()) { - return foundImplementation; - } - LOGGER.log(Level.WARNING, "More than one SpanManager service found. " + - "Falling back to default ThreadLocal implementation."); - } - return new ThreadLocalSpanManager(); - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java similarity index 62% rename from src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java rename to src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index 871b683..b8c040d 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/ThreadLocalSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -13,36 +13,42 @@ *

* The linked managed spans provide the following stack unwinding algorithm: *

    - *
  1. If the released span is not the managed span, the managed span is left alone.
  2. + *
  3. If the released span is not the managed span, the current managed span is left alone.
  4. *
  5. Otherwise, the first parent that is not yet released is set as the new managed span.
  6. *
  7. If no managed parents remain, the managed span is cleared.
  8. *
  9. Consecutive release() calls for already-released spans will be ignored.
  10. *
*/ -final class ThreadLocalSpanManager implements SpanManager { +public final class DefaultSpanManager implements SpanManager { - private static final Logger LOGGER = Logger.getLogger(ThreadLocalSpanManager.class.getName()); - private static final ThreadLocal MANAGED = new ThreadLocal(); + private static final Logger LOGGER = Logger.getLogger(DefaultSpanManager.class.getName()); - ThreadLocalSpanManager() { + private static final DefaultSpanManager INSTANCE = new DefaultSpanManager(); + private final ThreadLocal managed = new ThreadLocal(); + + private DefaultSpanManager() { + } + + public static SpanManager getInstance() { + return INSTANCE; } @Override public Span currentSpan() { - ManagedSpan managedSpan = MANAGED.get(); + LinkedManagedSpan managedSpan = managed.get(); return managedSpan != null ? managedSpan.span : NoopSpan.INSTANCE; } @Override - public io.opentracing.contrib.spanmanager.ManagedSpan manage(Span span) { - ManagedSpan managedSpan = new ManagedSpan(span, MANAGED.get()); - MANAGED.set(managedSpan); + public SpanManager.ManagedSpan manage(Span span) { + LinkedManagedSpan managedSpan = new LinkedManagedSpan(span, managed.get()); + managed.set(managedSpan); return managedSpan; } @Override public void clear() { - MANAGED.remove(); + managed.remove(); } @Override @@ -50,12 +56,12 @@ public String toString() { return getClass().getSimpleName(); } - private static final class ManagedSpan implements io.opentracing.contrib.spanmanager.ManagedSpan { - private final ManagedSpan parent; + private final class LinkedManagedSpan implements SpanManager.ManagedSpan { + private final LinkedManagedSpan parent; private final Span span; private final AtomicBoolean released = new AtomicBoolean(false); - private ManagedSpan(Span span, ManagedSpan parent) { + private LinkedManagedSpan(Span span, LinkedManagedSpan parent) { this.parent = parent; this.span = span; } @@ -66,17 +72,17 @@ public Span getSpan() { } /** - * Please see {@link ThreadLocalSpanManager outer class description} for the stack-unwinding documentation. + * Please see {@link DefaultSpanManager outer class description} for the stack-unwinding documentation. */ public void release() { if (released.compareAndSet(false, true)) { - ManagedSpan current = MANAGED.get(); + LinkedManagedSpan current = managed.get(); if (this == current) { while (current != null && current.released.get()) { current = current.parent; } - if (current == null) MANAGED.remove(); - else MANAGED.set(current); + if (current == null) managed.remove(); + else managed.set(current); LOGGER.log(Level.FINER, "Deactivated {0} and restored managed span to {1}.", new Object[]{this, current}); } else { LOGGER.log(Level.FINE, "Deactivated {0} without affecting managed span {1}.", new Object[]{this, current}); @@ -93,7 +99,7 @@ public void close() { @Override public String toString() { - return "ManagedSpan{" + span + '}'; + return "LinkedManagedSpan{" + span + '}'; } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java new file mode 100644 index 0000000..de7b6b0 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java @@ -0,0 +1,151 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.Span; +import io.opentracing.Tracer; + +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Forwards all methods to another {@linkplain SpanManager} that can be configured in one of two ways: + *
    + *
  1. Explicitly, calling {@link #register(SpanManager)} with a configured span manager, or:
  2. + *
  3. Automatically using the Java {@link ServiceLoader} SPI mechanism to load a {@linkplain SpanManager} + * from the classpath.
  4. + *
+ *

+ * When the SpanManager is needed it is lazily looked up using the following rules: + *

    + *
  1. The last-{@link #register(SpanManager) registered} SpanManager always takes precedence.
  2. + *
  3. If no SpanManager was registered, one is looked up from the {@link ServiceLoader}.
    + * The {@linkplain GlobalSpanManager} will not attempt to choose between implementations:
  4. + *
  5. If no single SpanManager service is found, the {@link DefaultSpanManager} will be used.
  6. + *
+ * + * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutors + * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutorService + */ +public final class GlobalSpanManager implements SpanManager { + private static final Logger LOGGER = Logger.getLogger(GlobalSpanManager.class.getName()); + + /** + * Singleton instance. + *

+ * Since we cannot prevent people using {@linkplain #get() GlobalSpanManager.get()} as a constant, + * this guarantees that references obtained before, during or after initialization + * all behave as if obtained after initialization once properly initialized.
+ * As a minor additional benefit it makes it harder to circumvent the {@link Tracer} API. + */ + private static final GlobalSpanManager INSTANCE = new GlobalSpanManager(); + + /** + * The resolved {@link SpanManager} to delegate the implementation to. + *

+ * This can be an {@link #register(SpanManager) explicitly registered} or + * the {@link #loadSingleSpiImplementation() automatically resolved} SpanManager + * (or null during initialization). + */ + private final AtomicReference globalSpanManager = new AtomicReference(); + + private GlobalSpanManager() { + } + + /** + * Returns the constant {@linkplain GlobalSpanManager}. + *

+ * All methods are forwarded to the currently configured SpanManager.
+ * Until a tracer is {@link #register(SpanManager) explicitly configured}, + * one is looked up from the {@link ServiceLoader}, + * falling back to the {@link DefaultSpanManager}.
+ * A span manager can be re-configured at any time. + * For example, the span manager used to manage a new span + * may be different than the one backing the {@link SpanManager.ManagedSpan ManagedSpan} + * from the previous span. + * + * @return The global tracer constant. + * @see #register(SpanManager) + */ + public static SpanManager get() { + return INSTANCE; + } + + /** + * Lazily resolves the global SpanManager. + *

+ * This indirection guarantees a valid SpanManager is available before, during and after + * application initialization.
+ * It also means the global SpanManager can be {@link #register(SpanManager) re-configured} at any time. + * + * @return The global SpanManager (non-null). + */ + private static SpanManager lazySpanManager() { + SpanManager spanManager = INSTANCE.globalSpanManager.get(); + if (spanManager == null) { + final SpanManager resolved = loadSingleSpiImplementation(); + while (spanManager == null && resolved != null) { // handle rare race condition + INSTANCE.globalSpanManager.compareAndSet(null, resolved); + spanManager = INSTANCE.globalSpanManager.get(); + } + LOGGER.log(Level.INFO, "Using GlobalSpanManager: {0}.", spanManager); + } + return spanManager; + } + + /** + * Explicitly configures a SpanManager to back the behaviour of the {@link GlobalSpanManager} methods. + *

+ * The previous SpanManager is returned so it can be: + *

    + *
  • {@link SpanManager#clear() cleared} as it no longer is the global manager, or
  • + *
  • restored later, for example in a testing situation.
  • + *
+ * + * @param spanManager New SpanManager for span propagation. + * @return The previously global SpanManager, or null if there was none. + */ + public static SpanManager register(SpanManager spanManager) { + SpanManager previous = INSTANCE.globalSpanManager.getAndSet(spanManager); + LOGGER.log(Level.INFO, "Registered GlobalSpanManager {0} (previously {1}).", new Object[]{spanManager, previous}); + return previous; + } + + @Override + public Span currentSpan() { + return lazySpanManager().currentSpan(); + } + + @Override + public ManagedSpan manage(Span span) { + return lazySpanManager().manage(span); + } + + @Override + public void clear() { + lazySpanManager().clear(); + } + + /** + * Loads a single service implementation from {@link ServiceLoader}. + * + * @return The single service or the DefaultSpanManager. + * @see DefaultSpanManager + */ + private static SpanManager loadSingleSpiImplementation() { + // Use the ServiceLoader to find the declared SpanManager implementation. + Iterator spiImplementations = + ServiceLoader.load(SpanManager.class, SpanManager.class.getClassLoader()).iterator(); + if (spiImplementations.hasNext()) { + SpanManager foundImplementation = spiImplementations.next(); + if (!spiImplementations.hasNext()) { + return foundImplementation; + } + LOGGER.log(Level.WARNING, "More than one SpanManager service found. " + + "Falling back to default ThreadLocal implementation."); + } + return DefaultSpanManager.getInstance(); + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java deleted file mode 100644 index 4b170b3..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/ManagedSpan.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.opentracing.contrib.spanmanager; - -import io.opentracing.Span; - -import java.io.Closeable; - -/** - * To {@linkplain #release() release} a {@link SpanManager#manage(Span) managed span} with. - *

- * It must be possible to repeatedly call {@linkplain #release()} without side effects. - * - * @see SpanManager - */ -public interface ManagedSpan extends Closeable { - - /** - * The span that became the managed span at some point. - * - * @return The contained span to be released. - */ - Span getSpan(); - - /** - * Makes the {@link #getSpan() contained span} no longer the managed span. - *

- * Implementation notes: - *

    - *
  1. It is encouraged to restore the managed span as it was before this span became managed - * (providing stack-like behaviour).
  2. - *
  3. It must be possible to repeatedly call release without side effects.
  4. - *
- */ - void release(); - - /** - * Alias for {@link #release()} to allow easy use from try-with-resources. - */ - void close(); - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index a05b412..85857e5 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -2,6 +2,8 @@ import io.opentracing.Span; +import java.io.Closeable; + /** * Manager to {@link #manage(Span) manage} and {@link ManagedSpan#release() release} * {@linkplain Span spans} and accessing the {@link #currentSpan() currently-managed span}. @@ -37,4 +39,38 @@ public interface SpanManager { */ void clear(); + /** + * To {@linkplain #release() release} a {@link SpanManager#manage(Span) managed span} with. + *

+ * It must be possible to repeatedly call {@linkplain #release()} without side effects. + * + * @see SpanManager + */ + interface ManagedSpan extends Closeable { + + /** + * The span that became the managed span at some point. + * + * @return The contained span to be released. + */ + Span getSpan(); + + /** + * Makes the {@link #getSpan() contained span} no longer the managed span. + *

+ * Implementation notes: + *

    + *
  1. It is encouraged to restore the managed span as it was before this span became managed + * (providing stack-like behaviour).
  2. + *
  3. It must be possible to repeatedly call release without side effects.
  4. + *
+ */ + void release(); + + /** + * Alias for {@link #release()} to allow easy use from try-with-resources. + */ + void close(); + + } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java deleted file mode 100644 index 6b29b93..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithActiveSpan.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.opentracing.contrib.spanmanager.concurrent; - -import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; -import io.opentracing.contrib.spanmanager.ManagedSpan; - -import java.util.concurrent.Callable; - -/** - * {@link Callable} wrapper that will execute with a custom active span specified from the scheduling thread. - * - * @see ActiveSpanManager - */ -final class CallableWithActiveSpan implements Callable { - - private final Callable delegate; - private final Span activeSpanOfScheduler; - - CallableWithActiveSpan(Callable delegate, Span activeSpanOfScheduler) { - if (delegate == null) throw new NullPointerException("Callable delegate is ."); - this.delegate = delegate; - this.activeSpanOfScheduler = activeSpanOfScheduler; - } - - /** - * Performs the delegate call with the specified active span. - * - * @return The result from the original call. - * @throws Exception if the original call threw an exception. - */ - public T call() throws Exception { - final ManagedSpan managedSpan = ActiveSpanManager.activate(activeSpanOfScheduler); - try { - return delegate.call(); - } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) - managedSpan.release(); - } - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java new file mode 100644 index 0000000..128c97d --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java @@ -0,0 +1,41 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.GlobalSpanManager; +import io.opentracing.contrib.spanmanager.SpanManager; + +import java.util.concurrent.Callable; + +/** + * {@link Callable} wrapper that will execute with a {@link GlobalSpanManager#currentSpan() custom current span} + * specified from the scheduling thread. + * + * @see GlobalSpanManager + */ +final class CallableWithCurrentSpan implements Callable { + + private final Callable delegate; + private final Span customCurrentSpan; + + CallableWithCurrentSpan(Callable delegate, Span customCurrentSpan) { + if (delegate == null) throw new NullPointerException("Callable delegate is ."); + this.delegate = delegate; + this.customCurrentSpan = customCurrentSpan; + } + + /** + * Performs the delegate call with the specified custom current span. + * + * @return The result from the original call. + * @throws Exception if the original call threw an exception. + */ + public T call() throws Exception { + final SpanManager.ManagedSpan managedSpan = GlobalSpanManager.get().manage(customCurrentSpan); + try { + return delegate.call(); + } finally { + managedSpan.release(); + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java deleted file mode 100644 index 99c45e6..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithActiveSpan.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.opentracing.contrib.spanmanager.concurrent; - -import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; -import io.opentracing.contrib.spanmanager.ManagedSpan; - -/** - * {@link Runnable} wrapper that will execute with a custom active span specified from the scheduling thread. - * - * @see ActiveSpanManager - */ -final class RunnableWithActiveSpan implements Runnable { - - private final Runnable delegate; - private final Span activeSpanOfScheduler; - - RunnableWithActiveSpan(Runnable delegate, Span parentSpan) { - if (delegate == null) throw new NullPointerException("Runnable delegate is ."); - this.delegate = delegate; - this.activeSpanOfScheduler = parentSpan; - } - - /** - * Performs the runnable action with the specified parent span. - */ - public void run() { - ManagedSpan managedSpan = ActiveSpanManager.activate(activeSpanOfScheduler); - try { - delegate.run(); - } finally { // TODO: simulate try-with-resources (preferably using Guava's Closer) - managedSpan.release(); - } - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java new file mode 100644 index 0000000..522a082 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java @@ -0,0 +1,36 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.GlobalSpanManager; +import io.opentracing.contrib.spanmanager.SpanManager; + +/** + * {@link Runnable} wrapper that will execute with a {@link GlobalSpanManager#currentSpan() custom current span} + * specified from the scheduling thread. + * + * @see GlobalSpanManager + */ +final class RunnableWithCurrentSpan implements Runnable { + + private final Runnable delegate; + private final Span customCurrentSpan; + + RunnableWithCurrentSpan(Runnable delegate, Span customCurrentSpan) { + if (delegate == null) throw new NullPointerException("Runnable delegate is ."); + this.delegate = delegate; + this.customCurrentSpan = customCurrentSpan; + } + + /** + * Performs the runnable action with the specified custom current span. + */ + public void run() { + SpanManager.ManagedSpan managedSpan = GlobalSpanManager.get().manage(customCurrentSpan); + try { + delegate.run(); + } finally { + managedSpan.release(); + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index 41ad18f..554313f 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -1,7 +1,7 @@ package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; +import io.opentracing.contrib.spanmanager.GlobalSpanManager; import java.util.ArrayList; import java.util.Collection; @@ -9,9 +9,12 @@ import java.util.concurrent.*; /** - * Propagates the {@link ActiveSpanManager#activeSpan() active span} from the caller into each call that is executed. + * Propagates the {@link GlobalSpanManager#currentSpan() currently active span} from the caller + * into each call that is executed. *

- * Note: The active span is merely propagated. It is explicitly not finished by the calls. + * Note: The active span is merely propagated. + * It is explicitly not finished when the calls end, + * nor will new spans be automatically related to the propagated span. */ public class SpanPropagatingExecutorService implements ExecutorService { private final ExecutorService delegate; @@ -22,88 +25,88 @@ private SpanPropagatingExecutorService(ExecutorService delegate) { } /** - * Wraps the delegate ExecutorService to propagate the {@link ActiveSpanManager#activeSpan() active span} + * Wraps the delegate ExecutorService to propagate the {@link GlobalSpanManager#currentSpan() currently active span} * of callers into the executed calls. * * @param delegate The executorservice to forward calls to. * @return An ExecutorService that propagates active spans from callers into executed calls. */ - public static SpanPropagatingExecutorService wrap(final ExecutorService delegate) { + public static SpanPropagatingExecutorService of(final ExecutorService delegate) { if (delegate instanceof SpanPropagatingExecutorService) return (SpanPropagatingExecutorService) delegate; return new SpanPropagatingExecutorService(delegate); } /** - * Propagates the {@link ActiveSpanManager#activeSpan() currently active span} into the runnable + * Propagates the {@link GlobalSpanManager#currentSpan() custom current span} into the runnable * and performs cleanup afterwards. *

- * Note: The activeSpan is merely propagated. + * Note: The customCurrentSpan is merely propagated. * The specified span is explicitly not finished by the runnable. * - * @param runnable The runnable to be executed. - * @param activeSpan The span to be propagated. - * @return The wrapped runnable to execute with the active span. + * @param runnable The runnable to be executed. + * @param customCurrentSpan The span to be propagated. + * @return The wrapped runnable to execute with the custom span as current span. */ - public static Runnable runnableWithActiveSpan(Runnable runnable, Span activeSpan) { - return new RunnableWithActiveSpan(runnable, activeSpan); + public static Runnable runnableWithCurrentSpan(Runnable runnable, Span customCurrentSpan) { + return new RunnableWithCurrentSpan(runnable, customCurrentSpan); } /** - * Propagates the {@link ActiveSpanManager#activeSpan() currently active span} into the callable + * Propagates the {@link GlobalSpanManager#currentSpan() custom current span} into the callable * and performs cleanup afterwards. *

- * Note: The activeSpan is merely propagated. + * Note: The customCurrentSpan is merely propagated. * The specified span is explicitly not finished by the callable. * - * @param The callable result type. - * @param callable The callable to be executed. - * @param activeSpan The span to be propagated. - * @return The wrapped callable to execute with the active span. + * @param The callable result type. + * @param callable The callable to be executed. + * @param customCurrentSpan The span to be propagated. + * @return The wrapped callable to execute with the custom span as current span. */ - public static Callable callableWithActiveSpan(Callable callable, Span activeSpan) { - return new CallableWithActiveSpan(callable, activeSpan); + public static Callable callableWithCurrentSpan(Callable callable, Span customCurrentSpan) { + return new CallableWithCurrentSpan(callable, customCurrentSpan); } @Override public void execute(Runnable command) { - delegate.execute(runnableWithActiveSpan(command, ActiveSpanManager.activeSpan())); + delegate.execute(runnableWithCurrentSpan(command, GlobalSpanManager.get().currentSpan())); } @Override public Future submit(Runnable task) { - return delegate.submit(runnableWithActiveSpan(task, ActiveSpanManager.activeSpan())); + return delegate.submit(runnableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan())); } @Override public Future submit(Runnable task, T result) { - return delegate.submit(runnableWithActiveSpan(task, ActiveSpanManager.activeSpan()), result); + return delegate.submit(runnableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan()), result); } @Override public Future submit(Callable task) { - return delegate.submit(callableWithActiveSpan(task, ActiveSpanManager.activeSpan())); + return delegate.submit(callableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan())); } @Override public List> invokeAll(Collection> tasks) throws InterruptedException { - return delegate.invokeAll(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan())); + return delegate.invokeAll(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan())); } @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { - return delegate.invokeAll(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan()), timeout, unit); + return delegate.invokeAll(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan()), timeout, unit); } @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan())); + return delegate.invokeAny(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan())); } @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(tasksWithActiveSpan(tasks, ActiveSpanManager.activeSpan()), timeout, unit); + return delegate.invokeAny(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan()), timeout, unit); } @Override @@ -131,11 +134,11 @@ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedE return delegate.awaitTermination(timeout, unit); } - private static Collection> tasksWithActiveSpan( - Collection> tasks, Span activeSpan) { + private static Collection> tasksWithCurrentSpan( + Collection> tasks, Span customCurrentSpan) { if (tasks == null) throw new NullPointerException("Collection of tasks is ."); Collection> result = new ArrayList>(tasks.size()); - for (Callable task : tasks) result.add(callableWithActiveSpan(task, activeSpan)); + for (Callable task : tasks) result.add(callableWithCurrentSpan(task, customCurrentSpan)); return result; } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index a5ce298..d272a99 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -17,7 +17,7 @@ * * * - * @see SpanPropagatingExecutorService#wrap(ExecutorService) + * @see SpanPropagatingExecutorService#of(ExecutorService) */ public final class SpanPropagatingExecutors { @@ -37,7 +37,7 @@ private SpanPropagatingExecutors() { * @see Executors#newFixedThreadPool(int) */ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { - return SpanPropagatingExecutorService.wrap(Executors.newFixedThreadPool(nThreads)); + return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads)); } /** @@ -50,7 +50,7 @@ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { * @see Executors#newFixedThreadPool(int, ThreadFactory) */ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.wrap(Executors.newFixedThreadPool(nThreads, threadFactory)); + return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads, threadFactory)); } /** @@ -61,7 +61,7 @@ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, Th * @see Executors#newSingleThreadExecutor() */ public static SpanPropagatingExecutorService newSingleThreadExecutor() { - return SpanPropagatingExecutorService.wrap(Executors.newSingleThreadExecutor()); + return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor()); } /** @@ -73,7 +73,7 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor() { * @see Executors#newSingleThreadExecutor(ThreadFactory) */ public static SpanPropagatingExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.wrap(Executors.newSingleThreadExecutor(threadFactory)); + return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor(threadFactory)); } /** @@ -84,7 +84,7 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor(ThreadFacto * @see Executors#newCachedThreadPool() */ public static SpanPropagatingExecutorService newCachedThreadPool() { - return SpanPropagatingExecutorService.wrap(Executors.newCachedThreadPool()); + return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool()); } /** @@ -96,7 +96,7 @@ public static SpanPropagatingExecutorService newCachedThreadPool() { * @see Executors#newCachedThreadPool(ThreadFactory) */ public static SpanPropagatingExecutorService newCachedThreadPool(ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.wrap(Executors.newCachedThreadPool(threadFactory)); + return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool(threadFactory)); } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java index dfe3824..e2210f1 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java @@ -2,7 +2,7 @@ import io.opentracing.Span; import io.opentracing.SpanContext; -import io.opentracing.contrib.spanmanager.ManagedSpan; +import io.opentracing.contrib.spanmanager.SpanManager; import java.util.Map; @@ -12,11 +12,11 @@ *

* All other methods are forwarded to the actual managed Span. */ -final class AutoReleasingManagedSpan implements ManagedSpan, Span { +final class AutoReleasingManagedSpan implements Span, SpanManager.ManagedSpan { - private final ManagedSpan managedSpan; + private final SpanManager.ManagedSpan managedSpan; - AutoReleasingManagedSpan(ManagedSpan managedSpan) { + AutoReleasingManagedSpan(SpanManager.ManagedSpan managedSpan) { if (managedSpan == null) throw new NullPointerException("Managed span was ."); this.managedSpan = managedSpan; } @@ -35,7 +35,7 @@ public void release() { } /** - * {@link Span#finish() Finishes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. + * {@link Span#finish() Finishes} the delegate and {@link SpanManager.ManagedSpan#release() releases} this ManagedSpan. */ @Override public void finish() { @@ -47,7 +47,7 @@ public void finish() { } /** - * {@link Span#finish(long) Finishes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. + * {@link Span#finish(long) Finishes} the delegate and {@link SpanManager.ManagedSpan#release() releases} this ManagedSpan. */ @Override public void finish(long finishMicros) { @@ -59,7 +59,7 @@ public void finish(long finishMicros) { } /** - * {@link Span#close() Closes} the delegate and {@link ManagedSpan#release() releases} this ManagedSpan. + * {@link Span#close() Closes} the delegate and {@link SpanManager.ManagedSpan#release() releases} this ManagedSpan. */ @Override public void close() { diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index f850712..c4b6d89 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -3,19 +3,19 @@ import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; -import io.opentracing.contrib.spanmanager.ManagedSpan; +import io.opentracing.contrib.spanmanager.GlobalSpanManager; +import io.opentracing.contrib.spanmanager.SpanManager; import java.util.Map; /** - * {@link SpanBuilder} that automatically {@link ActiveSpanManager#activate(Span) activates} newly started spans. + * {@link SpanBuilder} that automatically {@link GlobalSpanManager#manage(Span) activates} newly started spans. *

* The activated ManagedSpan is wrapped in an {@linkplain AutoReleasingManagedSpan} * to automatically release when finished.
* All other methods are forwarded to the delegate span builder. * - * @see ActiveSpanManager + * @see GlobalSpanManager * @see AutoReleasingManagedSpan#finish() */ final class ManagedSpanBuilder implements SpanBuilder { @@ -39,15 +39,15 @@ SpanBuilder rewrap(SpanBuilder spanBuilder) { } /** - * Starts the built Span and {@link ActiveSpanManager#activate(Span) activates} it. + * Starts the built Span and {@link GlobalSpanManager#manage(Span) activates} it. * * @return a new 'currently active' Span that deactivates itself upon finish or close calls. - * @see ActiveSpanManager#activate(Span) + * @see GlobalSpanManager#manage(Span) * @see AutoReleasingManagedSpan#release() */ @Override public Span start() { - return new AutoReleasingManagedSpan(ActiveSpanManager.activate(delegate.start())); + return new AutoReleasingManagedSpan(GlobalSpanManager.get().manage(delegate.start())); } // All other methods are forwarded to the delegate SpanBuilder. @@ -58,7 +58,7 @@ public SpanBuilder asChildOf(SpanContext parent) { } public SpanBuilder asChildOf(Span parent) { - if (parent instanceof ManagedSpan) parent = ((ManagedSpan) parent).getSpan(); + if (parent instanceof SpanManager.ManagedSpan) parent = ((SpanManager.ManagedSpan) parent).getSpan(); return rewrap(delegate.asChildOf(parent)); } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index 3d5b158..79a8022 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -3,24 +3,30 @@ import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; -import io.opentracing.contrib.spanmanager.ActiveSpanManager; -import io.opentracing.contrib.spanmanager.ManagedSpan; +import io.opentracing.contrib.spanmanager.GlobalSpanManager; +import io.opentracing.contrib.spanmanager.SpanManager; import io.opentracing.propagation.Format; /** - * Convenience {@link Tracer} that automates managing the {@linkplain ActiveSpanManager#activeSpan() active span}: + * Convenience {@link Tracer} that automates managing the {@linkplain GlobalSpanManager#currentSpan() currently active span}: *

    *
  1. It is a wrapper that forwards all calls to another {@link Tracer} implementation.
  2. *
  3. {@linkplain Span Spans} created with this tracer are - * automatically {@link ActiveSpanManager#activate(Span) activated} when started,
  4. - *
  5. and automatically {@link ManagedSpan#release() released} when they finish.
  6. + * automatically {@link GlobalSpanManager#manage(Span) managed} when started, + *
  7. and automatically {@link SpanManager.ManagedSpan#release() released} when they finish.
  8. *
+ *

+ * Implementation note: This {@link Tracer} wraps the {@linkplain SpanBuilder} and {@linkplain Span} + * in {@linkplain ManagedSpanBuilder} and {@linkplain AutoReleasingManagedSpan} respectively + * because no {@link Span} lifecycle callbacks are available in the opentracing API.
+ * If there were, the {@linkplain ManagedSpanTracer} could be a lot simpler.
+ * However, lifecycle callbacks in the API form a considerable performance risk. * - * @see ActiveSpanManager + * @see GlobalSpanManager */ public final class ManagedSpanTracer implements Tracer { - protected final Tracer delegate; + private final Tracer delegate; public ManagedSpanTracer(Tracer delegate) { if (delegate == null) throw new NullPointerException("The delegate Tracer implementation is ."); From 0f53f24e0d3b7087a16775592c4707961c10aa45 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Sun, 8 Jan 2017 14:21:04 +0100 Subject: [PATCH 18/51] Version -> 0.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 758e5ac..32be982 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.opentracing.contrib.spanmanager opentracing-spanmanager - 0.1.0-SNAPSHOT + 0.0.1-SNAPSHOT jar From c9c047fa72d99ae403c31ca527a16d980002217e Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Sun, 8 Jan 2017 14:45:50 +0100 Subject: [PATCH 19/51] Allow explicit SpanManager for SpanPropagatingExecutorService. --- ...Span.java => CallableWithManagedSpan.java} | 20 ++++--- .../concurrent/RunnableWithCurrentSpan.java | 36 ------------ .../concurrent/RunnableWithManagedSpan.java | 38 +++++++++++++ .../SpanPropagatingExecutorService.java | 55 +++++++++++++------ .../concurrent/SpanPropagatingExecutors.java | 36 +++++++----- 5 files changed, 109 insertions(+), 76 deletions(-) rename src/main/java/io/opentracing/contrib/spanmanager/concurrent/{CallableWithCurrentSpan.java => CallableWithManagedSpan.java} (50%) delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java create mode 100644 src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java similarity index 50% rename from src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java rename to src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java index 128c97d..e935026 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithCurrentSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java @@ -1,36 +1,38 @@ package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.GlobalSpanManager; import io.opentracing.contrib.spanmanager.SpanManager; import java.util.concurrent.Callable; /** - * {@link Callable} wrapper that will execute with a {@link GlobalSpanManager#currentSpan() custom current span} + * {@link Callable} wrapper that will execute with a {@link SpanManager#manage(Span) managed span} * specified from the scheduling thread. * - * @see GlobalSpanManager + * @see SpanManager */ -final class CallableWithCurrentSpan implements Callable { +final class CallableWithManagedSpan implements Callable { private final Callable delegate; - private final Span customCurrentSpan; + private final SpanManager spanManager; + private final Span spanToManage; - CallableWithCurrentSpan(Callable delegate, Span customCurrentSpan) { + CallableWithManagedSpan(Callable delegate, SpanManager spanManager, Span spanToManage) { if (delegate == null) throw new NullPointerException("Callable delegate is ."); + if (spanManager == null) throw new NullPointerException("Span manager is ."); this.delegate = delegate; - this.customCurrentSpan = customCurrentSpan; + this.spanManager = spanManager; + this.spanToManage = spanToManage; } /** - * Performs the delegate call with the specified custom current span. + * Performs the delegate call with the specified managed span. * * @return The result from the original call. * @throws Exception if the original call threw an exception. */ public T call() throws Exception { - final SpanManager.ManagedSpan managedSpan = GlobalSpanManager.get().manage(customCurrentSpan); + final SpanManager.ManagedSpan managedSpan = spanManager.manage(spanToManage); try { return delegate.call(); } finally { diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java deleted file mode 100644 index 522a082..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithCurrentSpan.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.opentracing.contrib.spanmanager.concurrent; - -import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.GlobalSpanManager; -import io.opentracing.contrib.spanmanager.SpanManager; - -/** - * {@link Runnable} wrapper that will execute with a {@link GlobalSpanManager#currentSpan() custom current span} - * specified from the scheduling thread. - * - * @see GlobalSpanManager - */ -final class RunnableWithCurrentSpan implements Runnable { - - private final Runnable delegate; - private final Span customCurrentSpan; - - RunnableWithCurrentSpan(Runnable delegate, Span customCurrentSpan) { - if (delegate == null) throw new NullPointerException("Runnable delegate is ."); - this.delegate = delegate; - this.customCurrentSpan = customCurrentSpan; - } - - /** - * Performs the runnable action with the specified custom current span. - */ - public void run() { - SpanManager.ManagedSpan managedSpan = GlobalSpanManager.get().manage(customCurrentSpan); - try { - delegate.run(); - } finally { - managedSpan.release(); - } - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java new file mode 100644 index 0000000..d203a46 --- /dev/null +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java @@ -0,0 +1,38 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.SpanManager; + +/** + * {@link Runnable} wrapper that will execute with a {@link SpanManager#manage(Span) managed span} + * specified from the scheduling thread. + * + * @see SpanManager + */ +final class RunnableWithManagedSpan implements Runnable { + + private final Runnable delegate; + private final SpanManager spanManager; + private final Span spanToManage; + + RunnableWithManagedSpan(Runnable delegate, SpanManager spanManager, Span spanToManage) { + if (delegate == null) throw new NullPointerException("Runnable delegate is ."); + if (spanManager == null) throw new NullPointerException("Span manager is ."); + this.delegate = delegate; + this.spanManager = spanManager; + this.spanToManage = spanToManage; + } + + /** + * Performs the runnable action with the specified managed span. + */ + public void run() { + SpanManager.ManagedSpan managedSpan = spanManager.manage(spanToManage); + try { + delegate.run(); + } finally { + managedSpan.release(); + } + } + +} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index 554313f..6f88788 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -2,6 +2,7 @@ import io.opentracing.Span; import io.opentracing.contrib.spanmanager.GlobalSpanManager; +import io.opentracing.contrib.spanmanager.SpanManager; import java.util.ArrayList; import java.util.Collection; @@ -9,7 +10,7 @@ import java.util.concurrent.*; /** - * Propagates the {@link GlobalSpanManager#currentSpan() currently active span} from the caller + * Propagates the {@link SpanManager#currentSpan() currently active span} from the caller * into each call that is executed. *

* Note: The active span is merely propagated. @@ -18,22 +19,39 @@ */ public class SpanPropagatingExecutorService implements ExecutorService { private final ExecutorService delegate; + private final SpanManager spanManager; - private SpanPropagatingExecutorService(ExecutorService delegate) { + private SpanPropagatingExecutorService(ExecutorService delegate, SpanManager spanManager) { if (delegate == null) throw new NullPointerException("Delegate executor service is ."); + if (spanManager == null) throw new NullPointerException("SpanManager is ."); this.delegate = delegate; + this.spanManager = spanManager; } /** * Wraps the delegate ExecutorService to propagate the {@link GlobalSpanManager#currentSpan() currently active span} - * of callers into the executed calls. + * of callers into the executed calls, using the {@link GlobalSpanManager}. * * @param delegate The executorservice to forward calls to. * @return An ExecutorService that propagates active spans from callers into executed calls. + * @see #of(ExecutorService, SpanManager) + * @see GlobalSpanManager */ public static SpanPropagatingExecutorService of(final ExecutorService delegate) { - if (delegate instanceof SpanPropagatingExecutorService) return (SpanPropagatingExecutorService) delegate; - return new SpanPropagatingExecutorService(delegate); + return of(delegate, GlobalSpanManager.get()); + } + + /** + * Wraps the delegate ExecutorService to propagate the {@link SpanManager#currentSpan() currently active span} + * of callers into the executed calls, using the specified {@link SpanManager}. + * + * @param delegate The executorservice to forward calls to. + * @param spanManager The span manager to use. + * @return An ExecutorService that propagates active spans from callers into executed calls. + * @see #of(ExecutorService) + */ + public static SpanPropagatingExecutorService of(ExecutorService delegate, SpanManager spanManager) { + return new SpanPropagatingExecutorService(delegate, spanManager); } /** @@ -46,9 +64,10 @@ public static SpanPropagatingExecutorService of(final ExecutorService delegate) * @param runnable The runnable to be executed. * @param customCurrentSpan The span to be propagated. * @return The wrapped runnable to execute with the custom span as current span. + * @see GlobalSpanManager */ - public static Runnable runnableWithCurrentSpan(Runnable runnable, Span customCurrentSpan) { - return new RunnableWithCurrentSpan(runnable, customCurrentSpan); + private Runnable runnableWithCurrentSpan(Runnable runnable, Span customCurrentSpan) { + return new RunnableWithManagedSpan(runnable, spanManager, customCurrentSpan); } /** @@ -63,50 +82,50 @@ public static Runnable runnableWithCurrentSpan(Runnable runnable, Span customCur * @param customCurrentSpan The span to be propagated. * @return The wrapped callable to execute with the custom span as current span. */ - public static Callable callableWithCurrentSpan(Callable callable, Span customCurrentSpan) { - return new CallableWithCurrentSpan(callable, customCurrentSpan); + private Callable callableWithCurrentSpan(Callable callable, Span customCurrentSpan) { + return new CallableWithManagedSpan(callable, spanManager, customCurrentSpan); } @Override public void execute(Runnable command) { - delegate.execute(runnableWithCurrentSpan(command, GlobalSpanManager.get().currentSpan())); + delegate.execute(runnableWithCurrentSpan(command, spanManager.currentSpan())); } @Override public Future submit(Runnable task) { - return delegate.submit(runnableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan())); + return delegate.submit(runnableWithCurrentSpan(task, spanManager.currentSpan())); } @Override public Future submit(Runnable task, T result) { - return delegate.submit(runnableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan()), result); + return delegate.submit(runnableWithCurrentSpan(task, spanManager.currentSpan()), result); } @Override public Future submit(Callable task) { - return delegate.submit(callableWithCurrentSpan(task, GlobalSpanManager.get().currentSpan())); + return delegate.submit(callableWithCurrentSpan(task, spanManager.currentSpan())); } @Override public List> invokeAll(Collection> tasks) throws InterruptedException { - return delegate.invokeAll(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan())); + return delegate.invokeAll(tasksWithCurrentSpan(tasks, spanManager.currentSpan())); } @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { - return delegate.invokeAll(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan()), timeout, unit); + return delegate.invokeAll(tasksWithCurrentSpan(tasks, spanManager.currentSpan()), timeout, unit); } @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan())); + return delegate.invokeAny(tasksWithCurrentSpan(tasks, spanManager.currentSpan())); } @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(tasksWithCurrentSpan(tasks, GlobalSpanManager.get().currentSpan()), timeout, unit); + return delegate.invokeAny(tasksWithCurrentSpan(tasks, spanManager.currentSpan()), timeout, unit); } @Override @@ -134,7 +153,7 @@ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedE return delegate.awaitTermination(timeout, unit); } - private static Collection> tasksWithCurrentSpan( + private Collection> tasksWithCurrentSpan( Collection> tasks, Span customCurrentSpan) { if (tasks == null) throw new NullPointerException("Collection of tasks is ."); Collection> result = new ArrayList>(tasks.size()); diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index d272a99..f412c37 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -1,5 +1,7 @@ package io.opentracing.contrib.spanmanager.concurrent; +import io.opentracing.contrib.spanmanager.SpanManager; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -10,10 +12,10 @@ *

  • {@link #newFixedThreadPool(int)}
  • *
  • {@link #newSingleThreadExecutor()}
  • *
  • {@link #newCachedThreadPool()}
  • - *
  • Variants of the above with additional {@link ThreadFactory} argument: - * {@link #newFixedThreadPool(int, ThreadFactory)}, - * {@link #newSingleThreadExecutor(ThreadFactory)}, - * {@link #newCachedThreadPool(ThreadFactory)} + *
  • Variants of the above with additional {@link ThreadFactory} and {@link SpanManager} arguments: + * {@link #newFixedThreadPool(int, ThreadFactory, SpanManager)}, + * {@link #newSingleThreadExecutor(ThreadFactory, SpanManager)}, + * {@link #newCachedThreadPool(ThreadFactory, SpanManager)} *
  • * * @@ -30,11 +32,12 @@ private SpanPropagatingExecutors() { /** * This method returns a {@link Executors#newFixedThreadPool(int) fixed threadpool} that propagates - * the active span into the started threads. + * the active span into the started threads, using the global span manager. * * @param nThreads the number of threads in the pool * @return the newly created thread pool * @see Executors#newFixedThreadPool(int) + * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads)); @@ -46,19 +49,22 @@ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads + * @param spanManager the SpanManager to manage spans with. * @return the newly created thread pool * @see Executors#newFixedThreadPool(int, ThreadFactory) */ - public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads, threadFactory)); + public static SpanPropagatingExecutorService newFixedThreadPool( + int nThreads, ThreadFactory threadFactory, SpanManager spanManager) { + return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads, threadFactory), spanManager); } /** * This method returns a {@link Executors#newSingleThreadExecutor() single-threaded executor} that propagates - * the active span into the started thread. + * the active span into the started thread using the global span manager. * * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor() + * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ public static SpanPropagatingExecutorService newSingleThreadExecutor() { return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor()); @@ -69,19 +75,22 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor() { * that propagates the active span into the started thread. * * @param threadFactory the factory to use when creating new threads + * @param spanManager the SpanManager to manage spans with. * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor(ThreadFactory) */ - public static SpanPropagatingExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor(threadFactory)); + public static SpanPropagatingExecutorService newSingleThreadExecutor( + ThreadFactory threadFactory, SpanManager spanManager) { + return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor(threadFactory), spanManager); } /** * This method returns a {@link Executors#newCachedThreadPool() cached threadpool} that propagates - * the active span into the started threads. + * the active span into the started threads using the global span manager. * * @return the newly created thread pool * @see Executors#newCachedThreadPool() + * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ public static SpanPropagatingExecutorService newCachedThreadPool() { return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool()); @@ -92,11 +101,12 @@ public static SpanPropagatingExecutorService newCachedThreadPool() { * the active span into the started threads. * * @param threadFactory the factory to use when creating new threads + * @param spanManager the SpanManager to manage spans with. * @return the newly created thread pool * @see Executors#newCachedThreadPool(ThreadFactory) */ - public static SpanPropagatingExecutorService newCachedThreadPool(ThreadFactory threadFactory) { - return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool(threadFactory)); + public static SpanPropagatingExecutorService newCachedThreadPool(ThreadFactory threadFactory, SpanManager spanManager) { + return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool(threadFactory), spanManager); } } From 38fb708b8a9a5f8e8f9683c684e91ef94ecb03ea Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Sun, 8 Jan 2017 14:53:42 +0100 Subject: [PATCH 20/51] Allow explicit SpanManager for ManagedSpanTracer. --- .../tracer/ManagedSpanBuilder.java | 16 +++++----- .../spanmanager/tracer/ManagedSpanTracer.java | 31 ++++++++++++++++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index c4b6d89..05d9fd2 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -3,28 +3,30 @@ import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; -import io.opentracing.contrib.spanmanager.GlobalSpanManager; import io.opentracing.contrib.spanmanager.SpanManager; import java.util.Map; /** - * {@link SpanBuilder} that automatically {@link GlobalSpanManager#manage(Span) activates} newly started spans. + * {@link SpanBuilder} that automatically {@link SpanManager#manage(Span) activates} newly started spans. *

    * The activated ManagedSpan is wrapped in an {@linkplain AutoReleasingManagedSpan} * to automatically release when finished.
    * All other methods are forwarded to the delegate span builder. * - * @see GlobalSpanManager + * @see SpanManager * @see AutoReleasingManagedSpan#finish() */ final class ManagedSpanBuilder implements SpanBuilder { protected SpanBuilder delegate; + private final SpanManager spanManager; - ManagedSpanBuilder(SpanBuilder delegate) { + ManagedSpanBuilder(SpanBuilder delegate, SpanManager spanManager) { if (delegate == null) throw new NullPointerException("Delegate SpanBuilder was ."); + if (spanManager == null) throw new NullPointerException("Span manager was ."); this.delegate = delegate; + this.spanManager = spanManager; } /** @@ -39,15 +41,15 @@ SpanBuilder rewrap(SpanBuilder spanBuilder) { } /** - * Starts the built Span and {@link GlobalSpanManager#manage(Span) activates} it. + * Starts the built Span and {@link SpanManager#manage(Span) activates} it. * * @return a new 'currently active' Span that deactivates itself upon finish or close calls. - * @see GlobalSpanManager#manage(Span) + * @see SpanManager#manage(Span) * @see AutoReleasingManagedSpan#release() */ @Override public Span start() { - return new AutoReleasingManagedSpan(GlobalSpanManager.get().manage(delegate.start())); + return new AutoReleasingManagedSpan(spanManager.manage(delegate.start())); } // All other methods are forwarded to the delegate SpanBuilder. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index 79a8022..d48dae2 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -8,11 +8,11 @@ import io.opentracing.propagation.Format; /** - * Convenience {@link Tracer} that automates managing the {@linkplain GlobalSpanManager#currentSpan() currently active span}: + * Convenience {@link Tracer} that automates managing the {@linkplain SpanManager#currentSpan() currently active span}: *

      *
    1. It is a wrapper that forwards all calls to another {@link Tracer} implementation.
    2. *
    3. {@linkplain Span Spans} created with this tracer are - * automatically {@link GlobalSpanManager#manage(Span) managed} when started,
    4. + * automatically {@link SpanManager#manage(Span) managed} when started, *
    5. and automatically {@link SpanManager.ManagedSpan#release() released} when they finish.
    6. *
    *

    @@ -22,15 +22,36 @@ * If there were, the {@linkplain ManagedSpanTracer} could be a lot simpler.
    * However, lifecycle callbacks in the API form a considerable performance risk. * - * @see GlobalSpanManager + * @see SpanManager */ public final class ManagedSpanTracer implements Tracer { private final Tracer delegate; + private final SpanManager spanManager; + /** + * Automatically manages created spans from delegate using the {@link GlobalSpanManager}. + * + * @param delegate The tracer to be wrapped. + * @see #ManagedSpanTracer(Tracer, SpanManager) + * @see GlobalSpanManager + */ public ManagedSpanTracer(Tracer delegate) { - if (delegate == null) throw new NullPointerException("The delegate Tracer implementation is ."); + this(delegate, GlobalSpanManager.get()); + } + + /** + * Automatically manages created spans from delegate using the the specified {@link SpanManager}. + * + * @param delegate The tracer to be wrapped. + * @param spanManager The span manager to use. + * @see #ManagedSpanTracer(Tracer) + */ + public ManagedSpanTracer(Tracer delegate, SpanManager spanManager) { + if (delegate == null) throw new NullPointerException("Delegate Tracer is ."); + if (spanManager == null) throw new NullPointerException("Span manager is ."); this.delegate = delegate; + this.spanManager = spanManager; } public void inject(SpanContext spanContext, Format format, C carrier) { @@ -44,7 +65,7 @@ public SpanContext extract(Format format, C carrier) { } public SpanBuilder buildSpan(String operationName) { - return new ManagedSpanBuilder(delegate.buildSpan(operationName)); + return new ManagedSpanBuilder(delegate.buildSpan(operationName), spanManager); } @Override From 99931738637692ef8835b71cb544c4fc464a3100 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 09:01:26 +0100 Subject: [PATCH 21/51] Removed GlobalSpanManager for now, wait for demand first. --- README.md | 53 ++---- .../spanmanager/DefaultSpanManager.java | 3 + .../spanmanager/GlobalSpanManager.java | 151 ------------------ .../SpanPropagatingExecutorService.java | 31 +--- .../concurrent/SpanPropagatingExecutors.java | 49 +++--- .../spanmanager/tracer/ManagedSpanTracer.java | 15 +- 6 files changed, 50 insertions(+), 252 deletions(-) delete mode 100644 src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java diff --git a/README.md b/README.md index 287fded..0a85487 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,6 @@ Interface that defines _current_ span management: or the `NoopSpan` if no span is managed. 3. `clear()` provides unconditional cleanup of _all managed spans_ for the current process. -## GlobalSpanManager - -Provides the `GlobalSpanManager.get()` method that returns the singleton _global span manager_. - -When the SpanManager is needed it is lazily looked up using the following rules: - 1. The SpanManager from the last `register(spanManager)` call always takes precedence. - 2. If no SpanManager was registered, one is looked up from the `ServiceLoader`. - The GlobalSpanManager will not attempt to choose between implementations: - 3. If no single implementation is found, the `DefaultSpanManager` will be used. - ## DefaultSpanManager A _default_ SpanManager maintaining a `Stack`-like `ThreadLocal` storage of _linked managed spans_. @@ -38,8 +28,7 @@ Releasing a _linked managed span_ uses the following algorithm: This `ExecutorService` _propagates the current span_ from the caller into each call that is executed. -The current span is obtained from the `GlobalSpanManager`. - +The current span of the caller is obtained from the configured `SpanManager`. _Please Note:_ The active span is merely _propagated_ (as-is). It is explicitly **not** finished when the calls end, @@ -48,9 +37,9 @@ nor will new spans be automatically related to the propagated span. ### SpanPropagatingExecutors Contains factory-methods similar to standard java `Executors`: - - `SpanPropagatingExecutors.newFixedThreadPool(int)` - - `SpanPropagatingExecutors.newSingleThreadExecutor()` - - `SpanPropagatingExecutors.newCachedThreadPool()` + - `SpanPropagatingExecutors.newFixedThreadPool(int, SpanManager)` + - `SpanPropagatingExecutors.newSingleThreadExecutor(SpanManager)` + - `SpanPropagatingExecutors.newCachedThreadPool(SpanManager)` - Variants of the above with additional `ThreadFactory` argument. ## ManagedSpanTracer @@ -61,20 +50,6 @@ This convenience `Tracer` automates managing the _current span_: - automatically _managed_ when started, and - automatically _released_ when finished. -## Custom span managers - -It is possible to provide a custom implementation for `ActiveSpanManager`. -This may be useful if you already have a way to propagate contextual information -from one thread to another. Creating a custom manager is one way to piggyback the _active span_ on -your existing propagation mechanism. - -The _active_ SpanManager is resolved as follows: - 1. The last-registered span manager always takes precedence. - 2. If no manager was registered, one is looked up from the `ServiceLoader`. - The ActiveSpanManager will not attempt to choose between implementations: - 3. If no single implementation is found, the default SpanManager will be used. - - ## Examples ### Manually propagating any Span into a background thread @@ -82,13 +57,14 @@ The _active_ SpanManager is resolved as follows: To propagate a `Span` into a new `Thread` can be accomplished as follows: ```java + final SpanManager spanManager = DefaultSpanManager.getInstance(); final Span someSpan = ... Thread thread = new Thread() { @Override public void run() { - try (ManagedSpan parent = GlobalSpanManager.get().manage(someSpan)) { + try (ManagedSpan parent = spanManager.manage(someSpan)) { // ...regular traced background process... - assert parent == GlobalSpanManager.get().currentSpan(); + assert parent == spanManager.currentSpan(); try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(parent.context()).start()) { // ...traced as child of the propagated span... } @@ -102,21 +78,23 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: ```java class TracedCall implements Callable { + static SpanManager spanManager = DefaultSpanManager.getInstance(); + @Override public String call() { - Span parent = GlobalSpanManager.get().currentSpan(); - try (Span newSpan = tracer.buildSpan("someCall").asChildOf(parent.context).start()) { + try (Span newSpan = tracer.buildSpan("someCall").asChildOf(spanManager.currentSpan().context()).start()) { return "New span: " + newSpan + ", parent: " + parent; } } } - + class Caller { - static ExecutorService propagatingThreadpool = SpanPropagatingExecutorService.of(anyThreadpool()); + static SpanManager spanManager = DefaultSpanManager.getInstance(); + static ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { // ...code that sets the current Span somewhere: - try (ManagedSpan current = GlobalSpanManager.get().manage(someSpan)) { + try (ManagedSpan current = spanManager.manage(someSpan)) { // scheduling the traced call: Future result = propagatingThreadpool.submit(new TracedCall()); @@ -132,7 +110,8 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: ```java class Caller { - static Tracer tracer = new ManagedSpanTracer(anyTracer()); + static Tracer tracer = new ManagedSpanTracer(anyTracer(), DefaultSpanManager.getInstance()); + static ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == currentSpan() diff --git a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index b8c040d..b55b102 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -29,6 +29,9 @@ public final class DefaultSpanManager implements SpanManager { private DefaultSpanManager() { } + /** + * @return The singleton instance of the default span manager. + */ public static SpanManager getInstance() { return INSTANCE; } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java deleted file mode 100644 index de7b6b0..0000000 --- a/src/main/java/io/opentracing/contrib/spanmanager/GlobalSpanManager.java +++ /dev/null @@ -1,151 +0,0 @@ -package io.opentracing.contrib.spanmanager; - -import io.opentracing.Span; -import io.opentracing.Tracer; - -import java.util.Iterator; -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Forwards all methods to another {@linkplain SpanManager} that can be configured in one of two ways: - *

      - *
    1. Explicitly, calling {@link #register(SpanManager)} with a configured span manager, or:
    2. - *
    3. Automatically using the Java {@link ServiceLoader} SPI mechanism to load a {@linkplain SpanManager} - * from the classpath.
    4. - *
    - *

    - * When the SpanManager is needed it is lazily looked up using the following rules: - *

      - *
    1. The last-{@link #register(SpanManager) registered} SpanManager always takes precedence.
    2. - *
    3. If no SpanManager was registered, one is looked up from the {@link ServiceLoader}.
      - * The {@linkplain GlobalSpanManager} will not attempt to choose between implementations:
    4. - *
    5. If no single SpanManager service is found, the {@link DefaultSpanManager} will be used.
    6. - *
    - * - * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutors - * @see io.opentracing.contrib.spanmanager.concurrent.SpanPropagatingExecutorService - */ -public final class GlobalSpanManager implements SpanManager { - private static final Logger LOGGER = Logger.getLogger(GlobalSpanManager.class.getName()); - - /** - * Singleton instance. - *

    - * Since we cannot prevent people using {@linkplain #get() GlobalSpanManager.get()} as a constant, - * this guarantees that references obtained before, during or after initialization - * all behave as if obtained after initialization once properly initialized.
    - * As a minor additional benefit it makes it harder to circumvent the {@link Tracer} API. - */ - private static final GlobalSpanManager INSTANCE = new GlobalSpanManager(); - - /** - * The resolved {@link SpanManager} to delegate the implementation to. - *

    - * This can be an {@link #register(SpanManager) explicitly registered} or - * the {@link #loadSingleSpiImplementation() automatically resolved} SpanManager - * (or null during initialization). - */ - private final AtomicReference globalSpanManager = new AtomicReference(); - - private GlobalSpanManager() { - } - - /** - * Returns the constant {@linkplain GlobalSpanManager}. - *

    - * All methods are forwarded to the currently configured SpanManager.
    - * Until a tracer is {@link #register(SpanManager) explicitly configured}, - * one is looked up from the {@link ServiceLoader}, - * falling back to the {@link DefaultSpanManager}.
    - * A span manager can be re-configured at any time. - * For example, the span manager used to manage a new span - * may be different than the one backing the {@link SpanManager.ManagedSpan ManagedSpan} - * from the previous span. - * - * @return The global tracer constant. - * @see #register(SpanManager) - */ - public static SpanManager get() { - return INSTANCE; - } - - /** - * Lazily resolves the global SpanManager. - *

    - * This indirection guarantees a valid SpanManager is available before, during and after - * application initialization.
    - * It also means the global SpanManager can be {@link #register(SpanManager) re-configured} at any time. - * - * @return The global SpanManager (non-null). - */ - private static SpanManager lazySpanManager() { - SpanManager spanManager = INSTANCE.globalSpanManager.get(); - if (spanManager == null) { - final SpanManager resolved = loadSingleSpiImplementation(); - while (spanManager == null && resolved != null) { // handle rare race condition - INSTANCE.globalSpanManager.compareAndSet(null, resolved); - spanManager = INSTANCE.globalSpanManager.get(); - } - LOGGER.log(Level.INFO, "Using GlobalSpanManager: {0}.", spanManager); - } - return spanManager; - } - - /** - * Explicitly configures a SpanManager to back the behaviour of the {@link GlobalSpanManager} methods. - *

    - * The previous SpanManager is returned so it can be: - *

      - *
    • {@link SpanManager#clear() cleared} as it no longer is the global manager, or
    • - *
    • restored later, for example in a testing situation.
    • - *
    - * - * @param spanManager New SpanManager for span propagation. - * @return The previously global SpanManager, or null if there was none. - */ - public static SpanManager register(SpanManager spanManager) { - SpanManager previous = INSTANCE.globalSpanManager.getAndSet(spanManager); - LOGGER.log(Level.INFO, "Registered GlobalSpanManager {0} (previously {1}).", new Object[]{spanManager, previous}); - return previous; - } - - @Override - public Span currentSpan() { - return lazySpanManager().currentSpan(); - } - - @Override - public ManagedSpan manage(Span span) { - return lazySpanManager().manage(span); - } - - @Override - public void clear() { - lazySpanManager().clear(); - } - - /** - * Loads a single service implementation from {@link ServiceLoader}. - * - * @return The single service or the DefaultSpanManager. - * @see DefaultSpanManager - */ - private static SpanManager loadSingleSpiImplementation() { - // Use the ServiceLoader to find the declared SpanManager implementation. - Iterator spiImplementations = - ServiceLoader.load(SpanManager.class, SpanManager.class.getClassLoader()).iterator(); - if (spiImplementations.hasNext()) { - SpanManager foundImplementation = spiImplementations.next(); - if (!spiImplementations.hasNext()) { - return foundImplementation; - } - LOGGER.log(Level.WARNING, "More than one SpanManager service found. " + - "Falling back to default ThreadLocal implementation."); - } - return DefaultSpanManager.getInstance(); - } - -} diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index 6f88788..69c76d8 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -21,37 +21,18 @@ public class SpanPropagatingExecutorService implements ExecutorService { private final ExecutorService delegate; private final SpanManager spanManager; - private SpanPropagatingExecutorService(ExecutorService delegate, SpanManager spanManager) { - if (delegate == null) throw new NullPointerException("Delegate executor service is ."); - if (spanManager == null) throw new NullPointerException("SpanManager is ."); - this.delegate = delegate; - this.spanManager = spanManager; - } - - /** - * Wraps the delegate ExecutorService to propagate the {@link GlobalSpanManager#currentSpan() currently active span} - * of callers into the executed calls, using the {@link GlobalSpanManager}. - * - * @param delegate The executorservice to forward calls to. - * @return An ExecutorService that propagates active spans from callers into executed calls. - * @see #of(ExecutorService, SpanManager) - * @see GlobalSpanManager - */ - public static SpanPropagatingExecutorService of(final ExecutorService delegate) { - return of(delegate, GlobalSpanManager.get()); - } - /** * Wraps the delegate ExecutorService to propagate the {@link SpanManager#currentSpan() currently active span} * of callers into the executed calls, using the specified {@link SpanManager}. * * @param delegate The executorservice to forward calls to. - * @param spanManager The span manager to use. - * @return An ExecutorService that propagates active spans from callers into executed calls. - * @see #of(ExecutorService) + * @param spanManager The manager to propagate spans with. */ - public static SpanPropagatingExecutorService of(ExecutorService delegate, SpanManager spanManager) { - return new SpanPropagatingExecutorService(delegate, spanManager); + public SpanPropagatingExecutorService(ExecutorService delegate, SpanManager spanManager) { + if (delegate == null) throw new NullPointerException("Delegate executor service is ."); + if (spanManager == null) throw new NullPointerException("SpanManager is ."); + this.delegate = delegate; + this.spanManager = spanManager; } /** diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index f412c37..32b51e4 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -2,24 +2,23 @@ import io.opentracing.contrib.spanmanager.SpanManager; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; /** * Factory-methods similar to standard java {@link Executors}: *
      - *
    • {@link #newFixedThreadPool(int)}
    • - *
    • {@link #newSingleThreadExecutor()}
    • - *
    • {@link #newCachedThreadPool()}
    • - *
    • Variants of the above with additional {@link ThreadFactory} and {@link SpanManager} arguments: + *
    • {@link #newFixedThreadPool(int, SpanManager)}
    • + *
    • {@link #newSingleThreadExecutor(SpanManager)}
    • + *
    • {@link #newCachedThreadPool(SpanManager)}
    • + *
    • Variants of the above with additional {@link ThreadFactory} argument: * {@link #newFixedThreadPool(int, ThreadFactory, SpanManager)}, * {@link #newSingleThreadExecutor(ThreadFactory, SpanManager)}, * {@link #newCachedThreadPool(ThreadFactory, SpanManager)} *
    • *
    * - * @see SpanPropagatingExecutorService#of(ExecutorService) + * @see SpanPropagatingExecutorService */ public final class SpanPropagatingExecutors { @@ -32,15 +31,15 @@ private SpanPropagatingExecutors() { /** * This method returns a {@link Executors#newFixedThreadPool(int) fixed threadpool} that propagates - * the active span into the started threads, using the global span manager. + * the active span into the started threads. * - * @param nThreads the number of threads in the pool + * @param nThreads the number of threads in the pool + * @param spanManager the manager for span propagation. * @return the newly created thread pool * @see Executors#newFixedThreadPool(int) - * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ - public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { - return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads)); + public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, SpanManager spanManager) { + return new SpanPropagatingExecutorService(Executors.newFixedThreadPool(nThreads), spanManager); } /** @@ -49,25 +48,25 @@ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads) { * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads - * @param spanManager the SpanManager to manage spans with. + * @param spanManager the manager for span propagation. * @return the newly created thread pool * @see Executors#newFixedThreadPool(int, ThreadFactory) */ public static SpanPropagatingExecutorService newFixedThreadPool( int nThreads, ThreadFactory threadFactory, SpanManager spanManager) { - return SpanPropagatingExecutorService.of(Executors.newFixedThreadPool(nThreads, threadFactory), spanManager); + return new SpanPropagatingExecutorService(Executors.newFixedThreadPool(nThreads, threadFactory), spanManager); } /** * This method returns a {@link Executors#newSingleThreadExecutor() single-threaded executor} that propagates - * the active span into the started thread using the global span manager. + * the active span into the started thread. * + * @param spanManager the manager for span propagation. * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor() - * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ - public static SpanPropagatingExecutorService newSingleThreadExecutor() { - return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor()); + public static SpanPropagatingExecutorService newSingleThreadExecutor(SpanManager spanManager) { + return new SpanPropagatingExecutorService(Executors.newSingleThreadExecutor(), spanManager); } /** @@ -75,25 +74,25 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor() { * that propagates the active span into the started thread. * * @param threadFactory the factory to use when creating new threads - * @param spanManager the SpanManager to manage spans with. + * @param spanManager the manager for span propagation. * @return the newly created single-theaded executor * @see Executors#newSingleThreadExecutor(ThreadFactory) */ public static SpanPropagatingExecutorService newSingleThreadExecutor( ThreadFactory threadFactory, SpanManager spanManager) { - return SpanPropagatingExecutorService.of(Executors.newSingleThreadExecutor(threadFactory), spanManager); + return new SpanPropagatingExecutorService(Executors.newSingleThreadExecutor(threadFactory), spanManager); } /** * This method returns a {@link Executors#newCachedThreadPool() cached threadpool} that propagates - * the active span into the started threads using the global span manager. + * the active span into the started threads. * + * @param spanManager the manager for span propagation. * @return the newly created thread pool * @see Executors#newCachedThreadPool() - * @see io.opentracing.contrib.spanmanager.GlobalSpanManager */ - public static SpanPropagatingExecutorService newCachedThreadPool() { - return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool()); + public static SpanPropagatingExecutorService newCachedThreadPool(SpanManager spanManager) { + return new SpanPropagatingExecutorService(Executors.newCachedThreadPool(), spanManager); } /** @@ -101,12 +100,12 @@ public static SpanPropagatingExecutorService newCachedThreadPool() { * the active span into the started threads. * * @param threadFactory the factory to use when creating new threads - * @param spanManager the SpanManager to manage spans with. + * @param spanManager the manager for span propagation. * @return the newly created thread pool * @see Executors#newCachedThreadPool(ThreadFactory) */ public static SpanPropagatingExecutorService newCachedThreadPool(ThreadFactory threadFactory, SpanManager spanManager) { - return SpanPropagatingExecutorService.of(Executors.newCachedThreadPool(threadFactory), spanManager); + return new SpanPropagatingExecutorService(Executors.newCachedThreadPool(threadFactory), spanManager); } } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index d48dae2..4a4edde 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -3,7 +3,6 @@ import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; -import io.opentracing.contrib.spanmanager.GlobalSpanManager; import io.opentracing.contrib.spanmanager.SpanManager; import io.opentracing.propagation.Format; @@ -29,23 +28,11 @@ public final class ManagedSpanTracer implements Tracer { private final Tracer delegate; private final SpanManager spanManager; - /** - * Automatically manages created spans from delegate using the {@link GlobalSpanManager}. - * - * @param delegate The tracer to be wrapped. - * @see #ManagedSpanTracer(Tracer, SpanManager) - * @see GlobalSpanManager - */ - public ManagedSpanTracer(Tracer delegate) { - this(delegate, GlobalSpanManager.get()); - } - /** * Automatically manages created spans from delegate using the the specified {@link SpanManager}. * * @param delegate The tracer to be wrapped. - * @param spanManager The span manager to use. - * @see #ManagedSpanTracer(Tracer) + * @param spanManager The manager providing span propagation. */ public ManagedSpanTracer(Tracer delegate, SpanManager spanManager) { if (delegate == null) throw new NullPointerException("Delegate Tracer is ."); From 78996c8a6e550d0402f1416d9e7e51f5626640dd Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 09:03:21 +0100 Subject: [PATCH 22/51] Removed documented reference to GlobalSpanManager. --- .../concurrent/SpanPropagatingExecutorService.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index 69c76d8..fb2f37a 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -1,7 +1,6 @@ package io.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; -import io.opentracing.contrib.spanmanager.GlobalSpanManager; import io.opentracing.contrib.spanmanager.SpanManager; import java.util.ArrayList; @@ -36,7 +35,7 @@ public SpanPropagatingExecutorService(ExecutorService delegate, SpanManager span } /** - * Propagates the {@link GlobalSpanManager#currentSpan() custom current span} into the runnable + * Propagates the {@link SpanManager#currentSpan() custom current span} into the runnable * and performs cleanup afterwards. *

    * Note: The customCurrentSpan is merely propagated. @@ -45,14 +44,13 @@ public SpanPropagatingExecutorService(ExecutorService delegate, SpanManager span * @param runnable The runnable to be executed. * @param customCurrentSpan The span to be propagated. * @return The wrapped runnable to execute with the custom span as current span. - * @see GlobalSpanManager */ private Runnable runnableWithCurrentSpan(Runnable runnable, Span customCurrentSpan) { return new RunnableWithManagedSpan(runnable, spanManager, customCurrentSpan); } /** - * Propagates the {@link GlobalSpanManager#currentSpan() custom current span} into the callable + * Propagates the {@link SpanManager#currentSpan() custom current span} into the callable * and performs cleanup afterwards. *

    * Note: The customCurrentSpan is merely propagated. From 282941de59808f124d4edc84a91773f27117552a Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 12:04:37 +0100 Subject: [PATCH 23/51] Made if and rewrap separation clearer. --- .../spanmanager/tracer/ManagedSpanBuilder.java | 17 ++++++++++------- .../spanmanager/tracer/ManagedSpanTracer.java | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index 05d9fd2..ead008b 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -1,5 +1,6 @@ package io.opentracing.contrib.spanmanager.tracer; +import io.opentracing.References; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer.SpanBuilder; @@ -19,7 +20,7 @@ */ final class ManagedSpanBuilder implements SpanBuilder { - protected SpanBuilder delegate; + SpanBuilder delegate; private final SpanManager spanManager; ManagedSpanBuilder(SpanBuilder delegate, SpanManager spanManager) { @@ -36,7 +37,9 @@ final class ManagedSpanBuilder implements SpanBuilder { * @return This re-wrapped ActiveSpanBuilder. */ SpanBuilder rewrap(SpanBuilder spanBuilder) { - if (spanBuilder != null) this.delegate = spanBuilder; + if (spanBuilder != null) { + this.delegate = spanBuilder; + } return this; } @@ -55,17 +58,17 @@ public Span start() { // All other methods are forwarded to the delegate SpanBuilder. public SpanBuilder asChildOf(SpanContext parent) { - if (parent instanceof ManagedSpanBuilder) parent = ((ManagedSpanBuilder) parent).delegate; - return rewrap(delegate.asChildOf(parent)); + return addReference(References.CHILD_OF, parent); } public SpanBuilder asChildOf(Span parent) { - if (parent instanceof SpanManager.ManagedSpan) parent = ((SpanManager.ManagedSpan) parent).getSpan(); - return rewrap(delegate.asChildOf(parent)); + return addReference(References.CHILD_OF, parent.context()); } public SpanBuilder addReference(String referenceType, SpanContext context) { - if (context instanceof ManagedSpanBuilder) context = ((ManagedSpanBuilder) context).delegate; + if (context instanceof ManagedSpanBuilder) { // Weird that SpanBuilder extends Context! + context = ((ManagedSpanBuilder) context).delegate; + } return rewrap(delegate.addReference(referenceType, context)); } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index 4a4edde..993c961 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -42,8 +42,9 @@ public ManagedSpanTracer(Tracer delegate, SpanManager spanManager) { } public void inject(SpanContext spanContext, Format format, C carrier) { - // Weird that SpanBuilder extends Context! - if (spanContext instanceof ManagedSpanBuilder) spanContext = ((ManagedSpanBuilder) spanContext).delegate; + if (spanContext instanceof ManagedSpanBuilder) { // Weird that SpanBuilder extends Context! + spanContext = ((ManagedSpanBuilder) spanContext).delegate; + } delegate.inject(spanContext, format, carrier); } From 55cec1cd558434aa32d00f34f8b2454739937d32 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 13:08:39 +0100 Subject: [PATCH 24/51] Moved stack unwinding algorithm to separate method. This makes the release() from another thread more predictable. I'll add unit tests next (as the code now deviates from my old, tested library). --- .../spanmanager/DefaultSpanManager.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index b55b102..cbf642c 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -36,15 +36,32 @@ public static SpanManager getInstance() { return INSTANCE; } + /** + * Stack unwinding algorithm that refreshes the currently managed span. + *

    + * See {@link DefaultSpanManager class javadoc} for a full description. + * + * @return The current non-released LinkedManagedSpan or null if none remained. + */ + private LinkedManagedSpan refreshCurrent() { + LinkedManagedSpan current = managed.get(); + while (current != null && current.released.get()) { // Unwind stack if necessary. + current = current.parent; + } + if (current == null) managed.remove(); + else managed.set(current); + return current; + } + @Override public Span currentSpan() { - LinkedManagedSpan managedSpan = managed.get(); - return managedSpan != null ? managedSpan.span : NoopSpan.INSTANCE; + LinkedManagedSpan current = refreshCurrent(); + return current != null ? current.span : NoopSpan.INSTANCE; } @Override public SpanManager.ManagedSpan manage(Span span) { - LinkedManagedSpan managedSpan = new LinkedManagedSpan(span, managed.get()); + LinkedManagedSpan managedSpan = new LinkedManagedSpan(span, refreshCurrent()); managed.set(managedSpan); return managedSpan; } @@ -74,22 +91,10 @@ public Span getSpan() { return span; } - /** - * Please see {@link DefaultSpanManager outer class description} for the stack-unwinding documentation. - */ public void release() { if (released.compareAndSet(false, true)) { - LinkedManagedSpan current = managed.get(); - if (this == current) { - while (current != null && current.released.get()) { - current = current.parent; - } - if (current == null) managed.remove(); - else managed.set(current); - LOGGER.log(Level.FINER, "Deactivated {0} and restored managed span to {1}.", new Object[]{this, current}); - } else { - LOGGER.log(Level.FINE, "Deactivated {0} without affecting managed span {1}.", new Object[]{this, current}); - } + LinkedManagedSpan current = refreshCurrent(); // Trigger stack-unwinding algorithm. + LOGGER.log(Level.FINER, "Released {0}, current span is {1}.", new Object[]{this, current}); } else { LOGGER.log(Level.FINEST, "No action needed, {0} was already released.", this); } From 2656675891f0052c5ed0263e0a4384841c9dda39 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 14:42:39 +0100 Subject: [PATCH 25/51] Unit tests for DefaultSpanManager stack-unwinding algorithm. --- .../spanmanager/DefaultSpanManager.java | 11 +- .../spanmanager/DefaultSpanManagerTest.java | 130 ++++++++++++++++++ 2 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java diff --git a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index cbf642c..0578909 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -44,19 +44,22 @@ public static SpanManager getInstance() { * @return The current non-released LinkedManagedSpan or null if none remained. */ private LinkedManagedSpan refreshCurrent() { - LinkedManagedSpan current = managed.get(); + LinkedManagedSpan managedSpan = managed.get(); + LinkedManagedSpan current = managedSpan; while (current != null && current.released.get()) { // Unwind stack if necessary. current = current.parent; } - if (current == null) managed.remove(); - else managed.set(current); + if (current != managedSpan) { // refresh current if necessary. + if (current == null) managed.remove(); + else managed.set(current); + } return current; } @Override public Span currentSpan() { LinkedManagedSpan current = refreshCurrent(); - return current != null ? current.span : NoopSpan.INSTANCE; + return current != null && current.span != null ? current.span : NoopSpan.INSTANCE; } @Override diff --git a/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java new file mode 100644 index 0000000..ea4a3fa --- /dev/null +++ b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java @@ -0,0 +1,130 @@ +package io.opentracing.contrib.spanmanager; + +import io.opentracing.NoopSpan; +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.SpanManager.ManagedSpan; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.mock; + +public class DefaultSpanManagerTest { + + SpanManager manager; + + @Before + public void resetManager() { + manager = DefaultSpanManager.getInstance(); + manager.clear(); + } + + @Test + public void testBasicStackBehaviour() { + Span span1 = mock(Span.class); + Span span2 = mock(Span.class); + Span span3 = mock(Span.class); + + assertThat("empty stack", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed1 = manager.manage(span1); + assertThat("pushed span1", manager.currentSpan(), is(sameInstance(span1))); + + ManagedSpan managed2 = manager.manage(span2); + assertThat("pushed span2", manager.currentSpan(), is(sameInstance(span2))); + + ManagedSpan managed3 = manager.manage(span3); + assertThat("pushed span3", manager.currentSpan(), is(sameInstance(span3))); + + managed3.release(); + assertThat("popped span3", manager.currentSpan(), is(sameInstance(span2))); + + managed2.release(); + assertThat("popped span2", manager.currentSpan(), is(sameInstance(span1))); + + managed1.release(); + assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + } + + @Test + public void testMultipleReleases() { + Span span1 = mock(Span.class); + Span span2 = mock(Span.class); + + assertThat("empty stack", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed1 = manager.manage(span1); + assertThat("pushed span1", manager.currentSpan(), is(sameInstance(span1))); + + ManagedSpan managed2 = manager.manage(span2); + assertThat("pushed span2", manager.currentSpan(), is(sameInstance(span2))); + + managed2.release(); + managed2.release(); + assertThat("popped span2", manager.currentSpan(), is(sameInstance(span1))); + + managed1.release(); + managed2.release(); + managed1.release(); + managed2.release(); + assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + } + + + @Test + public void testTemporaryNoSpan() { + Span span1 = mock(Span.class); + Span span2 = null; + Span span3 = mock(Span.class); + + assertThat("empty stack", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed1 = manager.manage(span1); + assertThat("pushed span1", manager.currentSpan(), is(sameInstance(span1))); + + ManagedSpan managed2 = manager.manage(span2); + assertThat("pushed span2", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed3 = manager.manage(span3); + assertThat("pushed span3", manager.currentSpan(), is(sameInstance(span3))); + + managed3.release(); + assertThat("popped span3", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + managed2.release(); + assertThat("popped span2", manager.currentSpan(), is(sameInstance(span1))); + + managed1.release(); + assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + } + + @Test + public void testOutOfOrderRelease() { + Span span1 = mock(Span.class); + Span span2 = mock(Span.class); + Span span3 = mock(Span.class); + + assertThat("empty stack", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed1 = manager.manage(span1); + assertThat("pushed span1", manager.currentSpan(), is(sameInstance(span1))); + + ManagedSpan managed2 = manager.manage(span2); + assertThat("pushed span2", manager.currentSpan(), is(sameInstance(span2))); + + ManagedSpan managed3 = manager.manage(span3); + assertThat("pushed span3", manager.currentSpan(), is(sameInstance(span3))); + + // Pop2: Span1 -> Span2(X) -> Span3 : current span stays Span3 + managed2.release(); + assertThat("released span2", manager.currentSpan(), is(sameInstance(span3))); + + managed3.release(); + assertThat("skipped span2 (already-released)", manager.currentSpan(), is(sameInstance(span1))); + + managed1.release(); + assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + } + +} From 4b09079683bac79897092e6d7c10fcac4e033488 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 15:01:46 +0100 Subject: [PATCH 26/51] Unit test where ManagedSpan was released from other thread. --- .../spanmanager/DefaultSpanManagerTest.java | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java index ea4a3fa..5e732be 100644 --- a/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java +++ b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java @@ -116,7 +116,7 @@ public void testOutOfOrderRelease() { ManagedSpan managed3 = manager.manage(span3); assertThat("pushed span3", manager.currentSpan(), is(sameInstance(span3))); - // Pop2: Span1 -> Span2(X) -> Span3 : current span stays Span3 + // Pop2: Span1 -> Span2(X) -> Span3 : currentSpan stays Span3 managed2.release(); assertThat("released span2", manager.currentSpan(), is(sameInstance(span3))); @@ -127,4 +127,53 @@ public void testOutOfOrderRelease() { assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); } + /** + * Note: This is not a normal use-case!
    + * The {@link ManagedSpan} is intended to be created and used in the scope of a try-with-resources block + * (so within the scope of a single thread). + *

    + * This test is merely here to guarantee predictable behaviour when it happens. + */ + @Test + public void testReleaseFromOtherThreads() throws InterruptedException { + Span span1 = mock(Span.class); + Span span2 = mock(Span.class); + Span span3 = mock(Span.class); + + assertThat("empty stack", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + + ManagedSpan managed1 = manager.manage(span1); + assertThat("pushed span1", manager.currentSpan(), is(sameInstance(span1))); + + final ManagedSpan managed2 = manager.manage(span2); + assertThat("pushed span2", manager.currentSpan(), is(sameInstance(span2))); + + ManagedSpan managed3 = manager.manage(span3); + assertThat("pushed span3", manager.currentSpan(), is(sameInstance(span3))); + + // Schedule 10 threads to release managed2 + Thread[] releasers = new Thread[10]; + for (int i = 0; i < releasers.length; i++) { + releasers[i] = new Thread() { + @Override + public void run() { + managed2.release(); + } + }; + } + + // Schedule managed2.release() 10x + for (int i = 0; i < releasers.length; i++) releasers[i].start(); + + managed3.release(); + + // Wait for managed2.releases + for (int i = 0; i < releasers.length; i++) releasers[i].join(); + + assertThat("popped span2+3", manager.currentSpan(), is(sameInstance(span1))); + + managed1.release(); + assertThat("popped span1", manager.currentSpan(), is(instanceOf(NoopSpan.class))); + } + } From 9897d4f3541a429c8fd8bf52f05fb24b6a9e9621 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 15:15:04 +0100 Subject: [PATCH 27/51] README example fix. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a85487..2ef13a6 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: public void run() { try (ManagedSpan parent = spanManager.manage(someSpan)) { // ...regular traced background process... - assert parent == spanManager.currentSpan(); + assert parent.getSpan() == spanManager.currentSpan(); try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(parent.context()).start()) { // ...traced as child of the propagated span... } From 60bea2da6e3da4224c3edcd48834a6cf70510a0c Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 15:16:00 +0100 Subject: [PATCH 28/51] README example fix. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ef13a6..a1a0d86 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: try (ManagedSpan parent = spanManager.manage(someSpan)) { // ...regular traced background process... assert parent.getSpan() == spanManager.currentSpan(); - try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(parent.context()).start()) { + try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(spanManager.currentSpan().context()).start()) { // ...traced as child of the propagated span... } } From f83baea8882129714406b15c175d2f2053e9d0fc Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 15:49:22 +0100 Subject: [PATCH 29/51] README example elaborated on manual example to show verbosity. --- README.md | 96 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index a1a0d86..5505ebb 100644 --- a/README.md +++ b/README.md @@ -52,33 +52,11 @@ This convenience `Tracer` automates managing the _current span_: ## Examples -### Manually propagating any Span into a background thread - -To propagate a `Span` into a new `Thread` can be accomplished as follows: - -```java - final SpanManager spanManager = DefaultSpanManager.getInstance(); - final Span someSpan = ... - Thread thread = new Thread() { - @Override - public void run() { - try (ManagedSpan parent = spanManager.manage(someSpan)) { - // ...regular traced background process... - assert parent.getSpan() == spanManager.currentSpan(); - try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(spanManager.currentSpan().context()).start()) { - // ...traced as child of the propagated span... - } - } - } - }; - -``` - -### Threadpool that propagates currentSpan() into threads +### Threadpool that propagates SpanManager.currentSpan() into threads ```java class TracedCall implements Callable { - static SpanManager spanManager = DefaultSpanManager.getInstance(); + SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); @Override public String call() { @@ -89,8 +67,8 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: } class Caller { - static SpanManager spanManager = DefaultSpanManager.getInstance(); - static ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); + SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); + ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { // ...code that sets the current Span somewhere: @@ -105,16 +83,17 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: ``` -### Threadpool with 'managed span' tracer +### Propagating threadpool with 'ManagedSpan' Tracer ```java class Caller { - static Tracer tracer = new ManagedSpanTracer(anyTracer(), DefaultSpanManager.getInstance()); - static ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); + SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); + Tracer tracer = new ManagedSpanTracer(anyTracer(), spanManager); + ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { - try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == currentSpan() + try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent === spanManager.currentSpan() // Scheduling the traced call: Future result = propagatingThreadpool.submit(new TracedCall()); @@ -125,3 +104,60 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: ``` +### Manually propagating any Span into a background thread + +To propagate a `Span` into a new `Thread` can be accomplished as follows: + +```java + + class ExampleThread extends Thread { + private SpanManager spanManager; + private Span propagatedSpan; + + ExampleThread(SpanManager spanManager) { + this.spanManager = spanManager; + this.propagatedSpan = spanManager.currentSpan(); // from calling thread. + } + + @Override + public void run() { + try (ManagedSpan parent = spanManager.manage(propagatedSpan)) { // make currentSpan() === propagatedSpan + + internalCode(); // any code that may require spanManager.currentSpan(); + + } // spanManager.currentSpan() is restored (NoopSpan in new thread). + } + + private void internalCode() { + // some code or library starting a new child-span from the 'current' span: + Span currentSpan = spanManager.currentSpan(); + try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(currentSpan.context()).start()) { + + // ...traced as child of the propagated span... + + } // newSpan is finished. + } + + } + + class App { + public static void main(String... args) throws InterruptedException { + Config config = ...; + Tracer tracer = config.getTracer(); + SpanManager spanManager = config.getSpanManager(); // or DefaultSpanManager.getInstance(); + Thread example = new ExampleThread(spanManager); + + // Start an outer span: + try (Span appSpan = tracer.buildSpan("main").start(); + ManagedSpan managed = spanManager.manage(appSpan)) { + + example.start(); + example.join(); + + } + + System.exit(0); + } + } + +``` From 40627f91ecfa7a8cae68b53214f8bfbb04262b25 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 16:39:24 +0100 Subject: [PATCH 30/51] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5505ebb..7a1ce51 100644 --- a/README.md +++ b/README.md @@ -145,12 +145,12 @@ To propagate a `Span` into a new `Thread` can be accomplished as follows: Config config = ...; Tracer tracer = config.getTracer(); SpanManager spanManager = config.getSpanManager(); // or DefaultSpanManager.getInstance(); - Thread example = new ExampleThread(spanManager); // Start an outer span: try (Span appSpan = tracer.buildSpan("main").start(); ManagedSpan managed = spanManager.manage(appSpan)) { + Thread example = new ExampleThread(spanManager); example.start(); example.join(); From 934a2b62cfeb760f8261807b2cb45473f78a2f63 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 16:40:33 +0100 Subject: [PATCH 31/51] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a1ce51..0766cea 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ This convenience `Tracer` automates managing the _current span_: // Scheduling the traced call: Future result = propagatingThreadpool.submit(new TracedCall()); - } // ((ManagedSpan) parent).release() // Performed by ManagedSpanTracer + } // parent.finish() + ((ManagedSpan) parent).release() // Performed by ManagedSpanTracer } } From d29009fd75b8911f2de1f49f819d66316a5e517f Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 16:42:18 +0100 Subject: [PATCH 32/51] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0766cea..1c06b00 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ This convenience `Tracer` automates managing the _current span_: @Override public String call() { - try (Span newSpan = tracer.buildSpan("someCall").asChildOf(spanManager.currentSpan().context()).start()) { + Span currentSpan = spanManager.currentSpan(); + try (Span newSpan = tracer.buildSpan("someCall").asChildOf(currentSpan.context()).start()) { return "New span: " + newSpan + ", parent: " + parent; } } From 8dc6c3995fb6d7c776e42c95be60426ca58720d2 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 16:45:46 +0100 Subject: [PATCH 33/51] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1c06b00..4076c41 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Interface that defines _current_ span management: A _default_ SpanManager maintaining a `Stack`-like `ThreadLocal` storage of _linked managed spans_. Releasing a _linked managed span_ uses the following algorithm: - 1. If the released span is not the _managed_ span, the _current managed_ span is left alone. - 2. Otherwise, the first parent that is not yet released is set as the new managed span. - 3. If no managed parents remain, the managed span is cleared. + 1. If the released span is not the _current_ span, the current span is left alone. + 2. Otherwise, the first parent that is not yet released is set as the new current span. + 3. If no current parents remain, the current span is cleared. 4. Consecutive `release()` calls for already-released spans will be ignored. ## Concurrency From 91156284359f081c441f620871270d4ae0d971eb Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Mon, 9 Jan 2017 21:26:56 +0100 Subject: [PATCH 34/51] Some additional simple unit tests. --- .../concurrent/CallableWithManagedSpan.java | 6 +- .../concurrent/RunnableWithManagedSpan.java | 6 +- .../SpanPropagatingExecutorServiceTest.java | 86 +++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java index e935026..b206c9c 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java @@ -17,10 +17,10 @@ final class CallableWithManagedSpan implements Callable { private final SpanManager spanManager; private final Span spanToManage; - CallableWithManagedSpan(Callable delegate, SpanManager spanManager, Span spanToManage) { - if (delegate == null) throw new NullPointerException("Callable delegate is ."); + CallableWithManagedSpan(Callable callable, SpanManager spanManager, Span spanToManage) { + if (callable == null) throw new NullPointerException("Callable is ."); if (spanManager == null) throw new NullPointerException("Span manager is ."); - this.delegate = delegate; + this.delegate = callable; this.spanManager = spanManager; this.spanToManage = spanToManage; } diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java index d203a46..8610c43 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java @@ -15,10 +15,10 @@ final class RunnableWithManagedSpan implements Runnable { private final SpanManager spanManager; private final Span spanToManage; - RunnableWithManagedSpan(Runnable delegate, SpanManager spanManager, Span spanToManage) { - if (delegate == null) throw new NullPointerException("Runnable delegate is ."); + RunnableWithManagedSpan(Runnable runnable, SpanManager spanManager, Span spanToManage) { + if (runnable == null) throw new NullPointerException("Runnable is ."); if (spanManager == null) throw new NullPointerException("Span manager is ."); - this.delegate = delegate; + this.delegate = runnable; this.spanManager = spanManager; this.spanToManage = spanToManage; } diff --git a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java new file mode 100644 index 0000000..0e523ab --- /dev/null +++ b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java @@ -0,0 +1,86 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.SpanManager; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.*; + +public class SpanPropagatingExecutorServiceTest { + + ExecutorService mockExecutorService; + SpanManager mockSpanManager; + Span mockSpan; + + SpanPropagatingExecutorService service; + + @Before + public void setUp() { + mockExecutorService = mock(ExecutorService.class); + mockSpanManager = mock(SpanManager.class); + mockSpan = mock(Span.class); + when(mockSpanManager.currentSpan()).thenReturn(mockSpan); + + service = new SpanPropagatingExecutorService(mockExecutorService, mockSpanManager); + } + + @After + public void verifyMocks() { + verifyNoMoreInteractions(mockExecutorService, mockSpanManager, mockSpan); + } + + @Test + public void testExecuteRunnable() { + Runnable runnable = mock(Runnable.class); + + service.execute(runnable); + + verify(mockSpanManager).currentSpan(); // current span must be propagated + verify(mockExecutorService).execute(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan + verifyNoMoreInteractions(runnable); + } + + @Test + public void testSubmitRunnable() { + Runnable runnable = mock(Runnable.class); + Future future = mock(Future.class); + when(mockExecutorService.submit(any(Runnable.class))).thenReturn(future); + + assertThat(service.submit(runnable), is(sameInstance(future))); + + verify(mockSpanManager).currentSpan(); // current span must be propagated. + verify(mockExecutorService).submit(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan + verifyNoMoreInteractions(runnable, future); + } + + @Test + public void testConstructorWithoutDelegate() { + try { + new SpanPropagatingExecutorService(null, mock(SpanManager.class)); + fail("Exception with message expected."); + } catch (RuntimeException expected) { + assertThat(expected.getMessage(), is(Matchers.notNullValue())); + } + } + + @Test + public void testConstructorWithoutSpanManager() { + try { + new SpanPropagatingExecutorService(mock(ExecutorService.class), null); + fail("Exception with message expected."); + } catch (RuntimeException expected) { + assertThat(expected.getMessage(), is(Matchers.notNullValue())); + } + } + +} From a3a4d942bdc4513acee15d81451d98cb737c1cd1 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Tue, 10 Jan 2017 10:59:49 +0100 Subject: [PATCH 35/51] Unit test for SpanPropagatingExecutorService. --- ...panPropagatingExecutorServiceMockTest.java | 112 +++++++++++++ .../SpanPropagatingExecutorServiceTest.java | 151 +++++++++++++----- 2 files changed, 220 insertions(+), 43 deletions(-) create mode 100644 src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java diff --git a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java new file mode 100644 index 0000000..d1e2eda --- /dev/null +++ b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java @@ -0,0 +1,112 @@ +package io.opentracing.contrib.spanmanager.concurrent; + +import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.SpanManager; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.*; + +public class SpanPropagatingExecutorServiceMockTest { + + ExecutorService mockExecutorService; + SpanManager mockSpanManager; + Span mockSpan; + + SpanPropagatingExecutorService service; + + @Before + public void setUp() { + mockExecutorService = mock(ExecutorService.class); + mockSpanManager = mock(SpanManager.class); + mockSpan = mock(Span.class); + when(mockSpanManager.currentSpan()).thenReturn(mockSpan); + + service = new SpanPropagatingExecutorService(mockExecutorService, mockSpanManager); + } + + @After + public void verifyMocks() { + verifyNoMoreInteractions(mockExecutorService, mockSpanManager, mockSpan); + } + + @Test + public void testExecuteRunnable() { + Runnable runnable = mock(Runnable.class); + + service.execute(runnable); + + verify(mockSpanManager).currentSpan(); // current span must be propagated + verify(mockExecutorService).execute(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan + } + + @Test + public void testSubmitRunnable() { + Runnable runnable = mock(Runnable.class); + Future future = mock(Future.class); + when(mockExecutorService.submit(any(Runnable.class))).thenReturn(future); + + assertThat(service.submit(runnable), is(sameInstance(future))); + + verify(mockSpanManager).currentSpan(); // current span must be propagated + verify(mockExecutorService).submit(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan + } + + @Test + public void testSubmitCallable() { + Callable callable = mock(Callable.class); + Future future = mock(Future.class); + when(mockExecutorService.submit(any(Callable.class))).thenReturn(future); + + assertThat(service.submit(callable), is(sameInstance(future))); + + verify(mockSpanManager).currentSpan(); // current span must be propagated + verify(mockExecutorService).submit(any(CallableWithManagedSpan.class)); // into CallableWithManagedSpan + } + + @Test + public void testInvokeAll() throws InterruptedException { + Collection> callables = Arrays.>asList(mock(Callable.class), mock(Callable.class)); + List> futures = mock(List.class); + when(mockExecutorService.invokeAll((Collection>) anyCollection())).thenReturn(futures); + + assertThat(service.invokeAll(callables), is(sameInstance(futures))); + + verify(mockSpanManager, times(1)).currentSpan(); // current span must be obtained once + verify(mockExecutorService).invokeAll(anyCollection()); + } + + @Test + public void testConstructorWithoutDelegate() { + try { + new SpanPropagatingExecutorService(null, mock(SpanManager.class)); + fail("Exception with message expected."); + } catch (RuntimeException expected) { + assertThat(expected.getMessage(), is(Matchers.notNullValue())); + } + } + + @Test + public void testConstructorWithoutSpanManager() { + try { + new SpanPropagatingExecutorService(mock(ExecutorService.class), null); + fail("Exception with message expected."); + } catch (RuntimeException expected) { + assertThat(expected.getMessage(), is(Matchers.notNullValue())); + } + } + +} diff --git a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java index 0e523ab..1f6f4d8 100644 --- a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java +++ b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java @@ -1,85 +1,150 @@ package io.opentracing.contrib.spanmanager.concurrent; +import io.opentracing.NoopSpan; import io.opentracing.Span; +import io.opentracing.contrib.spanmanager.DefaultSpanManager; import io.opentracing.contrib.spanmanager.SpanManager; -import org.hamcrest.Matchers; +import io.opentracing.contrib.spanmanager.SpanManager.ManagedSpan; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.*; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.mock; public class SpanPropagatingExecutorServiceTest { - ExecutorService mockExecutorService; - SpanManager mockSpanManager; - Span mockSpan; + static final ExecutorService threadpool = Executors.newCachedThreadPool(); + static final SpanManager spanManager = DefaultSpanManager.getInstance(); - SpanPropagatingExecutorService service; + SpanPropagatingExecutorService subject; @Before public void setUp() { - mockExecutorService = mock(ExecutorService.class); - mockSpanManager = mock(SpanManager.class); - mockSpan = mock(Span.class); - when(mockSpanManager.currentSpan()).thenReturn(mockSpan); - - service = new SpanPropagatingExecutorService(mockExecutorService, mockSpanManager); + subject = new SpanPropagatingExecutorService(threadpool, spanManager); + spanManager.clear(); } @After - public void verifyMocks() { - verifyNoMoreInteractions(mockExecutorService, mockSpanManager, mockSpan); + public void tearDown() { + spanManager.clear(); + } + + @AfterClass + public static void shutdownThreadpool() { + assertThat(threadpool.shutdownNow(), equalTo(Collections.emptyList())); } @Test - public void testExecuteRunnable() { - Runnable runnable = mock(Runnable.class); + public void testExecuteRunnable() throws ExecutionException, InterruptedException { + CurrentSpanRunnable runnable = new CurrentSpanRunnable(); + ManagedSpan callerManagedSpan = spanManager.manage(mock(Span.class)); + try { - service.execute(runnable); + // Execute runnable and wait for completion. + FutureTask future = new FutureTask(runnable, null); + subject.execute(future); + future.get(); - verify(mockSpanManager).currentSpan(); // current span must be propagated - verify(mockExecutorService).execute(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan - verifyNoMoreInteractions(runnable); + assertThat("Current span in thread", runnable.span, is(sameInstance(callerManagedSpan.getSpan()))); + } finally { + callerManagedSpan.release(); + } } @Test - public void testSubmitRunnable() { - Runnable runnable = mock(Runnable.class); - Future future = mock(Future.class); - when(mockExecutorService.submit(any(Runnable.class))).thenReturn(future); + public void testSubmitRunnable() throws ExecutionException, InterruptedException { + CurrentSpanRunnable runnable = new CurrentSpanRunnable(); + ManagedSpan callerManagedSpan = spanManager.manage(mock(Span.class)); + try { - assertThat(service.submit(runnable), is(sameInstance(future))); + subject.submit(runnable).get(); // submit and block. + assertThat("Current span in thread", runnable.span, is(sameInstance(callerManagedSpan.getSpan()))); - verify(mockSpanManager).currentSpan(); // current span must be propagated. - verify(mockExecutorService).submit(any(RunnableWithManagedSpan.class)); // into RunnableWithManagedSpan - verifyNoMoreInteractions(runnable, future); + } finally { + callerManagedSpan.release(); + } } @Test - public void testConstructorWithoutDelegate() { + public void testSubmitCallable() throws ExecutionException, InterruptedException { + Callable callable = new CurrentSpanCallable(); + ManagedSpan callerManagedSpan = spanManager.manage(mock(Span.class)); try { - new SpanPropagatingExecutorService(null, mock(SpanManager.class)); - fail("Exception with message expected."); - } catch (RuntimeException expected) { - assertThat(expected.getMessage(), is(Matchers.notNullValue())); + + Future threadSpan = subject.submit(callable); + assertThat("Current span in thread", threadSpan.get(), is(sameInstance(callerManagedSpan.getSpan()))); + + } finally { + callerManagedSpan.release(); } } @Test - public void testConstructorWithoutSpanManager() { + public void testInvokeAll() throws ExecutionException, InterruptedException { + Collection> callables = Arrays.>asList( + new CurrentSpanCallable(), new CurrentSpanCallable(), new CurrentSpanCallable()); + ManagedSpan callerManagedSpan = spanManager.manage(mock(Span.class)); try { - new SpanPropagatingExecutorService(mock(ExecutorService.class), null); - fail("Exception with message expected."); - } catch (RuntimeException expected) { - assertThat(expected.getMessage(), is(Matchers.notNullValue())); + + List> futures = subject.invokeAll(callables); + assertThat("Futures", futures, hasSize(equalTo(callables.size()))); + for (Future threadSpan : futures) { + assertThat("Current span in thread", threadSpan.get(), is(sameInstance(callerManagedSpan.getSpan()))); + } + + } finally { + callerManagedSpan.release(); + } + } + + + @Test + public void testExecuteRunnableWithoutCurrentSpan() throws ExecutionException, InterruptedException { + CurrentSpanRunnable runnable = new CurrentSpanRunnable(); + + // Execute runnable and wait for completion. + FutureTask future = new FutureTask(runnable, null); + subject.execute(future); + future.get(); + + assertThat("Current span in thread", runnable.span, allOf(notNullValue(), instanceOf(NoopSpan.class))); + } + + @Test + public void testSubmitRunnableWithoutCurrentSpan() throws ExecutionException, InterruptedException { + CurrentSpanRunnable runnable = new CurrentSpanRunnable(); + subject.submit(runnable).get(); // submit and block. + assertThat("Current span in thread", runnable.span, allOf(notNullValue(), instanceOf(NoopSpan.class))); + } + + @Test + public void testSubmitCallableWithoutCurrentSpan() throws ExecutionException, InterruptedException { + Future threadSpan = subject.submit(new CurrentSpanCallable()); + assertThat("Current span in thread", threadSpan.get(), allOf(notNullValue(), instanceOf(NoopSpan.class))); + } + + static class CurrentSpanRunnable implements Runnable { + Span span = null; + + @Override + public void run() { + span = spanManager.currentSpan(); + } + } + + static class CurrentSpanCallable implements Callable { + @Override + public Span call() { + return spanManager.currentSpan(); } } From a0b5ccda2a501fa86844e9985aa114bef08f805d Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Tue, 10 Jan 2017 12:57:04 +0100 Subject: [PATCH 36/51] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4076c41..f4f4b80 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# java-spanmanager +# opentracing-java-spanmanager + +Warning: This library is still a work in progress! This library provides a way to manage spans and propagate them to other threads. From 41efdcad1d06ad6660f949a7e547072fa6b731fa Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 11 Jan 2017 06:56:06 +0100 Subject: [PATCH 37/51] Manual propagation clarification. --- README.md | 130 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index f4f4b80..c1d9926 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,66 @@ This convenience `Tracer` automates managing the _current span_: ## Examples +### Manually propagating any Span into a background thread + +To propagate a `Span` into a new `Thread`, the _currentSpan_ from the caller must be +remembered by the `Runnable`: + +```java + class ExampleRunnable implements Runnable { + private final SpanManager spanManager; + private final Span currentSpanFromCallingThread; + + ExampleRunnable(SpanManager spanManager) { + this(spanManager, NoopSpan.INSTANCE); + } + + private ExampleRunnable(SpanManager spanManager, Span currentSpanFromCallingThread) { + this.spanManager = spanManager; + this.currentSpanFromCallingThread = currentSpanFromCallingThread; + } + + ExampleRunnable withCurrentSpan() { + return new ExampleRunnable(spanManager, spanManager.currentSpan()); + } + + @Override + public void run() { + try (ManagedSpan parent = spanManager.manage(currentSpanFromCallingThread)) { // propagate currentSpan() + + // Any background code that requires tracing + // and may use spanManager.currentSpan(). + + } // parent.release() restores spanManager.currentSpan() to NoopSpan in new thread. + } + } +``` + +Then the application can propagate this _currentSpan_ into background threads: + +```java + class App { + public static void main(String... args) throws InterruptedException { + Config config = ...; + Tracer tracer = config.getTracer(); + SpanManager spanManager = config.getSpanManager(); + ExampleRunnable runnable = new ExampleRunnable(spanManager); + + try (Span appSpan = tracer.buildSpan("main").start(); // start appSpan + ManagedSpan managed = spanManager.manage(appSpan)) { // set currentSpan() to appSpan + + Thread example = new Thread(runnable.withCurrentSpan()); + example.start(); + example.join(); + + } // managed.release() + appSpan.finish() + + System.exit(0); + } + } + +``` + ### Threadpool that propagates SpanManager.currentSpan() into threads ```java @@ -88,6 +148,17 @@ This convenience `Tracer` automates managing the _current span_: ### Propagating threadpool with 'ManagedSpan' Tracer +When starting a new span and making it the _currentSpan_, the manual example above used: +```java + try (Span span = tracer.buildSpan("main").start(); // start span + ManagedSpan managed = spanManager.manage(span)) { // set currentSpan() to span + // ...traced block of code... + } +``` + +The `ManagedSpanTracer` automatically makes any started span the active span +and also releases it when the span is finished: + ```java class Caller { @@ -96,7 +167,7 @@ This convenience `Tracer` automates managing the _current span_: ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { - try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent === spanManager.currentSpan() + try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == spanManager.currentSpan() // Scheduling the traced call: Future result = propagatingThreadpool.submit(new TracedCall()); @@ -107,60 +178,3 @@ This convenience `Tracer` automates managing the _current span_: ``` -### Manually propagating any Span into a background thread - -To propagate a `Span` into a new `Thread` can be accomplished as follows: - -```java - - class ExampleThread extends Thread { - private SpanManager spanManager; - private Span propagatedSpan; - - ExampleThread(SpanManager spanManager) { - this.spanManager = spanManager; - this.propagatedSpan = spanManager.currentSpan(); // from calling thread. - } - - @Override - public void run() { - try (ManagedSpan parent = spanManager.manage(propagatedSpan)) { // make currentSpan() === propagatedSpan - - internalCode(); // any code that may require spanManager.currentSpan(); - - } // spanManager.currentSpan() is restored (NoopSpan in new thread). - } - - private void internalCode() { - // some code or library starting a new child-span from the 'current' span: - Span currentSpan = spanManager.currentSpan(); - try (Span newSpan = tracer.buildSpan("someOperation").asChildOf(currentSpan.context()).start()) { - - // ...traced as child of the propagated span... - - } // newSpan is finished. - } - - } - - class App { - public static void main(String... args) throws InterruptedException { - Config config = ...; - Tracer tracer = config.getTracer(); - SpanManager spanManager = config.getSpanManager(); // or DefaultSpanManager.getInstance(); - - // Start an outer span: - try (Span appSpan = tracer.buildSpan("main").start(); - ManagedSpan managed = spanManager.manage(appSpan)) { - - Thread example = new ExampleThread(spanManager); - example.start(); - example.join(); - - } - - System.exit(0); - } - } - -``` From 9056af4d0f7c743986ad1d34a8394f6e5c52c934 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Wed, 11 Jan 2017 08:18:15 +0100 Subject: [PATCH 38/51] Added asynchronous example with separate request/response filters. --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c1d9926..2b54973 100644 --- a/README.md +++ b/README.md @@ -122,10 +122,8 @@ Then the application can propagate this _currentSpan_ into background threads: @Override public String call() { - Span currentSpan = spanManager.currentSpan(); - try (Span newSpan = tracer.buildSpan("someCall").asChildOf(currentSpan.context()).start()) { - return "New span: " + newSpan + ", parent: " + parent; - } + Span currentSpan = spanManager.currentSpan(); // The propagated currentSpan from the scheduling thread + // ... } } @@ -156,11 +154,10 @@ When starting a new span and making it the _currentSpan_, the manual example abo } ``` -The `ManagedSpanTracer` automatically makes any started span the active span -and also releases it when the span is finished: +The `ManagedSpanTracer` automatically makes every started span the active span. +It also releases it again when the span is finished: ```java - class Caller { SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); Tracer tracer = new ManagedSpanTracer(anyTracer(), spanManager); @@ -175,6 +172,44 @@ and also releases it when the span is finished: } // parent.finish() + ((ManagedSpan) parent).release() // Performed by ManagedSpanTracer } } +``` + +### Example with asynchronous request / response filters + +When asynchronous processing is handled by separate request/response filters, +a `try-with-resources` code block is insufficient. + +Existing filters that start / finish new spans asynchronously can simply +be supplied with the `ManagedSpanTracer` around the existing tracer. +This sets the _currentSpan_ from the request filter +and calls `release()` automatically from the response filter +when the existing filter finishes the span. +An example would be using the [opentracing jaxrs filters](https://github.com/opentracing-contrib/java-jaxrs) +in combination with the ManagedSpanTracer: +```java + // Add example when opentracing jaxrs library stabilizes +``` +Alternatively, the following hypothetic filter pair could be used on an asynchronous server: +Handling the request: +```java + final SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); + void onRequest(RequestContext reqCtx) { + Span span = ... // either obtain Span from previous filter or start from the request + ManagedSpan managedSpan = spanManager.manage(span); // span is now activeSpan. + reqCtx.put(SOMEKEY, managedSpan); + } ``` +For the response: +```java + final SpanManager spanManager = ... + void onResponse(RequestContext reqCtx, ResponseContext resCtx) { + spanManager.clear(); // Clear stack containing the currentSpan if this is a boundary-filter + // or: + // ManagedSpan managedSpan = reqCtx.get(SOMEKEY); + // managedSpan.release(); + + // If the corresponding request filter starts a span, don't forget to call span.finish() here! + } +``` From e8f3614cded47e52459a30a56cf99cc10b139bc0 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 12 Jan 2017 21:14:30 +0100 Subject: [PATCH 39/51] Set maven groupId to io.opentracing.contrib --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 32be982..85b5eef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 - io.opentracing.contrib.spanmanager + io.opentracing.contrib opentracing-spanmanager 0.0.1-SNAPSHOT jar From 41643e3bbe6d2e5b9265366a84d20a62c6e1eae6 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 12 Jan 2017 21:26:30 +0100 Subject: [PATCH 40/51] Add maven wrapper. --- .mvn/wrapper/.gitignore | 1 + .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49519 bytes .mvn/wrapper/maven-wrapper.properties | 1 + mvnw | 234 ++++++++++++++++++++++++++ mvnw.cmd | 145 ++++++++++++++++ pom.xml | 51 ++++-- 6 files changed, 419 insertions(+), 13 deletions(-) create mode 100644 .mvn/wrapper/.gitignore create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100755 mvnw create mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/.gitignore b/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..0781905 --- /dev/null +++ b/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +!maven-wrapper.jar diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c6feb8bb6f76f2553e266ff8bf8867105154237e GIT binary patch literal 49519 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%0A$pwhjXzyA0ZzErH{1@M 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 literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..6637ced --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..fc7efd1 --- /dev/null +++ b/mvnw @@ -0,0 +1,234 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS + diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..0010480 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 85b5eef..574ae0b 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,19 @@ + + UTF-8 + UTF-8 + 1.6 + + 0.20.7 + + 0.3.3 + 2.5.1 + 2.4 + 2.10.3 + + Sjoerd Talsma @@ -40,13 +53,6 @@ scm:git:git@github.com:opentracing-contrib/java-activespan.git - - UTF-8 - UTF-8 - 1.6 - 0.20.7 - - io.opentracing @@ -86,11 +92,34 @@ + + + + + io.takari + maven + ${maven-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + - org.apache.maven.plugins maven-compiler-plugin - 2.5.1 ${build.java.version} ${build.java.version} @@ -99,9 +128,7 @@ - org.apache.maven.plugins maven-source-plugin - 2.4 attach-sources @@ -112,9 +139,7 @@ - org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 generate-javadoc From 9341e8beb095d44f560fac43f988c8a5dc00f9f3 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 11:12:47 +0100 Subject: [PATCH 41/51] Add license plugin. --- pom.xml | 52 +++++++++++++++++++ src/etc/header.txt | 11 ++++ .../spanmanager/DefaultSpanManager.java | 13 +++++ .../contrib/spanmanager/SpanManager.java | 13 +++++ .../concurrent/CallableWithManagedSpan.java | 13 +++++ .../concurrent/RunnableWithManagedSpan.java | 13 +++++ .../SpanPropagatingExecutorService.java | 13 +++++ .../concurrent/SpanPropagatingExecutors.java | 13 +++++ .../tracer/AutoReleasingManagedSpan.java | 13 +++++ .../tracer/ManagedSpanBuilder.java | 13 +++++ .../spanmanager/tracer/ManagedSpanTracer.java | 13 +++++ .../spanmanager/DefaultSpanManagerTest.java | 13 +++++ ...panPropagatingExecutorServiceMockTest.java | 13 +++++ .../SpanPropagatingExecutorServiceTest.java | 13 +++++ 14 files changed, 219 insertions(+) create mode 100644 src/etc/header.txt diff --git a/pom.xml b/pom.xml index 574ae0b..b1a7004 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,19 @@ + @@ -36,6 +51,7 @@ 2.5.1 2.4 2.10.3 + 2.11 @@ -149,6 +165,42 @@ + + com.mycila + license-maven-plugin + ${license-maven-plugin.version} + +

    ${project.basedir}/src/etc/header.txt
    + + .travis.yml + .gitignore + .mvn/** + mvnw* + etc/header.txt + **/.idea/** + LICENSE + **/*.md + src/test/resources/** + src/main/resources/** + + true + + + + com.mycila + license-maven-plugin-git + ${license-maven-plugin.version} + + + + + + check + + compile + + + diff --git a/src/etc/header.txt b/src/etc/header.txt new file mode 100644 index 0000000..a9328a1 --- /dev/null +++ b/src/etc/header.txt @@ -0,0 +1,11 @@ +Copyright ${license.git.copyrightYears} The OpenTracing 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. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index 0578909..80256cf 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager; import io.opentracing.NoopSpan; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index 85857e5..0aeeed6 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager; import io.opentracing.Span; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java index b206c9c..561ac0b 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/CallableWithManagedSpan.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java index 8610c43..d52cb1b 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/RunnableWithManagedSpan.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index fb2f37a..6b3019a 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index 32b51e4..2595473 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.contrib.spanmanager.SpanManager; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java index e2210f1..91b216a 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.tracer; import io.opentracing.Span; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index ead008b..97f6c09 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.tracer; import io.opentracing.References; diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index 993c961..19be2c5 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.tracer; import io.opentracing.Span; diff --git a/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java index 5e732be..e6506a0 100644 --- a/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java +++ b/src/test/java/io/opentracing/contrib/spanmanager/DefaultSpanManagerTest.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager; import io.opentracing.NoopSpan; diff --git a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java index d1e2eda..ec0cb6b 100644 --- a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java +++ b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceMockTest.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.Span; diff --git a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java index 1f6f4d8..54d0141 100644 --- a/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java +++ b/src/test/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorServiceTest.java @@ -1,3 +1,16 @@ +/** + * Copyright 2017 The OpenTracing 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.opentracing.contrib.spanmanager.concurrent; import io.opentracing.NoopSpan; From df5e7acc517b777f915c1fc0946a777fe062cc4f Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 12:10:46 +0100 Subject: [PATCH 42/51] Add release + centralsync plugins for distribution. --- pom.xml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pom.xml b/pom.xml index b1a7004..a0cdf94 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,8 @@ 2.4 2.10.3 2.11 + 2.5.3 + 0.1.0 @@ -131,6 +133,16 @@ maven-javadoc-plugin ${maven-javadoc-plugin.version} + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + + io.zipkin.centralsync-maven-plugin + centralsync-maven-plugin + ${centralsync-maven-plugin.version} + @@ -201,6 +213,24 @@ + + maven-release-plugin + + false + release + true + @{project.version} + + + + io.zipkin.centralsync-maven-plugin + centralsync-maven-plugin + + opentracing + maven + ${project.artifactId} + + From 079ee36f703cb325cb713410ae373365ff605c26 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 12:18:50 +0100 Subject: [PATCH 43/51] Add distributionManagement + issueManagement to pom. --- pom.xml | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a0cdf94..c12198f 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,9 @@ 1.6 0.20.7 + 4.12 + 1.3 + 1.10.19 0.3.3 2.5.1 @@ -71,6 +74,22 @@ scm:git:git@github.com:opentracing-contrib/java-activespan.git + + Github + https://github.com/opentracing-contrib/java-activespan/issues + + + + + bintray + https://api.bintray.com/maven/opentracing/maven/java-activespan/;publish=1 + + + jfrog-snapshots + http://oss.jfrog.org/artifactory/oss-snapshot-local + + + io.opentracing @@ -92,19 +111,19 @@ junit junit - 4.12 + ${junit.version} test org.hamcrest hamcrest-library - 1.3 + ${hamcrest.version} test org.mockito mockito-all - 1.10.19 + ${mockito.version} test @@ -228,7 +247,7 @@ opentracing maven - ${project.artifactId} + java-activespan From 699fe8f8922c3eb98aa2203fb21a6b111877ae54 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 13:36:15 +0100 Subject: [PATCH 44/51] Add maven settings and travis files. Plus CONTRIBUTING.md and RELEASE.md documents. --- .settings.xml | 44 ++++++++++++++++ .travis.yml | 53 +++++++++++++++++++ CONTRIBUTING.md | 31 +++++++++++ RELEASE.md | 6 +++ travis/publish.sh | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 265 insertions(+) create mode 100644 .settings.xml create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 RELEASE.md create mode 100755 travis/publish.sh diff --git a/.settings.xml b/.settings.xml new file mode 100644 index 0000000..a4250cc --- /dev/null +++ b/.settings.xml @@ -0,0 +1,44 @@ + + + + + + sonatype + ${env.SONATYPE_USER} + ${env.SONATYPE_PASSWORD} + + + bintray + ${env.BINTRAY_USER} + ${env.BINTRAY_KEY} + + + jfrog-snapshots + ${env.BINTRAY_USER} + ${env.BINTRAY_KEY} + + + github.com + ${env.GH_USER} + ${env.GH_TOKEN} + + + + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..136cfbe --- /dev/null +++ b/.travis.yml @@ -0,0 +1,53 @@ +# Run `travis lint` when changing this file to avoid breaking the build. +# Default JDK is really old: 1.8.0_31; Trusty's is less old: 1.8.0_51 +# https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments +sudo: false +dist: trusty + +cache: + directories: + - $HOME/.m2 + +language: java + +jdk: + - oraclejdk8 + +addons: + apt: + packages: + - oracle-java8-installer + +before_install: + # allocate commits to CI, not the owner of the deploy key + - git config user.name "opentracingci" + - git config user.email "opentracingci+opentracing@googlegroups.com" + # setup https authentication credentials, used by ./mvnw release:prepare + - git config credential.helper "store --file=.git/credentials" + - echo "https://$GH_TOKEN:@github.com" > .git/credentials + +install: + # Override default travis to use the maven wrapper + - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + +script: + - ./travis/publish.sh + +# Don't build release tags. This avoids publish conflicts because the version commit exists both on master and the release tag. +# See https://github.com/travis-ci/travis-ci/issues/1532 +branches: + except: + - /^[0-9]/ + +env: + global: + # Ex. travis encrypt "BINTRAY_USER=your_github_account" -r opentracing-contrib/java-activespan + - secure: "ELx4kCmDRcIKbubq0yuncmQUI9VpEDs2TDMXxMh1XCI/Aq8a8d1qRIB1mGhrdQGNaLQ9XnPPrkPCXUmzaXGDknyKgeuQFq0kyr+Sr5cqdfUgtrlnEWdU//TjE0Rw+jbvUG0XCRdloRgw7Ce0kn83I90c9g66f0gx81mdShUN6iD0O0ovRoBvHcfcjOwqRci2wLBOzkR1H56dOsVEcoiMKNs7BISge+CpzKwgcmZw9BDdXMK0rbKktYLgQFcyKbYzgNm1g7e8T8/PdfxyissArKzjDCM4o/BmSHeLhV78VMMMzN2LDhNW49Y6j9JJ5bVYq1YY/FCNsv1P06MB0TrXKp5/9jqXVF6hLtMv4V9ODC3sPSQk7vBEBoRf3y/eAL+50jqYdphVA17bBbps3dsZst4u8VWF39LGl7WAafcrbks6JTDe6Xjtf1J9wEpiMOMr+qLpIDIY6FUTJDtcsZbAMQNYfPF7pEyjCuW9ePb8RWpxMJOpK5GyL02u2iqgVo+AE3EYfQ4gkdr06AMBmGWeFMvTPknM8JDPRPxI8s7vccKffNO0UFY57w1odRvy1Wr76i1KWmZD/sQc44F67BuLZPSNNbT1A8IKoJ9DQkSMOtc8q88VeBVeRghkw9BlAQDJzntzzfsos0YBVlebFbU3FxTNEWXRcNlx/pLfm90XCGc=" + # Ex. travis encrypt BINTRAY_KEY=xxx-https://bintray.com/profile/edit-xxx --add + - secure: "V2OUIEazbv6mzb8vXZ+vVtSuRl7GBPDMeZcRYCwK6z8edTpc9GQa0sDPWhOEHEk4j7Cx9P+RK27CN0mhe1raT+VDJ9sSXv47sUvfJhZbNi9syfr6bKvYHKplVll1/aq2LzI63D1su+r76jQPMf9nl+rtdjD0L0akxy0YJeBUpL6I0KkJ9RfBldF3jE2ybgogyFA0RFFG0Z7r6c4sLlgMkBtNcfVdR0l4fBYk0jQYPYJfuDmS61gixh65XJjk25EicrTvtyOUoYKpsVfhox2L5ZGbIVRnkrh9A6UsuDdCW5Xl/8KvZo2FK3o7eItSeI4uJanfqsmYp7zqjOdyt4pKb2W+w07xdgtxe4CjTXhkHth/J1vFWPxSpJob9NVMkIDi7Uu6xzrwXzHxaJc6olQ6KgbB6dntuoFzK8M66+VCIq9mrSyp1RYuhMFeaixk3ftc4UlDATrV0alIlohar8NT2NnHzO4JQ83Lx7zfUWJ+E4pb9/jrvEgkRqJHJCJGANrikH1FwMDStN1MattnecaC0N8g6oKblrzMeuH1DFESHcKOROpgmwhkOw5mNckL0H/zP9rimVvCGsvkRZI/NNuUDooMqWGdUbhfiXYoXQF/cMdII5VjttAN+fNQRUTXmscJve9L4KfRUhqhb1OVZn5vGmWg2DeI3EIWCuZy6SInQoA=" + # Ex. travis encrypt GH_TOKEN=XXX-https://github.com/settings/tokens-XXX --add + - secure: "PcqxTyKxGXIxSKpxQ/OKpx1xXKKahoOvauy2BhZ8bkFromcF8vl3+ypiXwfhBMBaIiPvxAM5tonxQ3v2DxVj/ayUmWD3pBjK7TCz997/KrEp8xjM28Q1whP+DpLxx+sMgtOes5sK5CBEG13oJwxGIXVzxXRWIVDxZEX2RjbJuPimUnr7Sc9cHr4+aNKqGMJrEFPjqXMvzKG1xGJrUGt4XBfmq5T52Gc3AtfbOMvhtyG8YheBHocKKL5M5rKi1kk37iTIPl9a8q07+8h8Ga8+CW00ZmDQxcrZeNpHUe5dXAm3GdpsEJNGyLskAIPGctRRrsBLLoDwfmnZh7bKBLpqMUpnbKO/x+qNKhDoEulw8H8lj/CuJ5YzRg2kcEcf3VhiHo0rE2KemlmsF3J5NdYoGjPb0vJd02PSAk+Ay7KM3POJbFAtHYfxn8px9ufAE0ef/H9pDk5sFqCJiB7Agh/V6Grx5k1OBrXRx9UqnFgSpE9OkVCf2JfRBaw8M10JMeJFHLcpAe+nSpUeKdA1Yr71Eiheukb+OWW17qnyz+kqqHVo5w4REfu0P2fDNIO0Y41RABvVDqrxGyvs0nN3eiQXvNk9hMRxb7SzdbNQ4d/LtAreSHXbXqro6An7WvqKrg17MMmHMqCTwAZecmymjqeAHsUPU1+TLzobRV5UXObHAzM=" + # Ex. travis encrypt SONATYPE_USER=your_sonatype_account + - secure: "MK/wBqkAOGTjSyyKSUNdP3QFGAACVHeBjYtQrDB/Y3GQDQZw6CKk2KZMVSTyr9E5ZAzglZEOAIWxTWgUjmqp0FYnUSsinmMsrk/NZx8RztozMFKyMHhdzs9OhyjbzGfM2PfTf+X3kB9LVQaKIUesk5a1Fb67YW76+oIOktP/BOTWDTfR50sF1JrkTdJ/F17V2eD5L/bLSGtMW9gIMPS15kSUluCesbO/eSYGRRANsvXrJjFmk4R8kpmHXLqKwCXAuc6WcvmdUeeIIB8vJHnSGTvvmpGaHz81wJeI0DSQdFVVTJTv3n/ed1vm63qgEs5pvzsU0bFTIscby0Gidts2zMOt6FW/qIMPFFuJV5uB5mg+zdRjO0eHZWsXY3+TxGgGZFM6pcQckzbEWYzT1apfqIbG62KDIIzLXKUnA4spzwpHeR015xq9FfHAmc296Esx3Uv2Q5c9ct2kDbMzD8+C4joWhKcW3xQHMIx1EdX2uUB3LOzKXxNlrCA7/2KC3y472CrLUbxWziHhq1FIkaDxbwiuWMCP53FZv3+N5aVJ3VTEE0zyBAIRP0OTTSe/4f51dcV8MA2j2hO2aIDUvitIixV8c/RjBn8aUoILyUHgjPusx1xsy/oEq2FnAx3WwWEzC28JaPFczxRD7Hi6+8Nfxv2V+mKZ0oeUIiT74kFE9vY=" + # Ex. travis encrypt SONATYPE_PASSWORD=your_sonatype_password + - secure: "bR2AZ3+wDpLlb99YKD3vWMmmGUdo1UCY6rEL03Nghdodhh+ZfKoVzlkGSzRTRF1dEWFwbz/8f+xd/vMtCqB8LvexFgs+2DCHHfExDvME1vT1vCdXIWKFjk6oQZoPONvUx4guj3a8pZ1a0WbNrQ3/hUwFsNLeHA1aXMtakkwAmmR0dC4o4leFlN4Boh9Lil7jz5MfYcV72Rumtt5GP3EaSDw+OPa2GRiuO4/m+ftlxBNwsYteaRqHSNMOmXs2Q8ZjIcYk59S98IPdeZLff6G5E/PXgqYrZiHzb/TBiuyHhHIYmufzI0os2DHjwhwam2s8oMgumwH0z4E3Id2dvhQUPN6+Z0mreXEoaSE7ycy6baig/oVUenNWVaXg3S00Cl+kGrZwy1v6lotIPIBhvwDTR+c2oZDnnRDYhgNevHnOoyaEar3vbfqt/vdm9PqR5L3IDPQ007cfIcy/JH97b0113jt1CCi1WVGPKWURWhNVYnwkfh0KtEgwhcv0OQadE1jgoIw5AsALEgYhZqAn9f6T5BrKdocSTeMwHP2U/uUe5f78glTk3nNwQd7G3jz5+BZ8jYOcc0GsrVT+L1MJCgFX4p44/Vm1JTXnpZCtTVzq8b5ZfuQqO0Nfln7jblhxPG+cFdU7qwvbGXDWR6T+NzhDBGVb6dqW8G3E2hZytMibq2Y=" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..86023d8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to opentracing-contrib / java-activespan + +If you would like to contribute code you can do so through GitHub by forking the repository +and sending a pull request (on a branch other than `master` or `gh-pages`). + + +## License + +By contributing your code, you agree to license your contribution under the terms of the APLv2: +https://github.com/opentracing-contrib/java-activespan/blob/master/LICENSE + +All files are released with the Apache 2.0 license. + +If you are adding a new file it should have a header like below. +This can be automatically added by running `./mvnw license:format`. + +``` +/** + * Copyright 2017 The OpenTracing 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. + */ + ``` diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..9fa4aa1 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,6 @@ +# OpenTracing Release Process + +This repo uses semantic versions. Please keep this in mind when choosing version numbers. + +For the up-to-date release process, please refer the +[release process from the OpenTracing Java API](https://github.com/opentracing/opentracing-java/blob/master/RELEASE.md). diff --git a/travis/publish.sh b/travis/publish.sh new file mode 100755 index 0000000..b6ef300 --- /dev/null +++ b/travis/publish.sh @@ -0,0 +1,131 @@ +# +# Copyright 2017 The OpenTracing 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. +# + +set -euo pipefail +set -x + +build_started_by_tag() { + if [ "${TRAVIS_TAG}" == "" ]; then + echo "[Publishing] This build was not started by a tag, publishing snapshot" + return 1 + else + echo "[Publishing] This build was started by the tag ${TRAVIS_TAG}, publishing release" + return 0 + fi +} + +is_pull_request() { + if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then + echo "[Not Publishing] This is a Pull Request" + return 0 + else + echo "[Publishing] This is not a Pull Request" + return 1 + fi +} + +is_travis_branch_master() { + if [ "${TRAVIS_BRANCH}" = master ]; then + echo "[Publishing] Travis branch is master" + return 0 + else + echo "[Not Publishing] Travis branch is not master" + return 1 + fi +} + +check_travis_branch_equals_travis_tag() { + #Weird comparison comparing branch to tag because when you 'git push --tags' + #the branch somehow becomes the tag value + #github issue: https://github.com/travis-ci/travis-ci/issues/1675 + if [ "${TRAVIS_BRANCH}" != "${TRAVIS_TAG}" ]; then + echo "Travis branch does not equal Travis tag, which it should, bailing out." + echo " github issue: https://github.com/travis-ci/travis-ci/issues/1675" + exit 1 + else + echo "[Publishing] Branch (${TRAVIS_BRANCH}) same as Tag (${TRAVIS_TAG})" + fi +} + +check_release_tag() { + tag="${TRAVIS_TAG}" + if [[ "$tag" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]]; then + echo "Build started by version tag $tag. During the release process tags like this" + echo "are created by the 'release' Maven plugin. Nothing to do here." + exit 0 + elif [[ ! "$tag" =~ ^release-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]]; then + echo "You must specify a tag of the format 'release-0.0.0' to release this project." + echo "The provided tag ${tag} doesn't match that. Aborting." + exit 1 + fi +} + +is_release_commit() { + project_version=$(./mvnw help:evaluate -N -Dexpression=project.version|grep -v '\[') + if [[ "$project_version" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]]; then + echo "Build started by release commit $project_version. Will synchronize to maven central." + return 0 + else + return 1 + fi +} + +release_version() { + echo "${TRAVIS_TAG}" | sed 's/^release-//' +} + +safe_checkout_master() { + # We need to be on a branch for release:perform to be able to create commits, and we want that branch to be master. + # But we also want to make sure that we build and release exactly the tagged version, so we verify that the remote + # master is where our tag is. + git checkout -B master + git fetch origin master:origin/master + commit_local_master="$(git show --pretty='format:%H' master)" + commit_remote_master="$(git show --pretty='format:%H' origin/master)" + if [ "$commit_local_master" != "$commit_remote_master" ]; then + echo "Master on remote 'origin' has commits since the version under release, aborting" + exit 1 + fi +} + +#---------------------- +# MAIN +#---------------------- + +if ! is_pull_request && build_started_by_tag; then + check_travis_branch_equals_travis_tag + check_release_tag +fi + +./mvnw install -nsu + +# If we are on a pull request, our only job is to run tests, which happened above via ./mvnw install +if is_pull_request; then + true +# If we are on master, we will deploy the latest snapshot or release version +# - If a release commit fails to deploy for a transient reason, delete the broken version from bintray and click rebuild +elif is_travis_branch_master; then + ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DskipTests deploy + + # If the deployment succeeded, sync it to Maven Central. Note: this needs to be done once per project, not module, hence -N + if is_release_commit; then + ./mvnw --batch-mode -s ./.settings.xml -nsu -N io.zipkin.centralsync-maven-plugin:centralsync-maven-plugin:sync + fi + +# If we are on a release tag, the following will update any version references and push a version tag for deployment. +elif build_started_by_tag; then + safe_checkout_master + ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DreleaseVersion="$(release_version)" -Darguments="-DskipTests" release:prepare +fi + From 52307295ce3eb5124cc0991a4dda097cd36069f0 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 14:01:01 +0100 Subject: [PATCH 45/51] Document the purpose of SpanManager. --- README.md | 9 ++++++++- .../io/opentracing/contrib/spanmanager/SpanManager.java | 7 +++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2b54973..65f4337 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,14 @@ This library provides a way to manage spans and propagate them to other threads. ## SpanManager -Interface that defines _current_ span management: +Defines _current span_ management. + +A SpanManager separates the creation of a `Span` from its use later on. +This relieves application developers from passing the current span around throughout their code. +Only tracing-related code will need access to a SpanManager reference. This can be provided just like any dependency. + +SpanManager provides the following methods: + 1. `manage(span)` makes the given span the _current_ managed span. Returns a `ManagedSpan` containing a `release()` method to later 'unmanage' the span with. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index 0aeeed6..3a1e4e6 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -18,8 +18,11 @@ import java.io.Closeable; /** - * Manager to {@link #manage(Span) manage} and {@link ManagedSpan#release() release} - * {@linkplain Span spans} and accessing the {@link #currentSpan() currently-managed span}. + * Defines {@linkplain #currentSpan() current span} management. + *

    + * A SpanManager separates the creation of a {@linkplain Span} from its use later on. + * This relieves application developers from passing the current span around throughout their code. + * Only tracing-related code will need access to a SpanManager reference. This can be provided just like any dependency. */ public interface SpanManager { From a3dc8a2a22176164e45ace22eac91f6943ccb5aa Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 15:26:38 +0100 Subject: [PATCH 46/51] Minor documentation improvement. --- README.md | 2 +- .../io/opentracing/contrib/spanmanager/DefaultSpanManager.java | 2 +- .../java/io/opentracing/contrib/spanmanager/SpanManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65f4337..195882d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Defines _current span_ management. A SpanManager separates the creation of a `Span` from its use later on. This relieves application developers from passing the current span around throughout their code. -Only tracing-related code will need access to a SpanManager reference. This can be provided just like any dependency. +Only tracing-related code will need access to a SpanManager reference, provided as an ordinary dependency. SpanManager provides the following methods: diff --git a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java index 80256cf..469adf9 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/DefaultSpanManager.java @@ -21,7 +21,7 @@ import java.util.logging.Logger; /** - * Default {@link SpanManager} implementation using {@link ThreadLocal} storage, + * Default {@link SpanManager} implementation using {@link ThreadLocal} storage * maintaining a stack-like structure of linked managed spans. *

    * The linked managed spans provide the following stack unwinding algorithm: diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index 3a1e4e6..ff9fbec 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -22,7 +22,7 @@ *

    * A SpanManager separates the creation of a {@linkplain Span} from its use later on. * This relieves application developers from passing the current span around throughout their code. - * Only tracing-related code will need access to a SpanManager reference. This can be provided just like any dependency. + * Only tracing-related code will need access to a SpanManager reference, provided as an ordinary dependency. */ public interface SpanManager { From 5b47e25d417b1effcf8dd42bffff3c447ef09a7b Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 15:50:40 +0100 Subject: [PATCH 47/51] Add CI + Maven shields. Also made the _work in progress_ caveat stand out more. --- README.md | 11 +++++++++-- pom.xml | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 195882d..3017cac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# opentracing-java-spanmanager +[![Build Status][ci-img]][ci] [![Released Version][maven-img]][maven] -Warning: This library is still a work in progress! +# Span manager for Java + +:heavy_exclamation_mark: **Warning:** This library is still a work in progress! This library provides a way to manage spans and propagate them to other threads. @@ -220,3 +222,8 @@ For the response: // If the corresponding request filter starts a span, don't forget to call span.finish() here! } ``` + + [ci-img]: https://img.shields.io/travis/opentracing-contrib/java-activespan/master.svg + [ci]: https://travis-ci.org/opentracing-contrib/java-activespan + [maven-img]: https://img.shields.io/maven-central/v/io.opentracing.contrib/java-spanmanager.svg + [maven]: http://search.maven.org/#search%7Cga%7C1%7Cjava-spanmanager diff --git a/pom.xml b/pom.xml index c12198f..af2d075 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ SpanManager library - Library for managing the currently active span and propagating it accross threads + Manager of current span and propagating it accross threads https://github.com/opentracing-contrib/java-activespan 2017 From 951cea7a5bd51088aa14e3f680079743aa0ee052 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 15:52:43 +0100 Subject: [PATCH 48/51] throughout -> through --- README.md | 2 +- .../java/io/opentracing/contrib/spanmanager/SpanManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3017cac..79695c3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This library provides a way to manage spans and propagate them to other threads. Defines _current span_ management. A SpanManager separates the creation of a `Span` from its use later on. -This relieves application developers from passing the current span around throughout their code. +This relieves application developers from passing the current span around through their code. Only tracing-related code will need access to a SpanManager reference, provided as an ordinary dependency. SpanManager provides the following methods: diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index ff9fbec..ecee34d 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -21,7 +21,7 @@ * Defines {@linkplain #currentSpan() current span} management. *

    * A SpanManager separates the creation of a {@linkplain Span} from its use later on. - * This relieves application developers from passing the current span around throughout their code. + * This relieves application developers from passing the current span around through their code. * Only tracing-related code will need access to a SpanManager reference, provided as an ordinary dependency. */ public interface SpanManager { From c80257008d5392660d61494ced06073cc4caf312 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Fri, 13 Jan 2017 16:54:32 +0100 Subject: [PATCH 49/51] Shorten line-length from example code. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 79695c3..fd5ef65 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ remembered by the `Runnable`: @Override public void run() { - try (ManagedSpan parent = spanManager.manage(currentSpanFromCallingThread)) { // propagate currentSpan() + try (ManagedSpan parent = spanManager.manage(currentSpanFromCallingThread)) { // Any background code that requires tracing // and may use spanManager.currentSpan(). @@ -109,7 +109,7 @@ Then the application can propagate this _currentSpan_ into background threads: ExampleRunnable runnable = new ExampleRunnable(spanManager); try (Span appSpan = tracer.buildSpan("main").start(); // start appSpan - ManagedSpan managed = spanManager.manage(appSpan)) { // set currentSpan() to appSpan + ManagedSpan managed = spanManager.manage(appSpan)) { // update currentSpan Thread example = new Thread(runnable.withCurrentSpan()); example.start(); @@ -131,21 +131,21 @@ Then the application can propagate this _currentSpan_ into background threads: @Override public String call() { - Span currentSpan = spanManager.currentSpan(); // The propagated currentSpan from the scheduling thread + Span currentSpan = spanManager.currentSpan(); // Propagated span from caller // ... } } class Caller { SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); - ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); + ExecutorService threadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { // ...code that sets the current Span somewhere: try (ManagedSpan current = spanManager.manage(someSpan)) { // scheduling the traced call: - Future result = propagatingThreadpool.submit(new TracedCall()); + Future result = threadpool.submit(new TracedCall()); } } @@ -170,15 +170,15 @@ It also releases it again when the span is finished: class Caller { SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); Tracer tracer = new ManagedSpanTracer(anyTracer(), spanManager); - ExecutorService propagatingThreadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); + ExecutorService threadpool = new SpanPropagatingExecutorService(anyThreadpool(), spanManager); void run() { - try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == spanManager.currentSpan() + try (Span parent = tracer.buildSpan("parentOperation").start()) { // parent == currentSpan // Scheduling the traced call: - Future result = propagatingThreadpool.submit(new TracedCall()); + Future result = threadpool.submit(new TracedCall()); - } // parent.finish() + ((ManagedSpan) parent).release() // Performed by ManagedSpanTracer + } // parent.finish() + ((ManagedSpan) parent).release() } } ``` From 8bba41c437ee69892a4acffbff5aa0ab0ba71e60 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Tue, 17 Jan 2017 21:25:34 +0100 Subject: [PATCH 50/51] Re-encrypt credentials for opentracing-contrib github org. --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 136cfbe..d2ba346 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,13 +41,13 @@ branches: env: global: - # Ex. travis encrypt "BINTRAY_USER=your_github_account" -r opentracing-contrib/java-activespan - - secure: "ELx4kCmDRcIKbubq0yuncmQUI9VpEDs2TDMXxMh1XCI/Aq8a8d1qRIB1mGhrdQGNaLQ9XnPPrkPCXUmzaXGDknyKgeuQFq0kyr+Sr5cqdfUgtrlnEWdU//TjE0Rw+jbvUG0XCRdloRgw7Ce0kn83I90c9g66f0gx81mdShUN6iD0O0ovRoBvHcfcjOwqRci2wLBOzkR1H56dOsVEcoiMKNs7BISge+CpzKwgcmZw9BDdXMK0rbKktYLgQFcyKbYzgNm1g7e8T8/PdfxyissArKzjDCM4o/BmSHeLhV78VMMMzN2LDhNW49Y6j9JJ5bVYq1YY/FCNsv1P06MB0TrXKp5/9jqXVF6hLtMv4V9ODC3sPSQk7vBEBoRf3y/eAL+50jqYdphVA17bBbps3dsZst4u8VWF39LGl7WAafcrbks6JTDe6Xjtf1J9wEpiMOMr+qLpIDIY6FUTJDtcsZbAMQNYfPF7pEyjCuW9ePb8RWpxMJOpK5GyL02u2iqgVo+AE3EYfQ4gkdr06AMBmGWeFMvTPknM8JDPRPxI8s7vccKffNO0UFY57w1odRvy1Wr76i1KWmZD/sQc44F67BuLZPSNNbT1A8IKoJ9DQkSMOtc8q88VeBVeRghkw9BlAQDJzntzzfsos0YBVlebFbU3FxTNEWXRcNlx/pLfm90XCGc=" + # Ex. travis encrypt -r opentracing-contrib/java-activespan BINTRAY_USER=your_github_account + - secure: "kUlDX2huODNAWdoTjOOzsthNsDxIU4DcNIXmdaXKAiOrASDHRldS6PLoZlEW55UEqsnEveOrWlm9DU9xEeznf8Rt3GU7PvCm8+LMRyWmkXbHhiF0dYA9yxSIpM9TeXhlqx0eYO3oNQ6U8YmIupOtfEHn39mkU7WZIwUqOKjKQCT9MNeBXPfzQPkXseCHtxBH4Axe6qng07eDWTY/k1IxGaL9F/k27371qlnS/xFLp6pSPQ990jrKX1xY5Mw68PFrqWJRwMiC0opotNzM69G+5av4yMwPakf4Sf0hZjm2+9voWmChecFVbG81NJK9caWfFH/BQqVLsvns28VIYfaM7bk2/LzpWg7/1XABPDfjulBU80vUOHvHXDaEj0VFng7NEO248OvbRfJRbtDv5+LheOjBiiJHCFIfH1hy4j1NCzLlI0yCS8/cycIvXI1VTqVFUGErL8wS7Sv5zh/E+97qTg08k+pLJTE4z/n5+Ehfsfp94snRwIQSrfRh/ccT97f7qNjlRfflYt2bFpMjNLXRpcrm34Y990c64Usr0ONdFN/hT2i5+K+kwshH4pYrnX9KzHVweVFAQt7bhyFPJjNrdKSOG2RgFDwRyAtcgNKx9oz5F4S7oe4dkd9SoSC0KB/nQxsBy+KBm1O4Camq7u2OgnByn1FIbavCxWHxGhOkOeE=" # Ex. travis encrypt BINTRAY_KEY=xxx-https://bintray.com/profile/edit-xxx --add - - secure: "V2OUIEazbv6mzb8vXZ+vVtSuRl7GBPDMeZcRYCwK6z8edTpc9GQa0sDPWhOEHEk4j7Cx9P+RK27CN0mhe1raT+VDJ9sSXv47sUvfJhZbNi9syfr6bKvYHKplVll1/aq2LzI63D1su+r76jQPMf9nl+rtdjD0L0akxy0YJeBUpL6I0KkJ9RfBldF3jE2ybgogyFA0RFFG0Z7r6c4sLlgMkBtNcfVdR0l4fBYk0jQYPYJfuDmS61gixh65XJjk25EicrTvtyOUoYKpsVfhox2L5ZGbIVRnkrh9A6UsuDdCW5Xl/8KvZo2FK3o7eItSeI4uJanfqsmYp7zqjOdyt4pKb2W+w07xdgtxe4CjTXhkHth/J1vFWPxSpJob9NVMkIDi7Uu6xzrwXzHxaJc6olQ6KgbB6dntuoFzK8M66+VCIq9mrSyp1RYuhMFeaixk3ftc4UlDATrV0alIlohar8NT2NnHzO4JQ83Lx7zfUWJ+E4pb9/jrvEgkRqJHJCJGANrikH1FwMDStN1MattnecaC0N8g6oKblrzMeuH1DFESHcKOROpgmwhkOw5mNckL0H/zP9rimVvCGsvkRZI/NNuUDooMqWGdUbhfiXYoXQF/cMdII5VjttAN+fNQRUTXmscJve9L4KfRUhqhb1OVZn5vGmWg2DeI3EIWCuZy6SInQoA=" + - secure: "HG1jFM22Tfj1LEhLEcMC5Y9xFajsqfRYYy2GRv3ZmDBP+7lxfevJEjoPE+xxBvUyZuouNKVfSLLduVm8JLhK4xOJUqI+H1xivntQH7fRU9u14XX8L2U5UJHNEp+yRx/nXzuTMUJqNZ75FSUBkVTRIQwAAi8PtIrtHef/jEgUUoZfR9vd3WbB85VWXoKxGr1yHa4sh9RDFKDG3JhnSTdbGhtX3mglTVdOP3rqjBszKsYlzDJjStwGuPZU7F3a1OcUZ86F3HQdIgr9QTE+JWVIF9AGq3GMA/1cBtdjAvYFpkZUmtMwMUZib2uykvLfwHxiQJqVlebB68EZSs+PRnPmOeNrkVx3USGFEUSHtTaoimeMG124nlHABhexgmBR6V+mksfXKESxiVuFn50kWGBKZAJUQ6CaNJQl8Qexl7BdVReJu2Gq6yHt6lcc1DotOgvpPAGic+uMqa8IRCaBUhPx2iuws2Q4Kx/3/qOOrIwiIysEq/CtIhNwjhSNQf/hkFX37IqXZ8EWyY771qCrCbwqvBVShO7Qywy4FMHAWRsfmTQYzHfo6STGKv1Yzit5ny2K/do+SgAhYhoT9L07B+/BzfHvB1VJ3Qv63PwQ9ANiyHyAIAR/SMagpb7CMNr1f1atEyCBiIRIZY7t/375/U9WvNVupj7eWYGw1Ocjfculywo=" # Ex. travis encrypt GH_TOKEN=XXX-https://github.com/settings/tokens-XXX --add - - secure: "PcqxTyKxGXIxSKpxQ/OKpx1xXKKahoOvauy2BhZ8bkFromcF8vl3+ypiXwfhBMBaIiPvxAM5tonxQ3v2DxVj/ayUmWD3pBjK7TCz997/KrEp8xjM28Q1whP+DpLxx+sMgtOes5sK5CBEG13oJwxGIXVzxXRWIVDxZEX2RjbJuPimUnr7Sc9cHr4+aNKqGMJrEFPjqXMvzKG1xGJrUGt4XBfmq5T52Gc3AtfbOMvhtyG8YheBHocKKL5M5rKi1kk37iTIPl9a8q07+8h8Ga8+CW00ZmDQxcrZeNpHUe5dXAm3GdpsEJNGyLskAIPGctRRrsBLLoDwfmnZh7bKBLpqMUpnbKO/x+qNKhDoEulw8H8lj/CuJ5YzRg2kcEcf3VhiHo0rE2KemlmsF3J5NdYoGjPb0vJd02PSAk+Ay7KM3POJbFAtHYfxn8px9ufAE0ef/H9pDk5sFqCJiB7Agh/V6Grx5k1OBrXRx9UqnFgSpE9OkVCf2JfRBaw8M10JMeJFHLcpAe+nSpUeKdA1Yr71Eiheukb+OWW17qnyz+kqqHVo5w4REfu0P2fDNIO0Y41RABvVDqrxGyvs0nN3eiQXvNk9hMRxb7SzdbNQ4d/LtAreSHXbXqro6An7WvqKrg17MMmHMqCTwAZecmymjqeAHsUPU1+TLzobRV5UXObHAzM=" + - secure: "X3b+6lVeFlHrZISNaVqyCZq04JQPmY0xv9+QQdzG5rRqWJdLUAV5kT7cNHccJ8gy83c5Zs72u/jwugIpqiQNHHzEkbF77CUwlb365LuatcHHR/KxVoO264rJLorW5UuSebzlZ0feUegaN7t5QQeEyp7bVWg9M6rNjBVocgvi4EFATDBTFxodri9Ktqr0EbTU89BXgxYdbanBwrNu78DDQFIlookYFOwe/8N9qPgjHvmLjt5BtOGNwWIhAmvjqPHdgkq6v/iyqhcjX19wcRGmS+Xk8sFFjeH68DTrNmA6lAuiDvMD8MhFuPeI6gM9X6cHMtubFccxiG6gsBbiN2T43kelSu6DgTilMWWg/BWKJI82ZhRPX2mT1YtQYlYm0VQ/GnrPyKs7pVzEzCXDg/feUa1DnBsu9x/oTt+MeaLUhY2PfDuSJthpUMLr+B9kOt+e1kXvUK1PR/ukYxveXgvA8eQpSW49Tm2/BXdl0Ig2ZBCD9INQO/ynpql6a5xcXpoiRyg1eq/g42vvJyC1/2pl0fS2YT4s1jkknTVaQJ0FUhzIdqCpnnBmpXVk+n6DGZraio9dfkHdJx6I4Y+FmO1o+R18X316uwdmPK3aQ0Hxwa7/0scNBokbdXxvGxLbdpUBIiicxsQN/RSikFD3mTIgeAPyabCe7Zulmvgufw35txk=" # Ex. travis encrypt SONATYPE_USER=your_sonatype_account - - secure: "MK/wBqkAOGTjSyyKSUNdP3QFGAACVHeBjYtQrDB/Y3GQDQZw6CKk2KZMVSTyr9E5ZAzglZEOAIWxTWgUjmqp0FYnUSsinmMsrk/NZx8RztozMFKyMHhdzs9OhyjbzGfM2PfTf+X3kB9LVQaKIUesk5a1Fb67YW76+oIOktP/BOTWDTfR50sF1JrkTdJ/F17V2eD5L/bLSGtMW9gIMPS15kSUluCesbO/eSYGRRANsvXrJjFmk4R8kpmHXLqKwCXAuc6WcvmdUeeIIB8vJHnSGTvvmpGaHz81wJeI0DSQdFVVTJTv3n/ed1vm63qgEs5pvzsU0bFTIscby0Gidts2zMOt6FW/qIMPFFuJV5uB5mg+zdRjO0eHZWsXY3+TxGgGZFM6pcQckzbEWYzT1apfqIbG62KDIIzLXKUnA4spzwpHeR015xq9FfHAmc296Esx3Uv2Q5c9ct2kDbMzD8+C4joWhKcW3xQHMIx1EdX2uUB3LOzKXxNlrCA7/2KC3y472CrLUbxWziHhq1FIkaDxbwiuWMCP53FZv3+N5aVJ3VTEE0zyBAIRP0OTTSe/4f51dcV8MA2j2hO2aIDUvitIixV8c/RjBn8aUoILyUHgjPusx1xsy/oEq2FnAx3WwWEzC28JaPFczxRD7Hi6+8Nfxv2V+mKZ0oeUIiT74kFE9vY=" + - secure: "YpUSzZYRhzCCAxNQap9Jx8dK/9N7Br5zejEA7Gdi3iO2HLwFp6zX+xLTand6tSGOy6/gYMo9won7BKvPI26hkVRLPwRtG/W9YbONkngO2zR0s+Ug2e0dUr3UcvxVOQ1Xrj2vozTzxXTwGQpwBkCgKDAD3iQAZSHgnsBAbw3+BSX/1egXbIaWjiC0cO75ubwQcERaVFSKaWPGUiUf/6KyO0wtIPIeX7wWn/JUllbCOEEEhW1BSgWbLZP1w0LEvKpoFrfBq6fVrr4TT64sOWoebMWxZx63Z6SPPf5uOx7wNS1XA4plo1C/XGsBKeWTqLlH/AebE83clGBCU3LJNDO1K6J9rWqr2GxcclURalVDBQph6pDmra1s36jL9yPKb0Fg+W9Bvb9YtwTqRNji5ZuD7nlX5u2UTZQQ0MToTI9c2jomMCWMeBVdqBZVm6v2Giwrja/PlObwgaSY6fzTS+mlsx3EIHqdaimGVZb26Z4jsj9zZS49Y8r1EufijlDsfaNvslpUiojPz4nF9x73vmnl1HqBuXhs7FrDOWj+PSw/qYgIeSeTNbu5b0FK/EWVnjHzUvJ1UDyQRSSTU0rcYfVhE3gJMEbRRB/eDdYa7H2pr/uoZ4+VdeddLhhepwTZJ1PRuuNn55s1Y1PQl3EyayoHD1ABKyiiRc/GGS2fY6fqZl0=" # Ex. travis encrypt SONATYPE_PASSWORD=your_sonatype_password - - secure: "bR2AZ3+wDpLlb99YKD3vWMmmGUdo1UCY6rEL03Nghdodhh+ZfKoVzlkGSzRTRF1dEWFwbz/8f+xd/vMtCqB8LvexFgs+2DCHHfExDvME1vT1vCdXIWKFjk6oQZoPONvUx4guj3a8pZ1a0WbNrQ3/hUwFsNLeHA1aXMtakkwAmmR0dC4o4leFlN4Boh9Lil7jz5MfYcV72Rumtt5GP3EaSDw+OPa2GRiuO4/m+ftlxBNwsYteaRqHSNMOmXs2Q8ZjIcYk59S98IPdeZLff6G5E/PXgqYrZiHzb/TBiuyHhHIYmufzI0os2DHjwhwam2s8oMgumwH0z4E3Id2dvhQUPN6+Z0mreXEoaSE7ycy6baig/oVUenNWVaXg3S00Cl+kGrZwy1v6lotIPIBhvwDTR+c2oZDnnRDYhgNevHnOoyaEar3vbfqt/vdm9PqR5L3IDPQ007cfIcy/JH97b0113jt1CCi1WVGPKWURWhNVYnwkfh0KtEgwhcv0OQadE1jgoIw5AsALEgYhZqAn9f6T5BrKdocSTeMwHP2U/uUe5f78glTk3nNwQd7G3jz5+BZ8jYOcc0GsrVT+L1MJCgFX4p44/Vm1JTXnpZCtTVzq8b5ZfuQqO0Nfln7jblhxPG+cFdU7qwvbGXDWR6T+NzhDBGVb6dqW8G3E2hZytMibq2Y=" + - secure: "jB2bn9K8z1DAbqHorduIGytoJVrKxqoAV9bdC57xtUmdbHYUwsCqnnvcOfUAiMhCDtSQl700jqSP1CuZF6mdO7Fva5e4FUF4ITGLScQkC1QQCVebbRKo6kBpdCqeEEWB9lfAqXFEgTORBUMzTOgsMJWGSnvwhDxRn3eqNkk0kZWYh4BT/odh6qV4jpolvMQQ6zXVRXsMCAehqPI6LEmp6ln7Bi9VFkoFyXiqCl3bQ9c2fT4YxdYKRf1X8lJ+fcq/FU939FDvDX2lJqFWDLUXYtRcADxdIh5ZZN15BcDj39MekNAk4M/1j4lPJJUHCUhzpeVP0dP5Av5eDWEEEyZpkpWtqgfshWFApckf6Sa4JRqOIqn8YaemF9dvhc2CSBXC8E28GejmsrzFPv5HsP07581VN8lIN9Us9WDiNUqz0h6aZciVr/AzBSgVS0kLj54eo6e+860TrX3b2L25IlnehfO6+W5205OBYfeLnL46NK76pGzZ0Oz18wxkT14HBykaIgRnXmZowWzHIEwAo1xJHIBmqrrUkLMhRn4bu1BSiEbTC5yD94YYgj1IXGAwuMaH2EgmPgJymecwhjjrC0TlEgLYTS8VOQrm5Ap77FZopnXJYYPjTxP99/sVBJKUCBPlpPCQXrxkrXeFeDUTY0dK9wQX07EousAcrBynNQVSuuc=" From 1c4c9adf58beaebc400df5c19ac478ef0abcd0a2 Mon Sep 17 00:00:00 2001 From: Sjoerd Talsma Date: Thu, 19 Jan 2017 20:05:39 +0100 Subject: [PATCH 51/51] Rename repository to 'java-spanmanager'. Re-encrypted travis credentials. --- .travis.yml | 12 ++++++------ CONTRIBUTING.md | 4 ++-- README.md | 6 +++--- pom.xml | 14 +++++++------- .../contrib/spanmanager/SpanManager.java | 2 +- .../concurrent/SpanPropagatingExecutorService.java | 6 +++--- .../concurrent/SpanPropagatingExecutors.java | 12 ++++++------ .../tracer/AutoReleasingManagedSpan.java | 2 +- .../spanmanager/tracer/ManagedSpanBuilder.java | 4 ++-- .../spanmanager/tracer/ManagedSpanTracer.java | 2 +- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index d2ba346..2b8d189 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,13 +41,13 @@ branches: env: global: - # Ex. travis encrypt -r opentracing-contrib/java-activespan BINTRAY_USER=your_github_account - - secure: "kUlDX2huODNAWdoTjOOzsthNsDxIU4DcNIXmdaXKAiOrASDHRldS6PLoZlEW55UEqsnEveOrWlm9DU9xEeznf8Rt3GU7PvCm8+LMRyWmkXbHhiF0dYA9yxSIpM9TeXhlqx0eYO3oNQ6U8YmIupOtfEHn39mkU7WZIwUqOKjKQCT9MNeBXPfzQPkXseCHtxBH4Axe6qng07eDWTY/k1IxGaL9F/k27371qlnS/xFLp6pSPQ990jrKX1xY5Mw68PFrqWJRwMiC0opotNzM69G+5av4yMwPakf4Sf0hZjm2+9voWmChecFVbG81NJK9caWfFH/BQqVLsvns28VIYfaM7bk2/LzpWg7/1XABPDfjulBU80vUOHvHXDaEj0VFng7NEO248OvbRfJRbtDv5+LheOjBiiJHCFIfH1hy4j1NCzLlI0yCS8/cycIvXI1VTqVFUGErL8wS7Sv5zh/E+97qTg08k+pLJTE4z/n5+Ehfsfp94snRwIQSrfRh/ccT97f7qNjlRfflYt2bFpMjNLXRpcrm34Y990c64Usr0ONdFN/hT2i5+K+kwshH4pYrnX9KzHVweVFAQt7bhyFPJjNrdKSOG2RgFDwRyAtcgNKx9oz5F4S7oe4dkd9SoSC0KB/nQxsBy+KBm1O4Camq7u2OgnByn1FIbavCxWHxGhOkOeE=" + # Ex. travis encrypt -r opentracing-contrib/java-spanmanager BINTRAY_USER=your_github_account + - secure: "RS267NqO3TR8MuAjP7XK3hItutoXDicHXF5tsKMiPjCTdxlVMWFqFI9zFhXQjKoCAUxsvaRnGIoF3xAZgH/nlC1q7QQzq0XKPpaoSmFv4+EwyjxF1O6DuhaUDDbFbmcFQzofvSUxgl0L45o5z9iJ1Y2Z+4kesWA/6jD499fhC+ekBIskmFrcI7Y+z92VDt5Y9ZPf49/P4iVQHiKk02omHa7OIR2wL3HHXuPy8lmKTky8p/IJa0tGQq7DfoD78ud3OaiXpEr4IjBLcJMGMOCO6DGS2/G8UfPrMEWv8iWGua2MMgfuHvIgtGVxTVJ+OtDuWndbzP558tjz33h2yxE/ASpVGtznRkEQlK6F1Yno3INhq9j3OEU0DG+vLluT1zWdkNriVkza89Wp9CBzi1IszV+9+cjkUWDWxomDu4K+O9y9RpBsXgxrTTBqymopKkg/8KHXWNjTEZRGqlgYvZD37nvpxIFzf35lXcK6HcWsFYoYlIB8h/Iieboymy8uyEcqK1TSsQFR88ZaacT4fK+goa+rFdcsTUO7sUzZJOmLQGgvRCKARu80pN/k2+d+XPRvCnl+pcSB0s8ZoInTSEi+gB3uNw1uyl8bzhy3mIlw6eMaUiT7krZzQCv94tl6tyrVyOXavsnNsRzBbkxC3w9CtK9DVqQSYFYQ8bZKX/KLl4c=" # Ex. travis encrypt BINTRAY_KEY=xxx-https://bintray.com/profile/edit-xxx --add - - secure: "HG1jFM22Tfj1LEhLEcMC5Y9xFajsqfRYYy2GRv3ZmDBP+7lxfevJEjoPE+xxBvUyZuouNKVfSLLduVm8JLhK4xOJUqI+H1xivntQH7fRU9u14XX8L2U5UJHNEp+yRx/nXzuTMUJqNZ75FSUBkVTRIQwAAi8PtIrtHef/jEgUUoZfR9vd3WbB85VWXoKxGr1yHa4sh9RDFKDG3JhnSTdbGhtX3mglTVdOP3rqjBszKsYlzDJjStwGuPZU7F3a1OcUZ86F3HQdIgr9QTE+JWVIF9AGq3GMA/1cBtdjAvYFpkZUmtMwMUZib2uykvLfwHxiQJqVlebB68EZSs+PRnPmOeNrkVx3USGFEUSHtTaoimeMG124nlHABhexgmBR6V+mksfXKESxiVuFn50kWGBKZAJUQ6CaNJQl8Qexl7BdVReJu2Gq6yHt6lcc1DotOgvpPAGic+uMqa8IRCaBUhPx2iuws2Q4Kx/3/qOOrIwiIysEq/CtIhNwjhSNQf/hkFX37IqXZ8EWyY771qCrCbwqvBVShO7Qywy4FMHAWRsfmTQYzHfo6STGKv1Yzit5ny2K/do+SgAhYhoT9L07B+/BzfHvB1VJ3Qv63PwQ9ANiyHyAIAR/SMagpb7CMNr1f1atEyCBiIRIZY7t/375/U9WvNVupj7eWYGw1Ocjfculywo=" + - secure: "ZOm+CpZs0hUBc5eREK9i87HL7bmHSDENdpH9hY4N/MPPaYLb04MEkaJ8pW1qGeLbmumATXcE3mfWdJ95LKROn+qUBdUofXbq430EScOkMnIzntkyfKVeq5wr/qigArkhgY6S70u6tUrS2NDLnfwR4gBYCHUKY7InpGUR/cggwdnbYVVKuu31j+xylMZNDAA5EXpVzAfeKSEDEiwS0ej5lPfo0KQCN3f3fDjiJASLsiBl5IdxDHwONxvZWqcWOKpRYIfXYSFOFdgdIGWfQarAPJlUsefVV9jXUtCfB2PXRn6fdScjE0LOWcfQSYdVO+l8sslpufp+DPH0V7edWwZbFgshUIETtu0eoAOLmGbd571WzVmAnYpalY8m4MJFFLVjLe32npvDtxvibo1vFp7l9NPvkQCV6BwS8iLsWJDpraeJmb3ZfOr1hUObJs/JayDVEe6rB3nSkjs4Y5dyBc3hPurPBnYWl4HSDsRmKZ1ieGaIh8FuEOl0MToDO/ZCTQJB3A6CvnlYbI0t4WFU7Q68zRSJih4bUxaQD3aERzGgpMatGAoxMi1hfv0krMAJQTHSZqKpTrftJ/CTjXn2cJavO+Eai3lODzZ42tnz/blzOApfSKU4biu3QwohTMIPqaLOA1KfF6hJkSKfsTzjbSnqqdEc09liYSCoRbLleEXBr9w=" # Ex. travis encrypt GH_TOKEN=XXX-https://github.com/settings/tokens-XXX --add - - secure: "X3b+6lVeFlHrZISNaVqyCZq04JQPmY0xv9+QQdzG5rRqWJdLUAV5kT7cNHccJ8gy83c5Zs72u/jwugIpqiQNHHzEkbF77CUwlb365LuatcHHR/KxVoO264rJLorW5UuSebzlZ0feUegaN7t5QQeEyp7bVWg9M6rNjBVocgvi4EFATDBTFxodri9Ktqr0EbTU89BXgxYdbanBwrNu78DDQFIlookYFOwe/8N9qPgjHvmLjt5BtOGNwWIhAmvjqPHdgkq6v/iyqhcjX19wcRGmS+Xk8sFFjeH68DTrNmA6lAuiDvMD8MhFuPeI6gM9X6cHMtubFccxiG6gsBbiN2T43kelSu6DgTilMWWg/BWKJI82ZhRPX2mT1YtQYlYm0VQ/GnrPyKs7pVzEzCXDg/feUa1DnBsu9x/oTt+MeaLUhY2PfDuSJthpUMLr+B9kOt+e1kXvUK1PR/ukYxveXgvA8eQpSW49Tm2/BXdl0Ig2ZBCD9INQO/ynpql6a5xcXpoiRyg1eq/g42vvJyC1/2pl0fS2YT4s1jkknTVaQJ0FUhzIdqCpnnBmpXVk+n6DGZraio9dfkHdJx6I4Y+FmO1o+R18X316uwdmPK3aQ0Hxwa7/0scNBokbdXxvGxLbdpUBIiicxsQN/RSikFD3mTIgeAPyabCe7Zulmvgufw35txk=" + - secure: "ktakwcLywOMAMeJpj2+haZHgdkbs5Me1JA1fDoPIcNJMttHSa3A8JEYxTFmwX5/ybNuXJpL98HblGQfYPIH1oL4oyO7oif1t/LzjUibFUDzgMVMgUy8oirGUVRwOH9Uj3/eILBLbMQO+yodbDvHHhiVT+xv1K1q3Qy+vwSabSr8YjYADvlL4Oz60C2BAPLpuG/VjZPQ96nQopVI/a+3IvgEega0kRHw0LFFT87Ht/MOpo++uU9FPJ8QU3Tf7EYuuuR+xJIJ4W04Cewp6TdreiPWynf1aLwVnsy4cXIt+e6K0KJg9PGtlGjwJGllB7TMYtFmQ+qCyVYs+OqmDpUqQ4JCUKpZ6KyRIdh+I+3CNYttMCFYBTvBvMBFEyzSO2OOJWysF3vahCmCVHkZdJacHQ5gaGrVpzZ/9nDdkmO2trH6xlmp+MPWehXsuMLgIVJLyJSEZb/hQCojzjpDY7vTAyleC/5DwyF7iInf7CO6ZNHnWODyMfgLOkB8yuXhRAQjMejXObRYcT5u6wiJaQb/m3IqXOdrZuOo/dbFzMZSnIXmC6TaK6NnWu3Noibh+Hyg9odsy7tAPCAsJUOBy+JQdeIJNeV76cxigN37EQSlFD9k2s82LIFrNqC0za3kuQfE+iWPeTrTBVJfIxq7KZrN9f0jfj/gvpPA0borwRKt+5xQ=" # Ex. travis encrypt SONATYPE_USER=your_sonatype_account - - secure: "YpUSzZYRhzCCAxNQap9Jx8dK/9N7Br5zejEA7Gdi3iO2HLwFp6zX+xLTand6tSGOy6/gYMo9won7BKvPI26hkVRLPwRtG/W9YbONkngO2zR0s+Ug2e0dUr3UcvxVOQ1Xrj2vozTzxXTwGQpwBkCgKDAD3iQAZSHgnsBAbw3+BSX/1egXbIaWjiC0cO75ubwQcERaVFSKaWPGUiUf/6KyO0wtIPIeX7wWn/JUllbCOEEEhW1BSgWbLZP1w0LEvKpoFrfBq6fVrr4TT64sOWoebMWxZx63Z6SPPf5uOx7wNS1XA4plo1C/XGsBKeWTqLlH/AebE83clGBCU3LJNDO1K6J9rWqr2GxcclURalVDBQph6pDmra1s36jL9yPKb0Fg+W9Bvb9YtwTqRNji5ZuD7nlX5u2UTZQQ0MToTI9c2jomMCWMeBVdqBZVm6v2Giwrja/PlObwgaSY6fzTS+mlsx3EIHqdaimGVZb26Z4jsj9zZS49Y8r1EufijlDsfaNvslpUiojPz4nF9x73vmnl1HqBuXhs7FrDOWj+PSw/qYgIeSeTNbu5b0FK/EWVnjHzUvJ1UDyQRSSTU0rcYfVhE3gJMEbRRB/eDdYa7H2pr/uoZ4+VdeddLhhepwTZJ1PRuuNn55s1Y1PQl3EyayoHD1ABKyiiRc/GGS2fY6fqZl0=" + - secure: "VyTo3xcnUMpXofZlgY5D8qBrypOclWPcyG1u8skwozrJkeJMg31DOkU6s3dLxPq1NMBKaiVnaKMzOrTvGMu11+7pWc+afamZ1UW/I4ZxToZPl2/Dq8iTHqt1PiIwNd6Y1cKrOC2bnBKjXONN0rpoHmgFERvLYqfXTjs0qZrFQRth3TfgxyQux3EXBmxZioQYEgZqwOISPRWyK0tFJ+9VNpUFbzw7JnpP4XwFRe2HaVyCRpbWyQ1pIhcr98dZEa9KsdGJtnOVdZm+Ej8QyNvTOwJgqFOblstaUcYTXVZErZWDQ9OMd8plExhPzpaqymz/0xB/tXaHnHTEf0yQCpjHi18UzrQveXJ20jk+n0unuqGeq6L/XpWLG2kIxTeBPt4MCDd+FCyli55jiQpzlRqAwOcWndiy47qoJzmqseWvp7DKoNvXwwSRgPXJ5e2u/ZQdbRmIwZmzxemhTBoa23ekoP2LvKPJH2mfDc5e1S10boHVjmuahKeKAbSw28CJ1m3mBGg1wyTLVtneauOaAK4zCzXfZ6JkdHKQSoAD+AiYNrYnfZk76kcUNVgi+wqadHgNXinni1WGM8MGu9nfsmYSY2PO4/2zqmASYhc417LRzkx0iu4wNMiy8zowbbhnFNxt8LrLbJV90eu6Z4XEcPAThfL5xzH8bSvyXuiYuT5sXV0=" # Ex. travis encrypt SONATYPE_PASSWORD=your_sonatype_password - - secure: "jB2bn9K8z1DAbqHorduIGytoJVrKxqoAV9bdC57xtUmdbHYUwsCqnnvcOfUAiMhCDtSQl700jqSP1CuZF6mdO7Fva5e4FUF4ITGLScQkC1QQCVebbRKo6kBpdCqeEEWB9lfAqXFEgTORBUMzTOgsMJWGSnvwhDxRn3eqNkk0kZWYh4BT/odh6qV4jpolvMQQ6zXVRXsMCAehqPI6LEmp6ln7Bi9VFkoFyXiqCl3bQ9c2fT4YxdYKRf1X8lJ+fcq/FU939FDvDX2lJqFWDLUXYtRcADxdIh5ZZN15BcDj39MekNAk4M/1j4lPJJUHCUhzpeVP0dP5Av5eDWEEEyZpkpWtqgfshWFApckf6Sa4JRqOIqn8YaemF9dvhc2CSBXC8E28GejmsrzFPv5HsP07581VN8lIN9Us9WDiNUqz0h6aZciVr/AzBSgVS0kLj54eo6e+860TrX3b2L25IlnehfO6+W5205OBYfeLnL46NK76pGzZ0Oz18wxkT14HBykaIgRnXmZowWzHIEwAo1xJHIBmqrrUkLMhRn4bu1BSiEbTC5yD94YYgj1IXGAwuMaH2EgmPgJymecwhjjrC0TlEgLYTS8VOQrm5Ap77FZopnXJYYPjTxP99/sVBJKUCBPlpPCQXrxkrXeFeDUTY0dK9wQX07EousAcrBynNQVSuuc=" + - secure: "kQYGrYzMw8lN0Vp4GbEOHxYIR+w1YtJ6LaYz00x6Lb/3pyaINFDGnLmKuANNXvHr4UyrT+q4bg8EWII4ctdbbvsSOBvxq0VkADBBGpYvlpa92598TsrfeJ9ZDz5kthajyQXK1kZU0kkBRHVbJ4I1CBqlJOHRYv9h2m99D+Fe3sHnPePre2e2BNJbFJj6EomKBTNarZQOdR7EFJ3DBmsalAmcZm52npSaeI/8D06QGNlEqvBufOwF6EYNN+IVJZsmhjoSyrYx/qqPIecQOVZfaDCk5Y6QNU4JWQaSDtLfVZjJ3QTIj86fjRqd30TBUtl9Dq+iBwuRCImBV6I9PpfLXoQMIaguE1UWt4lfDwBhsylnm9g35BQ/V0iOFnWXyhablK6irKAgYXlQMuvxaqrNnLf6tK5DIoPO0nJS9n1Sj8uH6sW/MfDJ4I3V7wfBDBN40Bi3N8LAcmf9vcDfKq6yqi4rXQ45eM2BffV/fWt4miN7vBjnBEyWPAzAfNr4b1m5U5eHtyk6sLrzWUK9eMIlN3CrIAKy7LLOYhgi7vnYuqyU9wCAzxIORFKE6NSn3bokTCr3WchCNmM2fvHpIh9b6BzPBgiO9MGllxq4PpyXpgpG9zygpA1t5ZXnUD1/Gs5UWy6peHqV85RtKBv/M/SwDFlewZG/Aj0LNThV6a9ZEyA=" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86023d8..b12db64 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to opentracing-contrib / java-activespan +# Contributing to opentracing-contrib / java-spanmanager If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request (on a branch other than `master` or `gh-pages`). @@ -7,7 +7,7 @@ and sending a pull request (on a branch other than `master` or `gh-pages`). ## License By contributing your code, you agree to license your contribution under the terms of the APLv2: -https://github.com/opentracing-contrib/java-activespan/blob/master/LICENSE +https://github.com/opentracing-contrib/java-spanmanager/blob/master/LICENSE All files are released with the Apache 2.0 license. diff --git a/README.md b/README.md index 7387e8f..822a1b2 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ This `ExecutorService` _propagates the current span_ from the caller into each call that is executed. The current span of the caller is obtained from the configured `SpanManager`. -_Please Note:_ The active span is merely _propagated_ (as-is). +_Please Note:_ The current span is merely _propagated_ (as-is). It is explicitly **not** finished when the calls end, nor will new spans be automatically related to the propagated span. @@ -163,7 +163,7 @@ When starting a new span and making it the _currentSpan_, the manual example abo } ``` -The `ManagedSpanTracer` automatically makes every started span the active span. +The `ManagedSpanTracer` automatically makes every started span the current span. It also releases it again when the span is finished: ```java @@ -205,7 +205,7 @@ Handling the request: final SpanManager spanManager = ... // inject or DefaultSpanManager.getInstance(); void onRequest(RequestContext reqCtx) { Span span = ... // either obtain Span from previous filter or start from the request - ManagedSpan managedSpan = spanManager.manage(span); // span is now activeSpan. + ManagedSpan managedSpan = spanManager.manage(span); // span is now currentSpan. reqCtx.put(SOMEKEY, managedSpan); } ``` diff --git a/pom.xml b/pom.xml index af2d075..690180f 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ SpanManager library Manager of current span and propagating it accross threads - https://github.com/opentracing-contrib/java-activespan + https://github.com/opentracing-contrib/java-spanmanager 2017 @@ -69,20 +69,20 @@ - https://github.com/opentracing-contrib/java-activespan/tree/master - scm:git:git://github.com/opentracing-contrib/java-activespan.git - scm:git:git@github.com:opentracing-contrib/java-activespan.git + https://github.com/opentracing-contrib/java-spanmanager/tree/master + scm:git:git://github.com/opentracing-contrib/java-spanmanager.git + scm:git:git@github.com:opentracing-contrib/java-spanmanager.git Github - https://github.com/opentracing-contrib/java-activespan/issues + https://github.com/opentracing-contrib/java-spanmanager/issues bintray - https://api.bintray.com/maven/opentracing/maven/java-activespan/;publish=1 + https://api.bintray.com/maven/opentracing/maven/java-spanmanager/;publish=1 jfrog-snapshots @@ -247,7 +247,7 @@ opentracing maven - java-activespan + java-spanmanager diff --git a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java index ecee34d..6dd1295 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/SpanManager.java @@ -47,7 +47,7 @@ public interface SpanManager { /** * Unconditional cleanup of all managed spans including any parents. *

    - * This allows boundary filters to release all active spans + * This allows boundary filters to release all current spans * before relinquishing control over their process, * which may end up repurposed by a threadpool. * diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java index 6b3019a..3f713d6 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutorService.java @@ -22,10 +22,10 @@ import java.util.concurrent.*; /** - * Propagates the {@link SpanManager#currentSpan() currently active span} from the caller + * Propagates the {@link SpanManager#currentSpan() current span} from the caller * into each call that is executed. *

    - * Note: The active span is merely propagated. + * Note: The current span is merely propagated. * It is explicitly not finished when the calls end, * nor will new spans be automatically related to the propagated span. */ @@ -34,7 +34,7 @@ public class SpanPropagatingExecutorService implements ExecutorService { private final SpanManager spanManager; /** - * Wraps the delegate ExecutorService to propagate the {@link SpanManager#currentSpan() currently active span} + * Wraps the delegate ExecutorService to propagate the {@link SpanManager#currentSpan() current span} * of callers into the executed calls, using the specified {@link SpanManager}. * * @param delegate The executorservice to forward calls to. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java index 2595473..262cb00 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/concurrent/SpanPropagatingExecutors.java @@ -44,7 +44,7 @@ private SpanPropagatingExecutors() { /** * This method returns a {@link Executors#newFixedThreadPool(int) fixed threadpool} that propagates - * the active span into the started threads. + * the current span into the started threads. * * @param nThreads the number of threads in the pool * @param spanManager the manager for span propagation. @@ -57,7 +57,7 @@ public static SpanPropagatingExecutorService newFixedThreadPool(int nThreads, Sp /** * This method returns a {@link Executors#newFixedThreadPool(int, ThreadFactory) fixed threadpool} that propagates - * the active span into the started threads. + * the current span into the started threads. * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads @@ -72,7 +72,7 @@ public static SpanPropagatingExecutorService newFixedThreadPool( /** * This method returns a {@link Executors#newSingleThreadExecutor() single-threaded executor} that propagates - * the active span into the started thread. + * the current span into the started thread. * * @param spanManager the manager for span propagation. * @return the newly created single-theaded executor @@ -84,7 +84,7 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor(SpanManager /** * This method returns a {@link Executors#newSingleThreadExecutor(ThreadFactory) single-threaded executor} - * that propagates the active span into the started thread. + * that propagates the current span into the started thread. * * @param threadFactory the factory to use when creating new threads * @param spanManager the manager for span propagation. @@ -98,7 +98,7 @@ public static SpanPropagatingExecutorService newSingleThreadExecutor( /** * This method returns a {@link Executors#newCachedThreadPool() cached threadpool} that propagates - * the active span into the started threads. + * the current span into the started threads. * * @param spanManager the manager for span propagation. * @return the newly created thread pool @@ -110,7 +110,7 @@ public static SpanPropagatingExecutorService newCachedThreadPool(SpanManager spa /** * This method returns a {@link Executors#newCachedThreadPool(ThreadFactory) cached threadpool} that propagates - * the active span into the started threads. + * the current span into the started threads. * * @param threadFactory the factory to use when creating new threads * @param spanManager the manager for span propagation. diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java index 91b216a..eb0e465 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/AutoReleasingManagedSpan.java @@ -40,7 +40,7 @@ public Span getSpan() { } /** - * Releases this currently active ManagedSpan. + * Releases this current ManagedSpan. */ @Override public void release() { diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java index 97f6c09..b350c33 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanBuilder.java @@ -47,7 +47,7 @@ final class ManagedSpanBuilder implements SpanBuilder { * Replaces the {@link #delegate} SpanBuilder by a delegated-method result. * * @param spanBuilder The builder returned from the delegate (normally '== delegate'). - * @return This re-wrapped ActiveSpanBuilder. + * @return This re-wrapped ManagedSpanBuilder. */ SpanBuilder rewrap(SpanBuilder spanBuilder) { if (spanBuilder != null) { @@ -59,7 +59,7 @@ SpanBuilder rewrap(SpanBuilder spanBuilder) { /** * Starts the built Span and {@link SpanManager#manage(Span) activates} it. * - * @return a new 'currently active' Span that deactivates itself upon finish or close calls. + * @return a new 'current' Span that releases itself upon finish or close calls. * @see SpanManager#manage(Span) * @see AutoReleasingManagedSpan#release() */ diff --git a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java index 19be2c5..49f964a 100644 --- a/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java +++ b/src/main/java/io/opentracing/contrib/spanmanager/tracer/ManagedSpanTracer.java @@ -20,7 +20,7 @@ import io.opentracing.propagation.Format; /** - * Convenience {@link Tracer} that automates managing the {@linkplain SpanManager#currentSpan() currently active span}: + * Convenience {@link Tracer} that automates managing the {@linkplain SpanManager#currentSpan() current span}: *

      *
    1. It is a wrapper that forwards all calls to another {@link Tracer} implementation.
    2. *
    3. {@linkplain Span Spans} created with this tracer are