org.zalando.tracer.Tracer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tracer-core Show documentation
Show all versions of tracer-core Show documentation
Tracing requests through a distributed system.
package org.zalando.tracer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static java.util.Arrays.asList;
/**
* A tracer is a lifecycle manager of one or more {@link Trace traces}. Traces are identified by name, e.g.
* {@code X-Trace-ID} and, during an active lifecycle, they can have a value, e.g.
* {@code 5dc90f18-231f-11e6-a17d-0f84f5d57f60}. Values can either be provided on a per-lifecycle basis or generated
* on-demand by a {@link Generator generator}. Lifecycle events, i.e. start and stop, can be observed by registering
* one or more {@link TraceListener listeners}.
*
* @see Trace
* @see Generator
* @see TraceListener
*/
public interface Tracer {
/**
* Starts a new trace lifecycle without transporting any trace identifiers.
*
* The most common use case would be to manage a trace lifecycle in a scheduled background job or during tests.
*
*
* tracer.start();
*
* try {
* // do some background work here
* } finally {
* tracer.stop();
* }
*
*
* @throws IllegalStateException if this tracer is already started
*/
default void start() {
start(name -> null);
}
/**
* Starts a new trace lifecycle with the possibility to transport given trace identifiers.
*
* The most common use case would be to read headers from an incoming HTTP request and hand them over to this
* tracer in order to persist them:
*
*
* tracer.start(request::getHeader)
*
* try {
* // continue with processing the request
* } finally {
* tracer.stop();
* }
*
*
* If the provider returns null a value will be created using the configured {@link Generator generator}.
*
*
* @param provider a provider for trace identifiers, will be called with individual trace names
* @throws IllegalStateException if this tracer is already started
*/
void start(final Function provider);
boolean isActive();
/**
* Retrieves the {@link Trace trace} with the given name. The returned instance is a thread-safe live-view that
* allows to observe future changes to that trace's lifecycle.
*
* This can be used to manually access a trace value, e.g. for additional audit logging:
*
*
* entity.setModifiedBy(trace.getValue());
* db.persist(entity);
*
*
* @param name the name of the trace
* @return the trace
* @throws IllegalArgumentException if no trace was configured for the given name
*/
Trace get(final String name);
/**
* Iterates all configured traces and allows to observe their current values.
*
* The most common use case woule be to pass all traces onto another system by adding them to an outgoing
* HTTP request:
*
*
* tracer.forEach(request::addHeader);
*
*
* @param consumer a consumer for traces, will be called with trace name-value pairs
* @throws IllegalStateException if this tracer not started
*/
void forEach(final BiConsumer consumer);
/**
* Provides an immutable snapshot of all configured traces mapped by name to their current values.
*
* @return an immutable snapshot of all traces
* @throws IllegalStateException if this tracer not started
*/
default Map snapshot() {
final Map map = new LinkedHashMap<>();
forEach(map::put);
return Collections.unmodifiableMap(map);
}
/**
* Stops the current lifecycle.
*
* @throws IllegalStateException if this tracer is not started
*/
void stop();
/**
* Creates a {@link Runnable runnable}, based on the given original, that will manage a completely new lifecycle
* for every invocation,
*
* @param task the original runnable
* @return a runnable that will add lifecycle management to the given runnable
* @see #delegate(Runnable, Function)
*/
default Runnable manage(final Runnable task) {
return delegate(task, name -> null);
}
/**
* Creates a {@link Runnable runnable}, based on the given original, that will preserve the current state of
* all traces for every invocation.
*
* @param task the original runnable
* @return a runnable that will add lifecycle management to the given runnable
* @throws IllegalStateException if this tracer is not started
* @see #snapshot()
* @see #delegate(Runnable, Function)
*/
default Runnable preserve(final Runnable task) {
return delegate(task, snapshot()::get);
}
/**
* Creates a {@link Runnable runnable}, based on the given original, that will preserve the current state of
* all traces for every invocation, if a trace is currently active. Otherwise it will manage a completely new
* lifecycle for every invocation.
*
* @param task the original runnable
* @return a runnable that will add lifecycle management to the given runnable
* @see #snapshot()
* @see #preserve(Runnable)
* @see #manage(Runnable)
*/
default Runnable tryPreserve(final Runnable task) {
return isActive() ? preserve(task) : manage(task);
}
/**
* Creates a delegating {@link Runnable runnable}, based on the given original, that will manage the tracer
* lifecycle for every invocation. Initial trace values can be seeded with the given provider.
*
* @param task the original runnable
* @param provider a provider for trace identifiers, will be called with individual trace names
* @return a runnable that will add lifecycle management to the given runnable
* @see #start(Function)
* @see #manage(Runnable)
* @see #preserve(Runnable)
*/
default Runnable delegate(final Runnable task, final Function provider) {
return () -> {
start(provider);
try {
task.run();
} finally {
stop();
}
};
}
/**
* Creates a {@link Callable callable}, based on the given original, that will manage a completely new lifecycle
* for every invocation,
*
* @param task the original callable
* @return a callable that will add lifecycle management to the given callable
* @see #delegate(Callable, Function)
*/
default Callable manage(final Callable task) {
return delegate(task, name -> null);
}
/**
* Creates a {@link Callable callable}, based on the given original, that will preserve the current state of
* all traces for every invocation.
*
* @param task the original callable
* @return a callable that will add lifecycle management to the given callable
* @throws IllegalStateException if this tracer is not started
* @see #snapshot()
* @see #delegate(Callable, Function)
*/
default Callable preserve(final Callable task) {
return delegate(task, snapshot()::get);
}
/**
* Creates a {@link Callable callable}, based on the given original, that will preserve the current state of
* all traces for every invocation, if a trace is currently active. Otherwise it will manage a completely new
* lifecycle for every invocation.
*
* @param task the original callable
* @return a callable that will add lifecycle management to the given callable
* @see #snapshot()
* @see #preserve(Callable)
* @see #manage(Callable)
*/
default Callable tryPreserve(final Callable task) {
return isActive() ? preserve(task) : manage(task);
}
/**
* Creates a delegating {@link Callable callable}, based on the given original, that will manage the tracer
* lifecycle for every invocation. Initial trace values can be seeded with the given provider.
*
* @param task the original callable
* @param provider a provider for trace identifiers, will be called with individual trace names
* @return a callable that will add lifecycle management to the given callable
* @see #start(Function)
* @see #manage(Callable)
* @see #preserve(Callable)
*/
default Callable delegate(final Callable task, final Function provider) {
return () -> {
start(provider);
try {
return task.call();
} finally {
stop();
}
};
}
/**
* Creates a new tracer that will manage the given traces. All traces will be using the {@link UUIDGenerator} by
* default. The returned instance will not have any {@link TraceListener listeners} attached to it.
*
* @param names trace names to be configured
* @return a new tracer
*/
static Tracer create(final String... names) {
return builder().traces(asList(names)).build();
}
static TracerFactory.Builder builder() {
return TracerFactory.builder();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy