Instrumentation API and Javadoc cleanup (#5954)
This commit is contained in:
parent
45d0518d69
commit
802f5aa3ea
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue