io.micrometer.observation.Observation Maven / Gradle / Ivy
Show all versions of micrometer-observation Show documentation
/*
* Copyright 2022 VMware, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.micrometer.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.common.lang.NonNull;
import io.micrometer.common.lang.Nullable;
import io.micrometer.common.util.internal.logging.InternalLoggerFactory;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* An act of viewing or noticing a fact or an occurrence for some scientific or other
* special purpose (According to dictionary.com).
*
* You can wrap an operation within your code in an {@link Observation} so that actions
* can take place within the lifecycle of that observation via the
* {@link ObservationHandler}.
*
* According to what is configured the actions can be e.g. taking measurements via
* {@code Timer}, creating spans for distributed tracing, correlating logs or just logging
* out additional information. You instrument your code once with an {@link Observation}
* but you can get as many benefits out of it as many {@link ObservationHandler} you have.
*
* @author Jonatan Ivanov
* @author Tommy Ludwig
* @author Marcin Grzejszczak
* @author Yanming Zhou
* @since 1.10.0
*/
public interface Observation extends ObservationView {
/**
* No-op observation.
*/
Observation NOOP = new NoopObservation();
/**
* Create and start an {@link Observation} with the given name. All Observations of
* the same type must share the same name.
*
* When no registry is passed or the observation is
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* not applicable}, a no-op observation will be returned.
* @param name name of the observation
* @param registry observation registry
* @return a started observation
*/
static Observation start(String name, @Nullable ObservationRegistry registry) {
return start(name, Context::new, registry);
}
/**
* Creates and starts an {@link Observation}. When the {@link ObservationRegistry} is
* null or the no-op registry, this fast returns a no-op {@link Observation} and skips
* the creation of the {@link Observation.Context}. This check avoids unnecessary
* {@link Observation.Context} creation, which is why it takes a {@link Supplier} for
* the context rather than the context directly. If the observation is not enabled
* (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned.
* @param name name of the observation
* @param contextSupplier mutable context supplier
* @param registry observation registry
* @return started observation
*/
static Observation start(String name, Supplier contextSupplier,
@Nullable ObservationRegistry registry) {
return createNotStarted(name, contextSupplier, registry).start();
}
/**
* Creates but does not start an {@link Observation}. Remember to call
* {@link Observation#start()} when you want the measurements to start. When no
* registry is passed or observation is not applicable will return a no-op
* observation.
* @param name name of the observation
* @param registry observation registry
* @return created but not started observation
*/
static Observation createNotStarted(String name, @Nullable ObservationRegistry registry) {
return createNotStarted(name, Context::new, registry);
}
/**
* Creates but does not start an {@link Observation}. Remember to call
* {@link Observation#start()} when you want the measurements to start. When the
* {@link ObservationRegistry} is null or the no-op registry, this fast returns a
* no-op {@link Observation} and skips the creation of the
* {@link Observation.Context}. This check avoids unnecessary
* {@link Observation.Context} creation, which is why it takes a {@link Supplier} for
* the context rather than the context directly. If the observation is not enabled
* (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned.
* @param name name of the observation
* @param contextSupplier supplier for mutable context
* @param registry observation registry
* @return created but not started observation
*/
static Observation createNotStarted(String name, Supplier contextSupplier,
@Nullable ObservationRegistry registry) {
if (registry == null || registry.isNoop()) {
return NOOP;
}
Context context = contextSupplier.get();
context.setParentFromCurrentObservation(registry);
if (!registry.observationConfig().isObservationEnabled(name, context)) {
return NOOP;
}
return new SimpleObservation(name, registry, context);
}
// @formatter:off
/**
* Creates but does not start an {@link Observation}.
* Remember to call {@link Observation#start()} when you want the measurements to start.
* When the {@link ObservationRegistry} is null or the no-op registry, this returns a
* no-op {@link Observation} and skips the creation of the {@link Observation.Context}.
* If the observation is not enabled (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned.
*
A single {@link ObservationConvention convention} will be used for this observation
* for getting its name and {@link KeyValues key values}:
*
* - the {@code customConvention} given as an argument, if not {@code null}
*
- a {@link GlobalObservationConvention} configured on the
* {@link ObservationRegistry.ObservationConfig#observationConvention(GlobalObservationConvention)}
* that matches this observation
*
- as a fallback, the {@code defaultConvention} will be used if none of the above are available
*
* @param type of context
* @param customConvention custom convention. If {@code null}, the default one will be
* picked.
* @param defaultConvention default convention when no custom convention was passed,
* nor a pre-configured one was found
* @param contextSupplier supplier for the observation context
* @param registry observation registry
* @return created but not started observation
*/
// @formatter:on
static Observation createNotStarted(@Nullable ObservationConvention customConvention,
ObservationConvention defaultConvention, Supplier contextSupplier,
@Nullable ObservationRegistry registry) {
if (registry == null || registry.isNoop()) {
return Observation.NOOP;
}
ObservationConvention convention;
T context = contextSupplier.get();
context.setParentFromCurrentObservation(registry);
if (customConvention != null) {
convention = customConvention;
}
else {
convention = registry.observationConfig().getObservationConvention(context, defaultConvention);
}
if (!registry.observationConfig().isObservationEnabled(convention.getName(), context)) {
return NOOP;
}
return new SimpleObservation(convention, registry, context);
}
/**
* Creates and starts an {@link Observation}. When no registry is passed or
* observation is not applicable will return a no-op observation.
*
* Please check the javadoc of
* {@link Observation#createNotStarted(ObservationConvention, ObservationConvention, Supplier, ObservationRegistry)}
* method for the logic of choosing the convention.
*
* @param observationConvention observation convention
* @param registry observation registry
* @return started observation
*/
static Observation start(ObservationConvention observationConvention, ObservationRegistry registry) {
return start(observationConvention, Context::new, registry);
}
/**
* Creates and starts an {@link Observation}. When the {@link ObservationRegistry} is
* null or the no-op registry, this fast returns a no-op {@link Observation} and skips
* the creation of the {@link Observation.Context}. This check avoids unnecessary
* {@link Observation.Context} creation, which is why it takes a {@link Supplier} for
* the context rather than the context directly. If the observation is not enabled
* (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned.
*
* Please check the javadoc of
* {@link Observation#createNotStarted(ObservationConvention, ObservationConvention, Supplier, ObservationRegistry)}
* method for the logic of choosing the convention.
*
* @param type of context
* @param observationConvention observation convention
* @param contextSupplier mutable context supplier
* @param registry observation registry
* @return started observation
*/
static Observation start(ObservationConvention observationConvention,
Supplier contextSupplier, ObservationRegistry registry) {
return createNotStarted(observationConvention, contextSupplier, registry).start();
}
/**
* Creates and starts an {@link Observation}. When the {@link ObservationRegistry} is
* null or the no-op registry, this fast returns a no-op {@link Observation} and skips
* the creation of the {@link Observation.Context}. This check avoids unnecessary
* {@link Observation.Context} creation, which is why it takes a {@link Supplier} for
* the context rather than the context directly. If the observation is not enabled
* (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned. Allows to set a custom {@link ObservationConvention} and requires to
* provide a default one if neither a custom nor a pre-configured one (via
* {@link ObservationRegistry.ObservationConfig#getObservationConvention(Context, ObservationConvention)})
* was found.
*
* Please check the javadoc of
* {@link Observation#createNotStarted(ObservationConvention, ObservationConvention, Supplier, ObservationRegistry)}
* method for the logic of choosing the convention.
*
* @param type of context
* @param registry observation registry
* @param contextSupplier the observation context supplier
* @param customConvention custom convention. If {@code null}, the default one will be
* picked.
* @param defaultConvention default convention when no custom convention was passed,
* nor a configured one was found
* @return started observation
*/
static Observation start(@Nullable ObservationConvention customConvention,
ObservationConvention defaultConvention, Supplier contextSupplier, ObservationRegistry registry) {
return createNotStarted(customConvention, defaultConvention, contextSupplier, registry).start();
}
/**
* Creates but does not start an {@link Observation}. Remember to call
* {@link Observation#start()} when you want the measurements to start. When no
* registry is passed or observation is not applicable will return a no-op
* observation.
*
* Please check the javadoc of
* {@link Observation#createNotStarted(ObservationConvention, ObservationConvention, Supplier, ObservationRegistry)}
* method for the logic of choosing the convention.
*
* @param observationConvention observation convention
* @param registry observation registry
* @return created but not started observation
*/
static Observation createNotStarted(ObservationConvention observationConvention,
ObservationRegistry registry) {
return createNotStarted(observationConvention, Context::new, registry);
}
/**
* Creates but does not start an {@link Observation}. Remember to call
* {@link Observation#start()} when you want the measurements to start. When the
* {@link ObservationRegistry} is null or the no-op registry, this fast returns a
* no-op {@link Observation} and skips the creation of the
* {@link Observation.Context}. This check avoids unnecessary
* {@link Observation.Context} creation, which is why it takes a {@link Supplier} for
* the context rather than the context directly. If the observation is not enabled
* (see
* {@link ObservationRegistry.ObservationConfig#observationPredicate(ObservationPredicate)
* ObservationConfig#observationPredicate}), a no-op observation will also be
* returned.
*
* Important! If you're using the
* {@link ObservationConvention#getContextualName(Context)} method to override the
* contextual name you MUST use a non {@code null} context (i.e. the
* {@code context} parameter of this method MUST NOT be {@code null}). The
* {@link ObservationConvention#getContextualName(Context)} requires a concrete type
* of {@link Context} to be passed and if you're not providing one we won't be able to
* initialize it ourselves.
*
*
* Please check the javadoc of
* {@link Observation#createNotStarted(ObservationConvention, ObservationConvention, Supplier, ObservationRegistry)}
* method for the logic of choosing the convention.
*
* @param type of context
* @param observationConvention observation convention
* @param contextSupplier mutable context supplier
* @param registry observation registry
* @return created but not started observation
*/
static Observation createNotStarted(ObservationConvention observationConvention,
Supplier contextSupplier, ObservationRegistry registry) {
if (registry == null || registry.isNoop() || observationConvention == NoopObservationConvention.INSTANCE) {
return NOOP;
}
T context = contextSupplier.get();
context.setParentFromCurrentObservation(registry);
if (!registry.observationConfig().isObservationEnabled(observationConvention.getName(), context)) {
return NOOP;
}
return new SimpleObservation(observationConvention, registry, context);
}
/**
* Sets the name that can be defined from the contents of the context. E.g. a span
* name should not be the default observation name but one coming from an HTTP
* request.
* @param contextualName contextual name
* @return this
*/
Observation contextualName(@Nullable String contextualName);
/**
* If you have access to a previously created {@link Observation} you can manually set
* the parent {@link Observation} using this method - that way you won't need to open
* scopes just to create a child observation.
*
* If you're using the {@link #openScope()} method then the parent observation will be
* automatically set, and you don't have to call this method.
* @param parentObservation parent observation to set
* @return this
*/
Observation parentObservation(@Nullable Observation parentObservation);
/**
* Adds a low cardinality key value. Low cardinality means that this key value will
* have a bounded number of possible values. A templated HTTP URL is a good example of
* such a key value (e.g. /foo/{userId}).
* @param keyValue key value
* @return this
*/
Observation lowCardinalityKeyValue(KeyValue keyValue);
/**
* Adds a low cardinality key value. Low cardinality means that this key value will
* have a bounded number of possible values. A templated HTTP URL is a good example of
* such a key value (e.g. /foo/{userId}).
* @param key key
* @param value value
* @return this
*/
default Observation lowCardinalityKeyValue(String key, String value) {
return lowCardinalityKeyValue(KeyValue.of(key, value));
}
/**
* Adds multiple low cardinality key value instances. Low cardinality means that the
* key value will have a bounded number of possible values. A templated HTTP URL is a
* good example of such a key value (e.g. /foo/{userId}).
* @param keyValues key value instances
* @return this
*/
default Observation lowCardinalityKeyValues(KeyValues keyValues) {
for (KeyValue keyValue : keyValues) {
lowCardinalityKeyValue(keyValue);
}
return this;
}
/**
* Adds a high cardinality key value. High cardinality means that this key value will
* have an unbounded number of possible values. An HTTP URL is a good example of such
* a key value (e.g. /foo/bar, /foo/baz etc.).
* @param keyValue key value
* @return this
*/
Observation highCardinalityKeyValue(KeyValue keyValue);
/**
* Adds a high cardinality key value. High cardinality means that this key value will
* have an unbounded number of possible values. An HTTP URL is a good example of such
* a key value (e.g. /foo/bar, /foo/baz etc.).
* @param key key
* @param value value
* @return this
*/
default Observation highCardinalityKeyValue(String key, String value) {
return highCardinalityKeyValue(KeyValue.of(key, value));
}
/**
* Adds multiple high cardinality key value instances. High cardinality means that the
* key value will have an unbounded number of possible values. An HTTP URL is a good
* example of such a key value (e.g. /foo/bar, /foo/baz etc.).
* @param keyValues key value instances
* @return this
*/
default Observation highCardinalityKeyValues(KeyValues keyValues) {
for (KeyValue keyValue : keyValues) {
highCardinalityKeyValue(keyValue);
}
return this;
}
/**
* Checks whether this {@link Observation} is no-op.
* @return {@code true} when this is a no-op observation
*/
default boolean isNoop() {
return this == NOOP;
}
/**
* Sets an observation convention that can be used to attach key values to the
* observation. Implementations should only set the convention when it supports (see:
* {@link ObservationConvention#supportsContext(Context)}) the current
* {@link Context}. WARNING: You must set the ObservationConvention to the Observation
* before it is started.
* @param observationConvention observation convention
* @return this
*/
Observation observationConvention(ObservationConvention> observationConvention);
/**
* Signals an error.
* @param error error
* @return this
*/
Observation error(Throwable error);
/**
* Signals an arbitrary {@link Event}.
* @param event event
* @return this
*/
Observation event(Event event);
/**
* Starts the observation. Remember to call this method, otherwise timing calculations
* will not take place.
* @return this
*/
Observation start();
/**
* Returns the context attached to this observation.
* @return corresponding context
*/
Context getContext();
/**
* Returns the context attached to this observation as a read only view.
* @return corresponding context
*/
@Override
default ContextView getContextView() {
return getContext();
}
/**
* Stop the observation. Remember to call this method, otherwise timing calculations
* won't be finished.
*/
void stop();
/**
* When put in scope, additional operations can take place by the
* {@link ObservationHandler}s such as putting entries in thread local.
* @return new scope
*/
Scope openScope();
/**
* Observes the passed {@link Runnable}, this means the followings:
*
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Runnable#run()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param runnable the {@link Runnable} to run
*/
default void observe(Runnable runnable) {
start();
try (Scope scope = openScope()) {
runnable.run();
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
default Runnable wrap(Runnable runnable) {
return () -> observe(runnable);
}
/**
* Observes the passed {@link CheckedRunnable}, this means the followings:
*
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link CheckedRunnable#run()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param checkedRunnable the {@link CheckedRunnable} to run
* @param type of exception thrown
*/
default void observeChecked(CheckedRunnable checkedRunnable) throws E {
start();
try (Scope scope = openScope()) {
checkedRunnable.run();
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
default CheckedRunnable wrapChecked(CheckedRunnable checkedRunnable) throws E {
return () -> observeChecked(checkedRunnable);
}
/**
* Observes the passed {@link Supplier}, this means the followings:
*
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Supplier#get()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param supplier the {@link Supplier} to call
* @param the type parameter of the {@link Supplier}
* @return the result from {@link Supplier#get()}
*/
@Nullable
default T observe(Supplier supplier) {
start();
try (Scope scope = openScope()) {
return supplier.get();
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
default Supplier wrap(Supplier supplier) {
return () -> observe(supplier);
}
/**
* Observes the passed {@link CheckedCallable}, this means the followings:
*
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link CheckedCallable#call()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param checkedCallable the {@link CheckedCallable} to call
* @param the return type of the {@link CheckedCallable}
* @param type of exception checkedCallable throws
* @return the result from {@link CheckedCallable#call()}
*/
@Nullable
default T observeChecked(CheckedCallable checkedCallable) throws E {
start();
try (Scope scope = openScope()) {
return checkedCallable.call();
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
default CheckedCallable wrapChecked(CheckedCallable checkedCallable) throws E {
return () -> observeChecked(checkedCallable);
}
/**
* Observes the passed {@link Function} which provides access to the {@link Context}.
*
* This means the followings:
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Function#apply(Object)} where it gets a {@link Context}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
*
* NOTE: When the {@link ObservationRegistry} is a noop, this function receives a
* default {@link Context} instance which is not the one that has been passed at
* {@link Observation} creation.
* @param function the {@link Function} to call
* @return the result from {@link Function#apply(Object)}
* @param the type of input {@link Context} to the function
* @param the type parameter of the {@link Function} return
* @since 1.11.0
* @deprecated scheduled for removal in 1.15.0, use {@code observe(...)} directly
*/
@SuppressWarnings({ "unused", "unchecked" })
@Nullable
@Deprecated
default T observeWithContext(Function function) {
InternalLoggerFactory.getInstance(Observation.class)
.warn("This method is deprecated. Please migrate to observation.observe(...)");
start();
try (Scope scope = openScope()) {
return function.apply((C) getContext());
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
/**
* Observes the passed {@link Function} which provides access to the {@link Context}.
*
* This means the followings:
*
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Function#apply(Object)} where it gets a {@link Context}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
*
* NOTE: When the {@link ObservationRegistry} is a noop, this function receives a
* default {@link Context} instance which is not the one that has been passed at
* {@link Observation} creation.
* @param function the {@link CheckedFunction} to call
* @return the result from {@link Function#apply(Object)}
* @param the type of input {@link Context} to the function
* @param the type of return to the function
* @param type of exception {@link CheckedFunction} throws
* @since 1.11.0
* @deprecated scheduled for removal in 1.15.0, use {@code observeChecked(...)}
* directly
*/
@SuppressWarnings({ "unused", "unchecked" })
@Nullable
@Deprecated
default T observeCheckedWithContext(CheckedFunction function)
throws E {
InternalLoggerFactory.getInstance(Observation.class)
.warn("This method is deprecated. Please migrate to observation.observeChecked(...)");
start();
try (Scope scope = openScope()) {
return function.apply((C) getContext());
}
catch (Throwable error) {
error(error);
throw error;
}
finally {
stop();
}
}
/**
* Wraps the given action in a scope and signals an error.
* @param runnable the {@link Runnable} to run
*/
@SuppressWarnings("unused")
default void scoped(Runnable runnable) {
try (Scope scope = openScope()) {
runnable.run();
}
catch (Exception exception) {
error(exception);
throw exception;
}
}
/**
* Wraps the given action in a scope and signals an error.
* @param checkedRunnable the {@link CheckedRunnable} to run
* @param type of exception thrown
*/
@SuppressWarnings("unused")
default void scopedChecked(CheckedRunnable checkedRunnable) throws E {
try (Scope scope = openScope()) {
checkedRunnable.run();
}
catch (Throwable throwable) {
error(throwable);
throw throwable;
}
}
/**
* Wraps the given action in a scope and signals an error.
* @param supplier the {@link Supplier} to call
* @param the return type of the {@link Supplier}
* @return the result from {@link Supplier#get()}
*/
@SuppressWarnings("unused")
default T scoped(Supplier supplier) {
try (Scope scope = openScope()) {
return supplier.get();
}
catch (Exception exception) {
error(exception);
throw exception;
}
}
/**
* Wraps the given action in a scope and signals an error.
* @param checkedCallable the {@link CheckedCallable} to call
* @param the return type of the {@link CheckedCallable}
* @param type of exception checkedCallable throws
* @return the result from {@link CheckedCallable#call()}
*/
@SuppressWarnings("unused")
default T scopedChecked(CheckedCallable checkedCallable) throws E {
try (Scope scope = openScope()) {
return checkedCallable.call();
}
catch (Throwable error) {
error(error);
throw error;
}
}
/**
* Tries to run the action against an Observation. If the Observation is null, we just
* run the action, otherwise we run the action in scope.
* @param parent observation, potentially {@code null}
* @param action action to run
*/
static void tryScoped(@Nullable Observation parent, Runnable action) {
if (parent != null) {
parent.scoped(action);
}
else {
action.run();
}
}
/**
* Tries to run the action against an Observation. If the Observation is null, we just
* run the action, otherwise we run the action in scope.
* @param parent observation, potentially {@code null}
* @param checkedRunnable the {@link CheckedRunnable} to run
* @param type of exception checkedRunnable throws
*/
static void tryScopedChecked(@Nullable Observation parent, CheckedRunnable checkedRunnable)
throws E {
if (parent != null) {
parent.scopedChecked(checkedRunnable);
}
else {
checkedRunnable.run();
}
}
/**
* Tries to run the action against an Observation. If the Observation is null, we just
* run the action, otherwise we run the action in scope.
* @param parent observation, potentially {@code null}
* @param action action to run
* @return result of the action
*/
static T tryScoped(@Nullable Observation parent, Supplier action) {
if (parent != null) {
return parent.scoped(action);
}
return action.get();
}
/**
* Tries to run the action against an Observation. If the Observation is null, we just
* run the action, otherwise we run the action in scope.
* @param parent observation, potentially {@code null}
* @param checkedCallable the {@link CheckedCallable} to call
* @param the return type of the {@link CheckedCallable}
* @param type of exception checkedCallable throws
* @return the result from {@link CheckedCallable#call()}
*/
static T tryScopedChecked(@Nullable Observation parent,
CheckedCallable checkedCallable) throws E {
if (parent != null) {
return parent.scopedChecked(checkedCallable);
}
return checkedCallable.call();
}
/**
* Scope represent an action within which certain resources (e.g. tracing context) are
* put in scope (e.g. in a ThreadLocal). When the scope is closed the resources will
* be removed from the scope.
*
* @since 1.10.0
*/
interface Scope extends AutoCloseable {
/**
* No-op scope.
*/
Scope NOOP = NoopObservation.NoopScope.INSTANCE;
/**
* Current observation available within this scope.
* @return current observation that this scope was created by
*/
Observation getCurrentObservation();
/**
* Parent scope.
* @return previously opened scope when this one was created
* @since 1.10.8
*/
@Nullable
default Observation.Scope getPreviousObservationScope() {
return null;
}
/**
* Clears the current scope and notifies the handlers that the scope was closed.
* You don't need to call this method manually. If you use try-with-resource, it
* will call this for you. Please only call this method if you know what you are
* doing and your use-case demands the usage of it.
*/
@Override
void close();
/**
* Resets the current scope. The effect of calling this method should be clearing
* all related thread local entries.
*
* You don't need to call this method in most of the cases. Please only call this
* method if you know what you are doing and your use-case demands the usage of
* it.
* @since 1.10.4
*/
void reset();
/**
* This method assumes that all previous scopes got {@link #reset()}. That means
* that in thread locals there are no more entries, and now we can make this scope
* current.
*
* Making this scope current can lead to additional work such as injecting
* variables to MDC.
*
* You don't need to call this method in most of the cases. Please only call this
* method if you know what you are doing and your use-case demands the usage of
* it.
* @since 1.10.6
*/
void makeCurrent();
/**
* Checks whether this {@link Scope} is no-op.
* @return {@code true} when this is a no-op scope
*/
default boolean isNoop() {
return this == NOOP;
}
}
/**
* A mutable holder of data required by an {@link ObservationHandler}. When extended
* you can provide your own, custom information to be processed by the handlers.
*
* @since 1.10.0
*/
@SuppressWarnings("unchecked")
class Context implements ContextView {
private final Map