Instrumenter instrumentation version and schema url (#5752)
* Instrumenter instrumentation version and schema url * nullable instrumentation version * Apply suggestions from code review Co-authored-by: Fabrizio Ferri-Benedetti <fferribenedetti@splunk.com> * reformat * code review comments * instrumentation properties files * Apply suggestions from code review Co-authored-by: Fabrizio Ferri-Benedetti <fferribenedetti@splunk.com> * code review comments * Apply suggestions from code review Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> Co-authored-by: Fabrizio Ferri-Benedetti <fferribenedetti@splunk.com> Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
ad2243fcf4
commit
2ce1162eac
|
@ -116,6 +116,31 @@ The `builder()` method accepts three arguments:
|
|||
An `Instrumenter` can be built from several smaller components. The following subsections describe
|
||||
all interfaces that can be used to customize an `Instrumenter`.
|
||||
|
||||
### Set the instrumentation version and OpenTelemetry schema URL
|
||||
|
||||
By setting the instrumentation library version, you let users identify which version of your
|
||||
instrumentation produced the telemetry. Make sure you always provide the version to
|
||||
the `Instrumenter`. You can do this in two ways:
|
||||
|
||||
* By calling the `setInstrumentationVersion()` method on the `InstrumenterBuilder`.
|
||||
* By making sure that the JAR file with your instrumentation library contains a properties file in
|
||||
the `META-INF/io/opentelemetry/instrumentation/` directory. You must name the file
|
||||
`${instrumentationName}.properties`, where `${instrumentationName}` is the name of the
|
||||
instrumentation library passed to the `Instrumenter#builder()` method. The file must contain a
|
||||
single property, `version`. For example:
|
||||
|
||||
```properties
|
||||
# META-INF/io/opentelemetry/instrumentation/my-instrumentation.properties
|
||||
version=1.2.3
|
||||
```
|
||||
|
||||
The `Instrumenter` automatically detects the properties file and determines the instrumentation
|
||||
version based on its name.
|
||||
|
||||
If the `Instrumenter` adheres to a specific OpenTelemetry schema, you can set the schema URL using
|
||||
the `setSchemaUrl()` method on the `InstrumenterBuilder`. To learn more about the OpenTelemetry
|
||||
schemas [see the Overview](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/schemas/overview.md).
|
||||
|
||||
### Name the spans using the `SpanNameExtractor`
|
||||
|
||||
A `SpanNameExtractor` is a simple functional interface that accepts the `REQUEST` type and returns
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.opentelemetry.api.trace.SpanKind;
|
|||
import io.opentelemetry.api.trace.StatusCode;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
|
||||
import io.opentelemetry.instrumentation.api.internal.SupportabilityMetrics;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
|
@ -59,11 +58,7 @@ public class Instrumenter<REQUEST, RESPONSE> {
|
|||
OpenTelemetry openTelemetry,
|
||||
String instrumentationName,
|
||||
SpanNameExtractor<? super REQUEST> spanNameExtractor) {
|
||||
return new InstrumenterBuilder<>(
|
||||
openTelemetry,
|
||||
instrumentationName,
|
||||
EmbeddedInstrumentationProperties.findVersion(instrumentationName),
|
||||
spanNameExtractor);
|
||||
return new InstrumenterBuilder<>(openTelemetry, instrumentationName, spanNameExtractor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,15 +77,19 @@ public class Instrumenter<REQUEST, RESPONSE> {
|
|||
* 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.
|
||||
*
|
||||
* @deprecated Use the {@link InstrumenterBuilder#setInstrumentationVersion(String)} method
|
||||
* instead.
|
||||
*/
|
||||
// TODO: add a setInstrumentationVersion method to the builder instead
|
||||
@Deprecated
|
||||
public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder(
|
||||
OpenTelemetry openTelemetry,
|
||||
String instrumentationName,
|
||||
String instrumentationVersion,
|
||||
SpanNameExtractor<? super REQUEST> spanNameExtractor) {
|
||||
return new InstrumenterBuilder<>(
|
||||
openTelemetry, instrumentationName, instrumentationVersion, spanNameExtractor);
|
||||
return Instrumenter.<REQUEST, RESPONSE>builder(
|
||||
openTelemetry, instrumentationName, spanNameExtractor)
|
||||
.setInstrumentationVersion(instrumentationVersion);
|
||||
}
|
||||
|
||||
private static final SupportabilityMetrics supportability = SupportabilityMetrics.instance();
|
||||
|
@ -112,19 +111,18 @@ public class Instrumenter<REQUEST, RESPONSE> {
|
|||
|
||||
Instrumenter(InstrumenterBuilder<REQUEST, RESPONSE> builder) {
|
||||
this.instrumentationName = builder.instrumentationName;
|
||||
this.tracer =
|
||||
builder.openTelemetry.getTracer(instrumentationName, builder.instrumentationVersion);
|
||||
this.tracer = builder.buildTracer();
|
||||
this.spanNameExtractor = builder.spanNameExtractor;
|
||||
this.spanKindExtractor = builder.spanKindExtractor;
|
||||
this.spanStatusExtractor = builder.spanStatusExtractor;
|
||||
this.spanLinksExtractors = new ArrayList<>(builder.spanLinksExtractors);
|
||||
this.attributesExtractors = new ArrayList<>(builder.attributesExtractors);
|
||||
this.contextCustomizers = new ArrayList<>(builder.contextCustomizers);
|
||||
this.requestListeners = new ArrayList<>(builder.requestListeners);
|
||||
this.requestListeners = builder.buildRequestListeners();
|
||||
this.errorCauseExtractor = builder.errorCauseExtractor;
|
||||
this.timeExtractor = builder.timeExtractor;
|
||||
this.enabled = builder.enabled;
|
||||
this.spanSuppressionStrategy = builder.getSpanSuppressionStrategy();
|
||||
this.spanSuppressionStrategy = builder.buildSpanSuppressionStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,12 +9,16 @@ 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;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.TextMapGetter;
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
|
||||
import io.opentelemetry.instrumentation.api.internal.SpanKey;
|
||||
import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
|
||||
import java.util.ArrayList;
|
||||
|
@ -38,17 +42,18 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
|
|||
.getBoolean("otel.instrumentation.experimental.outgoing-span-suppression-by-type", false);
|
||||
|
||||
final OpenTelemetry openTelemetry;
|
||||
final Meter meter;
|
||||
final String instrumentationName;
|
||||
final String instrumentationVersion;
|
||||
final SpanNameExtractor<? super REQUEST> spanNameExtractor;
|
||||
|
||||
final List<SpanLinksExtractor<? super REQUEST>> spanLinksExtractors = new ArrayList<>();
|
||||
final List<AttributesExtractor<? super REQUEST, ? super RESPONSE>> attributesExtractors =
|
||||
new ArrayList<>();
|
||||
final List<ContextCustomizer<? super REQUEST>> contextCustomizers = new ArrayList<>();
|
||||
final List<RequestListener> requestListeners = new ArrayList<>();
|
||||
private final List<RequestListener> requestListeners = new ArrayList<>();
|
||||
private final List<RequestMetrics> requestMetrics = new ArrayList<>();
|
||||
|
||||
@Nullable private String instrumentationVersion;
|
||||
@Nullable private String schemaUrl = null;
|
||||
SpanKindExtractor<? super REQUEST> spanKindExtractor = SpanKindExtractor.alwaysInternal();
|
||||
SpanStatusExtractor<? super REQUEST, ? super RESPONSE> spanStatusExtractor =
|
||||
SpanStatusExtractor.getDefault();
|
||||
|
@ -61,13 +66,34 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
|
|||
InstrumenterBuilder(
|
||||
OpenTelemetry openTelemetry,
|
||||
String instrumentationName,
|
||||
String instrumentationVersion,
|
||||
SpanNameExtractor<? super REQUEST> spanNameExtractor) {
|
||||
this.openTelemetry = openTelemetry;
|
||||
this.meter = openTelemetry.getMeterProvider().get(instrumentationName);
|
||||
this.instrumentationName = instrumentationName;
|
||||
this.instrumentationVersion = instrumentationVersion;
|
||||
this.spanNameExtractor = spanNameExtractor;
|
||||
this.instrumentationVersion =
|
||||
EmbeddedInstrumentationProperties.findVersion(instrumentationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instrumentation version that'll 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.
|
||||
*/
|
||||
public InstrumenterBuilder<REQUEST, RESPONSE> setInstrumentationVersion(
|
||||
String instrumentationVersion) {
|
||||
this.instrumentationVersion = instrumentationVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OpenTelemetry schema URL that'll be associated with all telemetry produced by this
|
||||
* {@link Instrumenter}.
|
||||
*/
|
||||
public InstrumenterBuilder<REQUEST, RESPONSE> setSchemaUrl(String schemaUrl) {
|
||||
this.schemaUrl = schemaUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +153,7 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
|
|||
|
||||
/** Adds a {@link RequestMetrics} whose metrics will be recorded for request start and end. */
|
||||
public InstrumenterBuilder<REQUEST, RESPONSE> addRequestMetrics(RequestMetrics factory) {
|
||||
requestListeners.add(factory.create(meter));
|
||||
requestMetrics.add(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -274,7 +300,44 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
|
|||
return constructor.create(this);
|
||||
}
|
||||
|
||||
SpanSuppressionStrategy getSpanSuppressionStrategy() {
|
||||
Tracer buildTracer() {
|
||||
TracerBuilder tracerBuilder =
|
||||
openTelemetry.getTracerProvider().tracerBuilder(instrumentationName);
|
||||
if (instrumentationVersion != null) {
|
||||
tracerBuilder.setInstrumentationVersion(instrumentationVersion);
|
||||
}
|
||||
if (schemaUrl != null) {
|
||||
tracerBuilder.setSchemaUrl(schemaUrl);
|
||||
}
|
||||
return tracerBuilder.build();
|
||||
}
|
||||
|
||||
List<RequestListener> buildRequestListeners() {
|
||||
// just copy the listeners list if there are no metrics registered
|
||||
if (requestMetrics.isEmpty()) {
|
||||
return new ArrayList<>(requestListeners);
|
||||
}
|
||||
|
||||
List<RequestListener> listeners =
|
||||
new ArrayList<>(requestListeners.size() + requestMetrics.size());
|
||||
listeners.addAll(requestListeners);
|
||||
|
||||
MeterBuilder meterBuilder = openTelemetry.getMeterProvider().meterBuilder(instrumentationName);
|
||||
if (instrumentationVersion != null) {
|
||||
meterBuilder.setInstrumentationVersion(instrumentationVersion);
|
||||
}
|
||||
if (schemaUrl != null) {
|
||||
meterBuilder.setSchemaUrl(schemaUrl);
|
||||
}
|
||||
Meter meter = meterBuilder.build();
|
||||
for (RequestMetrics factory : requestMetrics) {
|
||||
listeners.add(factory.create(meter));
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
SpanSuppressionStrategy buildSpanSuppressionStrategy() {
|
||||
Set<SpanKey> spanKeys = getSpanKeysFromAttributesExtractors();
|
||||
if (enableSpanSuppressionByType) {
|
||||
return SpanSuppressionStrategy.from(spanKeys);
|
||||
|
|
|
@ -749,10 +749,11 @@ class InstrumenterTest {
|
|||
|
||||
@Test
|
||||
void instrumentationVersion_custom() {
|
||||
InstrumenterBuilder<Map<String, String>, Map<String, String>> builder =
|
||||
Instrumenter.builder(otelTesting.getOpenTelemetry(), "test", "1.0", name -> "span");
|
||||
|
||||
Instrumenter<Map<String, String>, Map<String, String>> instrumenter = builder.newInstrumenter();
|
||||
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
|
||||
Instrumenter.<Map<String, String>, Map<String, String>>builder(
|
||||
otelTesting.getOpenTelemetry(), "test", name -> "span")
|
||||
.setInstrumentationVersion("1.0")
|
||||
.newInstrumenter();
|
||||
|
||||
Context context = instrumenter.start(Context.root(), Collections.emptyMap());
|
||||
assertThat(Span.fromContext(context)).isNotNull();
|
||||
|
@ -770,6 +771,30 @@ class InstrumenterTest {
|
|||
InstrumentationLibraryInfo.create("test", "1.0"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaUrl() {
|
||||
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
|
||||
Instrumenter.<Map<String, String>, Map<String, String>>builder(
|
||||
otelTesting.getOpenTelemetry(), "test", name -> "span")
|
||||
.setSchemaUrl("https://opentelemetry.io/schemas/1.0.0")
|
||||
.newInstrumenter();
|
||||
|
||||
Context context = instrumenter.start(Context.root(), Collections.emptyMap());
|
||||
assertThat(Span.fromContext(context)).isNotNull();
|
||||
|
||||
instrumenter.end(context, Collections.emptyMap(), Collections.emptyMap(), null);
|
||||
|
||||
InstrumentationLibraryInfo expectedLibraryInfo =
|
||||
InstrumentationLibraryInfo.create("test", null, "https://opentelemetry.io/schemas/1.0.0");
|
||||
otelTesting
|
||||
.assertTraces()
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("span").hasInstrumentationLibraryInfo(expectedLibraryInfo)));
|
||||
}
|
||||
|
||||
private static void validateInstrumentationTypeSpanPresent(SpanKey spanKey, Context context) {
|
||||
Span span = Span.fromContext(context);
|
||||
|
||||
|
|
Loading…
Reference in New Issue