Instrumentation API and Javadoc cleanup (#5954)

This commit is contained in:
Mateusz Rzeszutek 2022-05-10 07:13:45 +02:00 committed by GitHub
parent 45d0518d69
commit 802f5aa3ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 270 additions and 212 deletions

View File

@ -4,7 +4,7 @@ The `Instrumenter` encapsulates the entire logic for gathering telemetry, from c
to starting and ending spans, to recording values using metrics instruments. The `Instrumenter`
public API contains only three methods: `shouldStart()`, `start()` and `end()`. The class is
designed to decorate the actual invocation of the instrumented library code; `shouldStart()`
and `start()`methods are to be called at the start of the request processing, while `end()` must be
and `start()` methods are to be called at the start of the request processing, while `end()` must be
called when processing ends and a response arrives, or when it fails with an error.
The `Instrumenter` is a generic class parameterized with `REQUEST` and `RESPONSE` types. They
represent the input and output of the instrumented operation. `Instrumenter` can be configured with
@ -254,8 +254,8 @@ The `SpanLinksExtractor` interface can be used to add links to other spans when
operation starts. It has a single `extract()` method that receives the following arguments:
* A `SpanLinkBuilder` that can be used to add the links.
* The parent `Context` that was passed in to `Instrumenter.start()`.
* The `REQUEST` instance that was passed in to `Instrumenter.start()`.
* The parent `Context` that was passed in to `Instrumenter#start()`.
* The `REQUEST` instance that was passed in to `Instrumenter#start()`.
You can read more about span links and their use
cases [here](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#links-between-spans).
@ -300,7 +300,7 @@ Consider the following example:
class MyErrorCauseExtractor implements ErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
if (error instanceof MyLibWrapperException && error.getCause() != null) {
error = error.getCause();
}
@ -361,10 +361,10 @@ and `RequestListener` interfaces. `RequestMetrics` is simply a factory interface
the `RequestListener` - it receives an OpenTelemetry `Meter` and returns a new listener.
The `RequestListener` contains two methods:
* `start()` that gets executed when the instrumented operation starts. It returns a `Context` - it
can be used to store internal metrics state that should be propagated to the `end()` call, if
* `onStart()` that gets executed when the instrumented operation starts. It returns a `Context` - it
can be used to store internal metrics state that should be propagated to the `onEnd()` call, if
needed.
* `end()` that gets executed when the instrumented operation ends.
* `onEnd()` that gets executed when the instrumented operation ends.
Both methods accept a `Context`, an instance of `Attributes` that contains either attributes
computed on instrumented operation start or end, and the start and end nanoseconds timestamp that
@ -389,13 +389,13 @@ class MyRequestMetrics implements RequestListener {
}
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
activeRequests.add(1, startAttributes);
return context.with(new MyMetricsState(startAttributes));
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
MyMetricsState state = MyMetricsState.get(context);
activeRequests.add(1, state.startAttributes());
}
@ -404,9 +404,9 @@ class MyRequestMetrics implements RequestListener {
The sample class listed above implements the `RequestMetrics` factory interface in the
static `get()` method. The listener implementation uses a counter to measure the number of requests
that are currently in flight. Notice that the state between `start()` and `end()` method is shared
using the `MyMetricsState` class (a mostly trivial data class, not listed in the example above),
passed between the methods using the `Context`.
that are currently in flight. Notice that the state between `onStart()` and `onEnd()` method is
shared using the `MyMetricsState` class (a mostly trivial data class, not listed in the example
above), passed between the methods using the `Context`.
You can add `RequestMetrics` to the `InstrumenterBuilder` using the `addRequestMetrics()` method.
@ -414,7 +414,7 @@ You can add `RequestMetrics` to the `InstrumenterBuilder` using the `addRequestM
In some rare cases, there is a need to enrich the `Context` before it is returned from
the `Instrumenter#start()` method. The `ContextCustomizer` interface can be used to achieve that. It
exposes a single `start()` method that accepts a `Context`, a `REQUEST` and `Attributes` extracted
exposes a single `onStart()` method that accepts a `Context`, a `REQUEST` and `Attributes` extracted
on the operation start, and returns a modified `Context`.
Consider the following example:
@ -423,7 +423,7 @@ Consider the following example:
class MyContextCustomizer implements ContextCustomizer<Request> {
@Override
public Context start(Context context, Request request, Attributes startAttributes) {
public Context onStart(Context context, Request request, Attributes startAttributes) {
return context.with(new InProcessingAttributesHolder());
}
}

View File

@ -54,14 +54,14 @@ public final class HttpClientMetrics implements RequestListener {
}
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
return context.with(
HTTP_CLIENT_REQUEST_METRICS_STATE,
new AutoValue_HttpClientMetrics_State(startAttributes, startNanos));
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
State state = context.get(HTTP_CLIENT_REQUEST_METRICS_STATE);
if (state == null) {
logger.log(

View File

@ -64,7 +64,7 @@ public final class HttpServerMetrics implements RequestListener {
}
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
activeRequests.add(1, applyActiveRequestsView(startAttributes), context);
return context.with(
@ -73,7 +73,7 @@ public final class HttpServerMetrics implements RequestListener {
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
State state = context.get(HTTP_SERVER_REQUEST_METRICS_STATE);
if (state == null) {
logger.log(

View File

@ -54,14 +54,14 @@ public final class RpcClientMetrics implements RequestListener {
}
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
return context.with(
RPC_CLIENT_REQUEST_METRICS_STATE,
new AutoValue_RpcClientMetrics_State(startAttributes, startNanos));
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
State state = context.get(RPC_CLIENT_REQUEST_METRICS_STATE);
if (state == null) {
logger.log(

View File

@ -54,14 +54,14 @@ public final class RpcServerMetrics implements RequestListener {
}
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
return context.with(
RPC_SERVER_REQUEST_METRICS_STATE,
new AutoValue_RpcServerMetrics_State(startAttributes, startNanos));
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
State state = context.get(RPC_SERVER_REQUEST_METRICS_STATE);
if (state == null) {
logger.log(

View File

@ -59,15 +59,15 @@ class HttpClientMetricsTest {
TraceFlags.getSampled(),
TraceState.getDefault())));
Context context1 = listener.start(parent, requestAttributes, nanos(100));
Context context1 = listener.onStart(parent, requestAttributes, nanos(100));
assertThat(metricReader.collectAllMetrics()).isEmpty();
Context context2 = listener.start(Context.root(), requestAttributes, nanos(150));
Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150));
assertThat(metricReader.collectAllMetrics()).isEmpty();
listener.end(context1, responseAttributes, nanos(250));
listener.onEnd(context1, responseAttributes, nanos(250));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)
@ -95,7 +95,7 @@ class HttpClientMetricsTest {
.hasSpanId("090a0b0c0d0e0f00");
}));
listener.end(context2, responseAttributes, nanos(300));
listener.onEnd(context2, responseAttributes, nanos(300));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)

View File

@ -57,7 +57,7 @@ class HttpServerMetricsTest {
TraceFlags.getSampled(),
TraceState.getDefault())));
Context context1 = listener.start(parent, requestAttributes, nanos(100));
Context context1 = listener.onStart(parent, requestAttributes, nanos(100));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)
@ -85,7 +85,7 @@ class HttpServerMetricsTest {
.hasSpanId("090a0b0c0d0e0f00");
}));
Context context2 = listener.start(Context.root(), requestAttributes, nanos(150));
Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)
@ -97,7 +97,7 @@ class HttpServerMetricsTest {
.points()
.satisfiesExactly(point -> assertThat(point).hasValue(2)));
listener.end(context1, responseAttributes, nanos(250));
listener.onEnd(context1, responseAttributes, nanos(250));
assertThat(metricReader.collectAllMetrics())
.hasSize(2)
@ -132,7 +132,7 @@ class HttpServerMetricsTest {
.hasSpanId("090a0b0c0d0e0f00");
}));
listener.end(context2, responseAttributes, nanos(300));
listener.onEnd(context2, responseAttributes, nanos(300));
assertThat(metricReader.collectAllMetrics())
.hasSize(2)
@ -169,8 +169,8 @@ class HttpServerMetricsTest {
Context parentContext = Context.root();
// when
Context context = listener.start(parentContext, requestAttributes, nanos(100));
listener.end(context, responseAttributes, nanos(200));
Context context = listener.onStart(parentContext, requestAttributes, nanos(100));
listener.onEnd(context, responseAttributes, nanos(200));
// then
assertThat(metricReader.collectAllMetrics())

View File

@ -63,15 +63,15 @@ class RpcClientMetricsTest {
TraceFlags.getSampled(),
TraceState.getDefault())));
Context context1 = listener.start(parent, requestAttributes, nanos(100));
Context context1 = listener.onStart(parent, requestAttributes, nanos(100));
assertThat(metricReader.collectAllMetrics()).isEmpty();
Context context2 = listener.start(Context.root(), requestAttributes, nanos(150));
Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150));
assertThat(metricReader.collectAllMetrics()).isEmpty();
listener.end(context1, responseAttributes1, nanos(250));
listener.onEnd(context1, responseAttributes1, nanos(250));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)
@ -100,7 +100,7 @@ class RpcClientMetricsTest {
.hasSpanId("090a0b0c0d0e0f00");
}));
listener.end(context2, responseAttributes2, nanos(300));
listener.onEnd(context2, responseAttributes2, nanos(300));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)

