Skip to content

Commit

Permalink
Instrumenter instrumentation version and schema url (open-telemetry#5752
Browse files Browse the repository at this point in the history
)

* Instrumenter instrumentation version and schema url

* nullable instrumentation version

* Apply suggestions from code review

Co-authored-by: Fabrizio Ferri-Benedetti <[email protected]>

* reformat

* code review comments

* instrumentation properties files

* Apply suggestions from code review

Co-authored-by: Fabrizio Ferri-Benedetti <[email protected]>

* code review comments

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <[email protected]>

Co-authored-by: Fabrizio Ferri-Benedetti <[email protected]>
Co-authored-by: Trask Stalnaker <[email protected]>
  • Loading branch information
3 people authored and RashmiRam committed May 23, 2022
1 parent 228df7f commit ffe43c8
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 25 deletions.
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

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,12 +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.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 @@ -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();
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -127,7 +153,7 @@ public InstrumenterBuilder<REQUEST, RESPONSE> addRequestListener(RequestListener

/** 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;
}

Expand Down Expand Up @@ -274,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() {
// 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);
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

0 comments on commit ffe43c8

Please sign in to comment.