Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrumenter instrumentation version and schema url #5752

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/contributing/using-instrumenter-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC @theletterf - I've added a new section to the Instrumenter API doc, please take a look at this when you have a moment 🙏


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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
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;
Expand Down Expand Up @@ -59,11 +58,7 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
OpenTelemetry openTelemetry,
String instrumentationName,
SpanNameExtractor<? super REQUEST> spanNameExtractor) {
return new InstrumenterBuilder<>(
openTelemetry,
instrumentationName,
EmbeddedInstrumentationProperties.findVersion(instrumentationName),
spanNameExtractor);
return new InstrumenterBuilder<>(openTelemetry, instrumentationName, spanNameExtractor);
}

/**
Expand All @@ -82,15 +77,19 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
* 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();
Expand All @@ -112,19 +111,18 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder

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();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@

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.annotations.UnstableApi;
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;
Expand All @@ -39,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();
Expand All @@ -62,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;
}

/**
Expand Down Expand Up @@ -127,9 +152,8 @@ public InstrumenterBuilder<REQUEST, RESPONSE> addRequestListener(RequestListener
}

/** Adds a {@link RequestMetrics} whose metrics will be recorded for request start and end. */
@UnstableApi
public InstrumenterBuilder<REQUEST, RESPONSE> addRequestMetrics(RequestMetrics factory) {
requestListeners.add(factory.create(meter));
requestMetrics.add(factory);
return this;
}

Expand Down Expand Up @@ -276,7 +300,44 @@ private Instrumenter<REQUEST, RESPONSE> newInstrumenter(
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() {
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
// 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);
}
Comment on lines +326 to +331
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,11 @@ void instrumentationVersion_default() {

@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();
Expand All @@ -770,6 +771,30 @@ void instrumentationVersion_custom() {
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);

Expand Down