View File

@ -63,15 +63,15 @@ class RpcServerMetricsTest {
TraceFlags.getSampled(),
TraceState.getDefault())));
Context context1 = listener.start(parent, requestAttributes, nanos(100));
Context context1 = listener.onStart(parent, requestAttributes, nanos(100));
assertThat(metricReader.collectAllMetrics()).isEmpty();
Context context2 = listener.start(Context.root(), requestAttributes, nanos(150));
Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150));
assertThat(metricReader.collectAllMetrics()).isEmpty();
listener.end(context1, responseAttributes1, nanos(250));
listener.onEnd(context1, responseAttributes1, nanos(250));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)
@ -99,7 +99,7 @@ class RpcServerMetricsTest {
.hasSpanId("090a0b0c0d0e0f00");
}));
listener.end(context2, responseAttributes2, nanos(300));
listener.onEnd(context2, responseAttributes2, nanos(300));
assertThat(metricReader.collectAllMetrics())
.hasSize(1)

View File

@ -18,13 +18,13 @@ import javax.annotation.Nullable;
/**
* Represents the global agent configuration consisting of system properties, environment variables,
* contents of the agent configuration file and properties defined by the {@code
* ConfigPropertySource} SPI (see {@code ConfigInitializer} and {@link ConfigBuilder}).
* ConfigPropertySource} SPI implementations.
*
* <p>In case any {@code get*()} method variant gets called for the same property more than once
* (e.g. each time an advice class executes) it is suggested to cache the result instead of
* repeatedly calling {@link Config}. Agent configuration does not change during the runtime so
* retrieving the property once and storing its result in e.g. static final field allows JIT to do
* its magic and remove some code branches.
* retrieving the property once and storing its result in a static final field allows JIT to do its
* magic and remove some code branches.
*/
@AutoValue
public abstract class Config {
@ -47,9 +47,8 @@ public abstract class Config {
Config() {}
/**
* Sets the agent configuration singleton. This method is only supposed to be called once, from
* the agent classloader just before the first instrumentation is loaded (and before {@link
* Config#get()} is used for the first time).
* Sets the agent configuration singleton. This method is only supposed to be called once, during
* the agent initialization, just before {@link Config#get()} is used for the first time.
*
* <p>This method is internal and is hence not for public use. Its API is unstable and can change
* at any time.
@ -73,7 +72,10 @@ public abstract class Config {
return instance;
}
/** Returns all properties stored in this instance. The returned map is unmodifiable. */
/**
* Returns all properties stored in this {@link Config} instance. The returned map is
* unmodifiable.
*/
public abstract Map<String, String> getAllProperties();
/**
@ -102,8 +104,8 @@ public abstract class Config {
}
/**
* Returns a integer-valued configuration property or {@code defaultValue} if a property with name
* {@code name} has not been configured or when parsing has failed.
* Returns an integer-valued configuration property or {@code defaultValue} if a property with
* name {@code name} has not been configured or when parsing has failed.
*/
public int getInt(String name, int defaultValue) {
return safeGetTypedProperty(name, ConfigValueParsers::parseInt, defaultValue);
@ -140,6 +142,8 @@ public abstract class Config {
* </ul>
*
* <p>If no unit is specified, milliseconds is the assumed duration unit.
*
* <p>Examples: 10s, 20ms, 5000
*/
public Duration getDuration(String name, Duration defaultValue) {
return safeGetTypedProperty(name, ConfigValueParsers::parseDuration, defaultValue);
@ -148,7 +152,7 @@ public abstract class Config {
/**
* Returns a list-valued configuration property or {@code defaultValue} if a property with name
* {@code name} has not been configured. The format of the original value must be comma-separated,
* e.g. {@code one,two,three}.
* e.g. {@code one,two,three}. The returned list is unmodifiable.
*/
public List<String> getList(String name, List<String> defaultValue) {
return safeGetTypedProperty(name, ConfigValueParsers::parseList, defaultValue);
@ -158,7 +162,7 @@ public abstract class Config {
* Returns a map-valued configuration property or {@code defaultValue} if a property with name
* {@code name} has not been configured or when parsing has failed. The format of the original
* value must be comma-separated for each key, with an '=' separating the key and value, e.g.
* {@code key=value,anotherKey=anotherValue}.
* {@code key=value,anotherKey=anotherValue}. The returned map is unmodifiable.
*/
public Map<String, String> getMap(String name, Map<String, String> defaultValue) {
return safeGetTypedProperty(name, ConfigValueParsers::parseMap, defaultValue);

View File

@ -19,7 +19,7 @@ public final class ConfigBuilder {
/** Constructs a new {@link ConfigBuilder}. */
ConfigBuilder() {}
/** Adds a single property named to the config. */
/** Adds a single property to the config. */
public ConfigBuilder addProperty(String name, @Nullable String value) {
if (value != null) {
allProperties.put(NamingConvention.DOT.normalize(name), value);
@ -27,23 +27,24 @@ public final class ConfigBuilder {
return this;
}
/** Adds all properties from the passed {@code properties} to the config. */
/** Adds all properties from the passed {@link Properties} to the config. */
public ConfigBuilder addProperties(Properties properties) {
for (String name : properties.stringPropertyNames()) {
allProperties.put(NamingConvention.DOT.normalize(name), properties.getProperty(name));
addProperty(name, properties.getProperty(name));
}
return this;
}
/** Adds all properties from the passed {@code properties} to the config. */
/** Adds all properties from the passed {@link Map} to the config. */
public ConfigBuilder addProperties(Map<String, String> properties) {
return fromConfigMap(properties, NamingConvention.DOT);
}
/**
* Adds environment variables, converted to Java property naming convention, to the config.
* Adds environment variables (converted to the Java property naming convention) to the config.
*
* <p>Environment variable names are lowercased, and the underscores are replaced by dots.
* <p>Environment variable names are converted to lower case, with all underscores replaced by
* dots.
*/
public ConfigBuilder addEnvironmentVariables() {
return fromConfigMap(System.getenv(), NamingConvention.ENV_VAR);

View File

@ -5,10 +5,12 @@
package io.opentelemetry.instrumentation.api.config;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import java.time.Duration;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -55,26 +57,30 @@ final class ConfigValueParsers {
}
static List<String> parseList(@SuppressWarnings("unused") String propertyName, String value) {
return Collections.unmodifiableList(filterBlanks(value.split(",")));
return unmodifiableList(filterBlanks(value.split(",")));
}
static Map<String, String> parseMap(String propertyName, String value) {
return parseList(propertyName, value).stream()
.map(keyValuePair -> trim(keyValuePair.split("=", 2)))
.map(
splitKeyValuePairs -> {
if (splitKeyValuePairs.size() != 2 || splitKeyValuePairs.get(0).isEmpty()) {
throw new ConfigParsingException(
"Invalid map property: " + propertyName + "=" + value);
}
return new AbstractMap.SimpleImmutableEntry<>(
splitKeyValuePairs.get(0), splitKeyValuePairs.get(1));
})
// If duplicate keys, prioritize later ones similar to duplicate system properties on a
// Java command line.
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (first, next) -> next, LinkedHashMap::new));
return unmodifiableMap(
parseList(propertyName, value).stream()
.map(keyValuePair -> trim(keyValuePair.split("=", 2)))
.map(
splitKeyValuePairs -> {
if (splitKeyValuePairs.size() != 2 || splitKeyValuePairs.get(0).isEmpty()) {
throw new ConfigParsingException(
"Invalid map property: " + propertyName + "=" + value);
}
return new AbstractMap.SimpleImmutableEntry<>(
splitKeyValuePairs.get(0), splitKeyValuePairs.get(1));
})
// If duplicate keys, prioritize later ones similar to duplicate system properties on a
// Java command line.
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(first, next) -> next,
LinkedHashMap::new)));
}
private static List<String> filterBlanks(String[] values) {

View File

@ -6,17 +6,18 @@
package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import javax.annotation.Nullable;
/**
* Extractor of {@link io.opentelemetry.api.common.Attributes} for a given request and response.
* Will be called {@linkplain #onStart(AttributesBuilder, Context, Object) on start} with just the
* {@link REQUEST} and again {@linkplain #onEnd(AttributesBuilder, Context, Object, Object,
* Throwable) on end} with both {@link REQUEST} and {@link RESPONSE} to allow populating attributes
* at each stage of a request's lifecycle. It is best to populate as much as possible in {@link
* #onStart(AttributesBuilder, Context, Object)} to have it available during sampling.
* Extractor of {@link Attributes} for a given request and response. Will be called {@linkplain
* #onStart(AttributesBuilder, Context, Object) on start} with just the {@link REQUEST} and again
* {@linkplain #onEnd(AttributesBuilder, Context, Object, Object, Throwable) on end} with both
* {@link REQUEST} and {@link RESPONSE} to allow populating attributes at each stage of a request's
* lifecycle. It is best to populate as many attributes as possible in {@link
* #onStart(AttributesBuilder, Context, Object)} to have them available during span sampling.
*/
public interface AttributesExtractor<REQUEST, RESPONSE> {
@ -39,7 +40,7 @@ public interface AttributesExtractor<REQUEST, RESPONSE> {
/**
* Returns an {@link AttributesExtractor} implementation that always extracts the provided
* constant value.
* constant value on the request start.
*/
static <REQUEST, RESPONSE, T> AttributesExtractor<REQUEST, RESPONSE> constant(
AttributeKey<T> attributeKey, T attributeValue) {

View File

@ -9,16 +9,14 @@ import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
/**
* Customizer of the {@link Context}. Instrumented libraries will call {@link #start(Context,
* Object, Attributes)} during {@link Instrumenter#start(Context, Object)}, allowing customization
* of the {@link Context} that is returned from that method.
* Customizer of the {@link Context}. The {@link #onStart(Context, Object, Attributes)} method will
* be called during the {@linkplain Instrumenter#start(Context, Object) <code>Instrumenter</code>
* start}, allowing customization of the {@link Context} just before the {@link Instrumenter}
* returns it.
*/
@FunctionalInterface
public interface ContextCustomizer<REQUEST> {
/**
* Context customizer method that is called during {@link Instrumenter#start(Context, Object)},
* allowing customization of the {@link Context} that is returned from that method.
*/
Context start(Context context, REQUEST request, Attributes startAttributes);
/** Allows to customize the operation {@link Context}. */
Context onStart(Context context, REQUEST request, Attributes startAttributes);
}

View File

@ -13,7 +13,8 @@ package io.opentelemetry.instrumentation.api.instrumenter;
@FunctionalInterface
public interface ErrorCauseExtractor {
Throwable extractCause(Throwable error);
/** Strips away the wrapper exceptions and returns the actual cause of a request error. */
Throwable extract(Throwable error);
/**
* Returns a {@link ErrorCauseExtractor} which unwraps common standard library wrapping

View File

@ -6,7 +6,6 @@
package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanKind;
@ -21,35 +20,38 @@ import java.util.ListIterator;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
// TODO(anuraaga): Need to define what are actually useful knobs, perhaps even providing a
// base-class
// for instrumentation library builders.
/**
* An instrumenter of the start and end of a request/response lifecycle. Almost all instrumentation
* of libraries falls into modeling start and end, generating observability signals from these such
* as a tracing {@link Span}, or metrics such as the duration taken, active requests, etc. When
* instrumenting a library, there will generally be four steps.
* The {@link Instrumenter} encapsulates the entire logic for gathering telemetry, from collecting
* the data, to starting and ending spans, to recording values using metrics instruments.
*
* <p>An {@link Instrumenter} is called at the start and the end of a request/response lifecycle.
* When instrumenting a library, there will generally be four steps.
*
* <ul>
* <li>Create an {@link Instrumenter} using {@link InstrumenterBuilder}. Use the builder to
* configure any library-specific customizations, and also expose useful knobs to your user.
* <li>Call {@link Instrumenter#shouldStart(Context, Object)} and do not proceed if {@code false}.
* <li>Call {@link Instrumenter#shouldStart(Context, Object)} and do not proceed if it returns
* {@code false}.
* <li>Call {@link Instrumenter#start(Context, Object)} at the beginning of a request.
* <li>Call {@link Instrumenter#end(Context, Object, Object, Throwable)} at the end of a request.
* </ul>
*
* <p>For more detailed information about using the {@link Instrumenter} see the <a
* href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/contributing/using-instrumenter-api.md">Using
* the Instrumenter API</a> page.
*/
public class Instrumenter<REQUEST, RESPONSE> {
/**
* Returns a new {@link InstrumenterBuilder}.
*
* <p>The {@code instrumentationName} is the name of the instrumentation library, not the name of
* the instrument*ed* library. The value passed in this parameter should uniquely identify the
* instrumentation library so that during troubleshooting it's possible to pinpoint what tracer
* produced problematic telemetry.
* <p>The {@code instrumentationName} indicates the instrumentation library name, not the
* instrument<b>ed</b> library name. The value passed in this parameter should uniquely identify
* the instrumentation library so that during troubleshooting it's possible to determine where the
* telemetry came from.
*
* <p>In this project we use a convention to encode the minimum supported version of the
* instrument*ed* library into the instrumentation name, for example {@code
* <p>In OpenTelemetry instrumentations we use a convention to encode the minimum supported
* version of the instrument<b>ed</b> library into the instrumentation name, for example {@code
* io.opentelemetry.apache-httpclient-4.0}. This way, if there are different instrumentations for
* different library versions it's easy to find out which instrumentations produced the telemetry
* data.
@ -95,10 +97,14 @@ public class Instrumenter<REQUEST, RESPONSE> {
}
/**
* Returns whether instrumentation should be applied for the {@link REQUEST}. If {@code true},
* call {@link #start(Context, Object)} and {@link #end(Context, Object, Object, Throwable)}
* around the operation being instrumented, or if {@code false} execute the operation directly
* without calling those methods.
* Determines whether the operation should be instrumented for telemetry or not. If the return
* value is {@code true}, call {@link #start(Context, Object)} and {@link #end(Context, Object,
* Object, Throwable)} around the instrumented operation; if the return value is false {@code
* false} execute the operation directly without calling those methods.
*
* <p>The {@code parentContext} is the parent of the resulting instrumented operation and should
* usually be {@link Context#current() Context.current()}. The {@code request} is the request
* object of this operation.
*/
public boolean shouldStart(Context parentContext, REQUEST request) {
if (!enabled) {
@ -114,11 +120,13 @@ public class Instrumenter<REQUEST, RESPONSE> {
}
/**
* Starts a new operation to be instrumented. The {@code parentContext} is the parent of the
* resulting instrumented operation and should usually be {@code Context.current()}. The {@code
* request} is the request object of this operation. The returned {@link Context} should be
* propagated along with the operation and passed to {@link #end(Context, Object, Object,
* Throwable)} when it is finished.
* Starts a new instrumented operation. The returned {@link Context} should be propagated along
* with the operation and passed to the {@link #end(Context, Object, Object, Throwable)} method
* when it is finished.
*
* <p>The {@code parentContext} is the parent of the resulting instrumented operation and should
* usually be {@link Context#current() Context.current()}. The {@code request} is the request
* object of this operation.
*/
public Context start(Context parentContext, REQUEST request) {
SpanKind spanKind = spanKindExtractor.extract(request);
@ -139,11 +147,10 @@ public class Instrumenter<REQUEST, RESPONSE> {
spanLinksExtractor.extract(spanLinksBuilder, parentContext, request);
}
UnsafeAttributes attributesBuilder = new UnsafeAttributes();
UnsafeAttributes attributes = new UnsafeAttributes();
for (AttributesExtractor<? super REQUEST, ? super RESPONSE> extractor : attributesExtractors) {
extractor.onStart(attributesBuilder, parentContext, request);
extractor.onStart(attributes, parentContext, request);
}
Attributes attributes = attributesBuilder;
Context context = parentContext;
@ -152,13 +159,13 @@ public class Instrumenter<REQUEST, RESPONSE> {
context = context.with(span);
for (ContextCustomizer<? super REQUEST> contextCustomizer : contextCustomizers) {
context = contextCustomizer.start(context, request, attributes);
context = contextCustomizer.onStart(context, request, attributes);
}
if (!requestListeners.isEmpty()) {
long startNanos = getNanos(startTime);
for (RequestListener requestListener : requestListeners) {
context = requestListener.start(context, attributes, startNanos);
context = requestListener.onStart(context, attributes, startNanos);
}
}
@ -170,17 +177,21 @@ public class Instrumenter<REQUEST, RESPONSE> {
}
/**
* Ends an instrumented operation. The {@link Context} must be what was returned from {@link
* #start(Context, Object)}. {@code request} is the request object of the operation, {@code
* Ends an instrumented operation. It is of extreme importance for this method to be always called
* after {@link #start(Context, Object) start()}. Calling {@code start()} without later {@code
* end()} will result in inaccurate or wrong telemetry and context leaks.
*
* <p>The {@code context} must be the same value that was returned from {@link #start(Context,
* Object)}. The {@code request} parameter is the request object of the operation, {@code
* response} is the response object of the operation, and {@code error} is an exception that was
* thrown by the operation, or {@code null} if none was thrown.
* thrown by the operation or {@code null} if no error occurred.
*/
public void end(
Context context, REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) {
Span span = Span.fromContext(context);
if (error != null) {
error = errorCauseExtractor.extractCause(error);
error = errorCauseExtractor.extract(error);
span.recordException(error);
}
@ -200,7 +211,7 @@ public class Instrumenter<REQUEST, RESPONSE> {
ListIterator<? extends RequestListener> i =
requestListeners.listIterator(requestListeners.size());
while (i.hasPrevious()) {
i.previous().end(context, attributes, endNanos);
i.previous().onEnd(context, attributes, endNanos);
}
}

View File

@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
@ -29,9 +30,10 @@ import java.util.stream.Stream;
import javax.annotation.Nullable;
/**
* A builder of {@link Instrumenter}. Instrumentation libraries should generally expose their own
* builder with controls that are appropriate for that library and delegate to this to create the
* {@link Instrumenter}.
* A builder of an {@link Instrumenter}.
*
* <p>Instrumentation libraries should generally expose their own builder with controls that are
* appropriate for that library and delegate to this class to create the {@link Instrumenter}.
*/
public final class InstrumenterBuilder<REQUEST, RESPONSE> {
@ -70,11 +72,11 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Sets the instrumentation version that'll be associated with all telemetry produced by this
* Sets the instrumentation version that will be associated with all telemetry produced by this
* {@link Instrumenter}.
*
* @param instrumentationVersion is the version of the instrumentation library, not the version of
* the instrument*ed* library.
* the instrument<b>ed</b> library.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> setInstrumentationVersion(
String instrumentationVersion) {
@ -83,7 +85,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Sets the OpenTelemetry schema URL that'll be associated with all telemetry produced by this
* Sets the OpenTelemetry schema URL that will be associated with all telemetry produced by this
* {@link Instrumenter}.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> setSchemaUrl(String schemaUrl) {
@ -92,7 +94,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Sets the {@link SpanStatusExtractor} to use to determine the {@link StatusCode} for a response.
* Sets the {@link SpanStatusExtractor} that will determine the {@link StatusCode} for a response.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> setSpanStatusExtractor(
SpanStatusExtractor<? super REQUEST, ? super RESPONSE> spanStatusExtractor) {
@ -100,14 +102,16 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
return this;
}
/** Adds a {@link AttributesExtractor} to extract attributes from requests and responses. */
/**
* Adds a {@link AttributesExtractor} that will extract attributes from requests and responses.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> addAttributesExtractor(
AttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
this.attributesExtractors.add(attributesExtractor);
return this;
}
/** Adds {@link AttributesExtractor}s to extract attributes from requests and responses. */
/** Adds {@link AttributesExtractor}s that will extract attributes from requests and responses. */
public InstrumenterBuilder<REQUEST, RESPONSE> addAttributesExtractors(
Iterable<? extends AttributesExtractor<? super REQUEST, ? super RESPONSE>>
attributesExtractors) {
@ -115,7 +119,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
return this;
}
/** Adds {@link AttributesExtractor}s to extract attributes from requests and responses. */
/** Adds {@link AttributesExtractor}s that will extract attributes from requests and responses. */
@SafeVarargs
@SuppressWarnings("varargs")
public final InstrumenterBuilder<REQUEST, RESPONSE> addAttributesExtractors(
@ -123,7 +127,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
return addAttributesExtractors(Arrays.asList(attributesExtractors));
}
/** Adds a {@link SpanLinksExtractor} to extract span links from requests. */
/** Adds a {@link SpanLinksExtractor} that will extract span links from requests. */
public InstrumenterBuilder<REQUEST, RESPONSE> addSpanLinksExtractor(
SpanLinksExtractor<REQUEST> spanLinksExtractor) {
spanLinksExtractors.add(spanLinksExtractor);
@ -131,7 +135,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Adds a {@link ContextCustomizer} to customize the context during {@link
* Adds a {@link ContextCustomizer} that will customize the context during {@link
* Instrumenter#start(Context, Object)}.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> addContextCustomizer(
@ -140,21 +144,24 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
return this;
}
/** Adds a {@link RequestListener} which will be called for request start and end. */
/** Adds a {@link RequestListener} that will be called when request processing starts and ends. */
public InstrumenterBuilder<REQUEST, RESPONSE> addRequestListener(RequestListener listener) {
requestListeners.add(listener);
return this;
}
/** Adds a {@link RequestMetrics} whose metrics will be recorded for request start and end. */
/**
* Adds a {@link RequestMetrics} that will produce a {@link RequestListener} capturing the
* requests processing metrics.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> addRequestMetrics(RequestMetrics factory) {
requestMetrics.add(factory);
return this;
}
/**
* Sets the {@link ErrorCauseExtractor} to extract the root cause from an exception handling the
* request.
* Sets the {@link ErrorCauseExtractor} that will extract the root cause of an error thrown during
* request processing.
*/
public InstrumenterBuilder<REQUEST, RESPONSE> setErrorCauseExtractor(
ErrorCauseExtractor errorCauseExtractor) {
@ -163,14 +170,14 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Sets the {@link TimeExtractor} to extract the timestamp marking the start and end of
* processing. If unset, the constructed instrumenter will defer determining start and end
* timestamps to the OpenTelemetry SDK.
* Sets the {@link TimeExtractor} that will extract the timestamp marking the start and the end of
* request processing. If unset, the constructed {@link Instrumenter} will defer determining the
* start and end timestamps to the OpenTelemetry SDK.
*
* <p>Note: if metrics are generated by the Instrumenter, the start and end times from the {@link
* TimeExtractor} will be used to generate any duration metrics, but the internal metric timestamp
* (when it occurred) will always be stamped with "now" when the metric is recorded (i.e. there is
* no way to back date a metric recording).
* <p>Note: if metrics are generated by the {@link Instrumenter}, the start and end times from the
* {@link TimeExtractor} will be used to generate any duration metrics, but the internal metric
* timestamp (when it occurred) will always be stamped with "now" when the metric is recorded
* (i.e. there is no way to back date a metric recording).
*/
public InstrumenterBuilder<REQUEST, RESPONSE> setTimeExtractor(
TimeExtractor<REQUEST, RESPONSE> timeExtractor) {
@ -188,8 +195,8 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Returns a new {@link Instrumenter} which will create client spans and inject context into
* requests.
* Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#CLIENT client} spans
* and inject context into requests.
*/
public Instrumenter<REQUEST, RESPONSE> newClientInstrumenter(TextMapSetter<REQUEST> setter) {
return newInstrumenter(
@ -197,8 +204,8 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Returns a new {@link Instrumenter} which will create server spans and extract context from
* requests.
* Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#SERVER server} spans
* and extract context from requests.
*/
public Instrumenter<REQUEST, RESPONSE> newServerInstrumenter(TextMapGetter<REQUEST> getter) {
return newInstrumenter(
@ -206,8 +213,8 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Returns a new {@link Instrumenter} which will create producer spans and inject context into
* requests.
* Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#PRODUCER producer}
* spans and inject context into requests.
*/
public Instrumenter<REQUEST, RESPONSE> newProducerInstrumenter(TextMapSetter<REQUEST> setter) {
return newInstrumenter(
@ -216,8 +223,8 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Returns a new {@link Instrumenter} which will create consumer spans and extract context from
* requests.
* Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#CONSUMER consumer}
* spans and extract context from requests.
*/
public Instrumenter<REQUEST, RESPONSE> newConsumerInstrumenter(TextMapGetter<REQUEST> getter) {
return newInstrumenter(
@ -226,8 +233,8 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
}
/**
* Returns a new {@link Instrumenter} which will create internal spans and do no context
* propagation.
* Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#INTERNAL internal}
* spans and do no context propagation.
*/
public Instrumenter<REQUEST, RESPONSE> newInstrumenter() {
return newInstrumenter(InstrumenterConstructor.internal(), SpanKindExtractor.alwaysInternal());
@ -235,7 +242,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
/**
* Returns a new {@link Instrumenter} which will create spans with kind determined by the passed
* {@code spanKindExtractor} and do no context propagation.
* {@link SpanKindExtractor} and do no context propagation.
*/
public Instrumenter<REQUEST, RESPONSE> newInstrumenter(
SpanKindExtractor<? super REQUEST> spanKindExtractor) {

View File

@ -17,13 +17,13 @@ final class JdkErrorCauseExtractor implements ErrorCauseExtractor {
private static final Class<?> COMPLETION_EXCEPTION_CLASS = getCompletionExceptionClass();
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
if (error.getCause() != null
&& (error instanceof ExecutionException
|| isInstanceOfCompletionException(error)
|| error instanceof InvocationTargetException
|| error instanceof UndeclaredThrowableException)) {
return extractCause(error.getCause());
return extract(error.getCause());
}
return error;
}

View File

@ -9,10 +9,10 @@ import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
/**
* A listener of the start and end of a request. Instrumented libraries will call {@link
* #start(Context, Attributes, long)} as early as possible in the processing of a request and {@link
* #end(Context, Attributes, long)} as late as possible when finishing the request. These correspond
* to the start and end of a span when tracing.
* A listener of the start and end of a request. The {@link #onStart(Context, Attributes, long)}
* methods will be called as early as possible in the processing of a request; and the {@link
* #onEnd(Context, Attributes, long)} method will be called as late as possible when finishing the
* processing. These correspond to the start and end of a span when tracing.
*/
public interface RequestListener {
@ -23,14 +23,46 @@ public interface RequestListener {
*
* @param startNanos The nanosecond timestamp marking the start of the request. Can be used to
* compute the duration of the entire operation.
* @deprecated Implement the {@link #onStart(Context, Attributes, long)} method instead.
*/
Context start(Context context, Attributes startAttributes, long startNanos);
@Deprecated
default Context start(Context context, Attributes startAttributes, long startNanos) {
throw new UnsupportedOperationException(
"This method variant is deprecated and will be removed in the next minor release.");
}
/**
* Listener method that is called at the start of a request processing. If any state needs to be
* kept between the start and end of the request, e.g., an in-progress span, it should be added to
* the passed in {@link Context} and returned.
*
* @param startNanos The nanosecond timestamp marking the start of the request. Can be used to
* compute the duration of the entire operation.
*/
default Context onStart(Context context, Attributes startAttributes, long startNanos) {
return start(context, startAttributes, startNanos);
}
/**
* Listener method that is called at the end of a request.
*
* @param endNanos The nanosecond timestamp marking the end of the request. Can be used to compute
* the duration of the entire operation.
* @deprecated Implement the {@link #onEnd(Context, Attributes, long)} method instead.
*/
void end(Context context, Attributes endAttributes, long endNanos);
@Deprecated
default void end(Context context, Attributes endAttributes, long endNanos) {
throw new UnsupportedOperationException(
"This method variant is deprecated and will be removed in the next minor release.");
}
/**
* Listener method that is called at the end of a request processing.
*
* @param endNanos The nanosecond timestamp marking the end of the request. Can be used to compute
* the duration of the entire operation.
*/
default void onEnd(Context context, Attributes endAttributes, long endNanos) {
end(context, endAttributes, endNanos);
}
}

View File

@ -7,9 +7,12 @@ package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.metrics.Meter;
/** A factory for a {@link RequestListener} for recording metrics using a {@link Meter}. */
/** A factory for creating a {@link RequestListener} instance that records request metrics. */
@FunctionalInterface
public interface RequestMetrics {
/** Returns a {@link RequestListener} for recording metrics using the given {@link Meter}. */
/**
* Returns a {@link RequestListener} that records request metrics using the given {@link Meter}.
*/
RequestListener create(Meter meter);
}

View File

@ -8,13 +8,16 @@ package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.trace.SpanKind;
/**
* Extractor or {@link SpanKind}. In most cases, the span kind will be constant for server or client
* requests, but you may need to implement a custom {@link SpanKindExtractor} if a framework can
* generate different kinds of spans, for example both HTTP and messaging spans.
* Extractor of the {@link SpanKind}. In most cases, the span kind will be constant for server or
* client requests, but you may need to implement a custom {@link SpanKindExtractor} if a framework
* can generate different kinds of spans, for example both HTTP and messaging spans.
*/
@FunctionalInterface
public interface SpanKindExtractor<REQUEST> {
/** Returns the {@link SpanKind} corresponding to the {@link REQUEST}. */
SpanKind extract(REQUEST request);
/** Returns a {@link SpanNameExtractor} which always returns {@link SpanKind#INTERNAL}. */
static <REQUEST> SpanKindExtractor<REQUEST> alwaysInternal() {
return request -> SpanKind.INTERNAL;
@ -39,7 +42,4 @@ public interface SpanKindExtractor<REQUEST> {
static <REQUEST> SpanKindExtractor<REQUEST> alwaysConsumer() {
return request -> SpanKind.CONSUMER;
}
/** Returns the {@link SpanKind} corresponding to the {@link REQUEST}. */
SpanKind extract(REQUEST request);
}

View File

@ -5,12 +5,7 @@
package io.opentelemetry.instrumentation.api.instrumenter;
/**
* Extractor of the span name for a request. Where possible, an extractor based on semantic
* conventions returned from the factories in this class should be used. The most common reason to
* provide a custom implementation would be to apply the extractor for a particular semantic
* convention by first determining which convention applies to the request.
*/
/** Extractor of the span name for a request. */
@FunctionalInterface
public interface SpanNameExtractor<REQUEST> {

View File

@ -9,12 +9,15 @@ import io.opentelemetry.api.trace.StatusCode;
import javax.annotation.Nullable;
/**
* Extractor of {@link StatusCode}, which will be called after a request and response is completed
* to determine its final status.
* Extractor of {@link StatusCode}. The {@link #extract(Object, Object, Throwable)} method will be
* called after a request processing is completed to determine its final status.
*/
@FunctionalInterface
public interface SpanStatusExtractor<REQUEST, RESPONSE> {
/** Returns the {@link StatusCode}. */
StatusCode extract(REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
/**
* Returns the default {@link SpanStatusExtractor}, which returns {@link StatusCode#ERROR} if the
* framework returned an unhandled exception, or {@link StatusCode#UNSET} otherwise.
@ -23,7 +26,4 @@ public interface SpanStatusExtractor<REQUEST, RESPONSE> {
static <REQUEST, RESPONSE> SpanStatusExtractor<REQUEST, RESPONSE> getDefault() {
return (SpanStatusExtractor<REQUEST, RESPONSE>) DefaultSpanStatusExtractor.INSTANCE;
}
/** Returns the {@link StatusCode}. */
StatusCode extract(REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
}

View File

@ -11,16 +11,16 @@ import javax.annotation.Nullable;
/**
* Extractor of the start and end times of request processing.
*
* <p>Note: if metrics are generated by the Instrumenter, the start and end times from the {@code
* TimeExtractor} will be used to generate any duration metrics, but the internal metric timestamp
* (when it occurred) will always be stamped with "now" when the metric is recorded (i.e. there is
* no way to back date a metric recording).
* <p>Note: if metrics are generated by the {@link Instrumenter}, the start and end times from the
* {@link TimeExtractor} will be used to generate any duration metrics, but the internal metric
* timestamp (when it occurred) will always be stamped with "now" when the metric is recorded (i.e.
* there is no way to back date a metric recording).
*/
public interface TimeExtractor<REQUEST, RESPONSE> {
/** Returns the timestamp marking the start of the request processing. */
Instant extractStartTime(REQUEST request);
/** Returns the timestamp marking the end of the response processing. */
/** Returns the timestamp marking the end of the request processing. */
Instant extractEndTime(REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
}

View File

@ -13,7 +13,7 @@ public final class ClassNames {
private static final Cache<Class<?>, String> simpleNames = Cache.weak();
/**
* Returns a simple name based on a given class reference, e.g. for use in span names and span
* Returns a simple class name based on a given class reference, e.g. for use in span names and
* attributes. Anonymous classes are named based on their parent.
*/
public static String simpleName(Class<?> type) {

View File

@ -383,13 +383,13 @@ class InstrumenterTest {
RequestListener requestListener =
new RequestListener() {
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
startContext.set(true);
return context;
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
endContext.set(true);
}
};
@ -415,13 +415,13 @@ class InstrumenterTest {
RequestListener requestListener =
new RequestListener() {
@Override
public Context start(Context context, Attributes startAttributes, long startNanos) {
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
startContext.set(context);
return context;
}
@Override
public void end(Context context, Attributes endAttributes, long endNanos) {
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
endContext.set(context);
}
};

View File

@ -31,7 +31,7 @@ class JdkErrorCauseExtractorTest {
.getConstructor(Throwable.class)
.newInstance(new IllegalArgumentException("test"));
assertThat(ErrorCauseExtractor.jdk().extractCause(exception))
assertThat(ErrorCauseExtractor.jdk().extract(exception))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("test");
}
@ -40,7 +40,7 @@ class JdkErrorCauseExtractorTest {
void multipleUnwraps() {
assertThat(
ErrorCauseExtractor.jdk()
.extractCause(
.extract(
new ExecutionException(
new UndeclaredThrowableException(new IllegalArgumentException("test")))))
.isInstanceOf(IllegalArgumentException.class)
@ -49,13 +49,12 @@ class JdkErrorCauseExtractorTest {
@Test
void notWrapped() {
assertThat(ErrorCauseExtractor.jdk().extractCause(new IllegalArgumentException("test")))
assertThat(ErrorCauseExtractor.jdk().extract(new IllegalArgumentException("test")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("test");
assertThat(
ErrorCauseExtractor.jdk()
.extractCause(
new IllegalArgumentException("test", new IllegalStateException("state"))))
.extract(new IllegalArgumentException("test", new IllegalStateException("state"))))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("test");
}

View File

@ -11,10 +11,10 @@ import javax.faces.FacesException;
public class JsfErrorCauseExtractor implements ErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
while (error.getCause() != null && error instanceof FacesException) {
error = error.getCause();
}
return ErrorCauseExtractor.jdk().extractCause(error);
return ErrorCauseExtractor.jdk().extract(error);
}
}

View File

@ -11,8 +11,8 @@ import javax.el.ELException;
public class MyFacesErrorCauseExtractor extends JsfErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
error = super.extractCause(error);
public Throwable extract(Throwable error) {
error = super.extract(error);
while (error.getCause() != null && error instanceof ELException) {
error = error.getCause();
}

View File

@ -10,7 +10,7 @@ import org.quartz.SchedulerException;
final class QuartzErrorCauseExtractor implements ErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
Throwable userError = error;
while (userError instanceof SchedulerException) {
userError = ((SchedulerException) userError).getUnderlyingException();

View File

@ -50,7 +50,7 @@ public final class RatpackSingletons {
public static void onError(io.opentelemetry.context.Context context, Throwable error) {
Span span = Span.fromContext(context);
span.setStatus(StatusCode.ERROR);
span.recordException(ErrorCauseExtractor.jdk().extractCause(error));
span.recordException(ErrorCauseExtractor.jdk().extract(error));
}
private RatpackSingletons() {}

View File

@ -15,10 +15,10 @@ public class ServletErrorCauseExtractor<REQUEST, RESPONSE> implements ErrorCause
}
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
if (accessor.isServletException(error) && error.getCause() != null) {
error = error.getCause();
}
return ErrorCauseExtractor.jdk().extractCause(error);
return ErrorCauseExtractor.jdk().extract(error);
}
}

View File

@ -12,10 +12,10 @@ enum SpringKafkaErrorCauseExtractor implements ErrorCauseExtractor {
INSTANCE;
@Override
public Throwable extractCause(Throwable error) {
public Throwable extract(Throwable error) {
if (error instanceof ListenerExecutionFailedException && error.getCause() != null) {
error = error.getCause();
}
return ErrorCauseExtractor.jdk().extractCause(error);
return ErrorCauseExtractor.jdk().extract(error);
}
}

View File

@ -25,7 +25,7 @@ public class TapestrySingletons {
if (error instanceof ComponentEventException) {
error = error.getCause();
}
return ErrorCauseExtractor.jdk().extractCause(error);
return ErrorCauseExtractor.jdk().extract(error);
})
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
.newInstrumenter();