Introduce usage of the OpenTelemetry schema with a Tracer/MeterBuilder (#3309)

* Add the OpenTelemetry schema URL to the InstrumentationLibraryInfo and the corresponding API calls.

* small refactoring and doc tweaks from feedback

* make the instrumentation version nullable on the method that takes a schema

* update the apidiffs

* add since tags and a few more missing nullable annotations

* Switch to using a Builder rather than method overloads.
This commit is contained in:
John Watson 2021-06-11 20:50:23 -07:00 committed by GitHub
parent b212fce5a1
commit ca92a9ab03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 527 additions and 49 deletions

View File

@ -7,6 +7,7 @@ package io.opentelemetry.api;
import io.opentelemetry.api.internal.GuardedBy; import io.opentelemetry.api.internal.GuardedBy;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.ContextPropagators;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -126,6 +127,20 @@ public final class GlobalOpenTelemetry {
return get().getTracer(instrumentationName, instrumentationVersion); return get().getTracer(instrumentationName, instrumentationVersion);
} }
/**
* Creates a TracerBuilder for a named {@link Tracer} instance.
*
* <p>This is a shortcut method for {@code get().tracerBuilder(instrumentationName)}
*
* @param instrumentationName The name of the instrumentation library, not the name of the
* instrument*ed* library.
* @return a TracerBuilder instance.
* @since 1.4.0
*/
public static TracerBuilder tracerBuilder(String instrumentationName) {
return get().tracerBuilder(instrumentationName);
}
/** /**
* Unsets the global {@link OpenTelemetry}. This is only meant to be used from tests which need to * Unsets the global {@link OpenTelemetry}. This is only meant to be used from tests which need to
* reconfigure {@link OpenTelemetry}. * reconfigure {@link OpenTelemetry}.

View File

@ -6,6 +6,7 @@
package io.opentelemetry.api; package io.opentelemetry.api;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.ContextPropagators;
@ -63,6 +64,18 @@ public interface OpenTelemetry {
return getTracerProvider().get(instrumentationName, instrumentationVersion); return getTracerProvider().get(instrumentationName, instrumentationVersion);
} }
/**
* Creates a {@link TracerBuilder} for a named {@link Tracer} instance.
*
* @param instrumentationName The name of the instrumentation library, not the name of the
* instrument*ed* library.
* @return a TracerBuilder instance.
* @since 1.4.0
*/
default TracerBuilder tracerBuilder(String instrumentationName) {
return getTracerProvider().tracerBuilder(instrumentationName);
}
/** Returns the {@link ContextPropagators} for this {@link OpenTelemetry}. */ /** Returns the {@link ContextPropagators} for this {@link OpenTelemetry}. */
ContextPropagators getPropagators(); ContextPropagators getPropagators();
} }

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.trace;
class DefaultTracerBuilder implements TracerBuilder {
private static final DefaultTracerBuilder INSTANCE = new DefaultTracerBuilder();
static TracerBuilder getInstance() {
return INSTANCE;
}
@Override
public TracerBuilder setSchemaUrl(String schemaUrl) {
return this;
}
@Override
public TracerBuilder setInstrumentationVersion(String instrumentationVersion) {
return this;
}
@Override
public Tracer build() {
return DefaultTracer.getInstance();
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.trace;
/**
* Builder class for creating {@link Tracer} instances.
*
* @since 1.4.0
*/
public interface TracerBuilder {
/**
* Assign an OpenTelemetry schema URL to the resulting Tracer.
*
* @param schemaUrl The URL of the OpenTelemetry schema being used by this instrumentation
* library.
* @return this
*/
TracerBuilder setSchemaUrl(String schemaUrl);
/**
* Assign a version to the instrumentation library that is using the resulting Tracer.
*
* @param instrumentationVersion The version of the instrumentation library.
* @return this
*/
TracerBuilder setInstrumentationVersion(String instrumentationVersion);
/**
* Gets or creates a {@link Tracer} instance.
*
* @return a {@link Tracer} instance configured with the provided options.
*/
Tracer build();
}

View File

@ -46,4 +46,16 @@ public interface TracerProvider {
* @return a tracer instance. * @return a tracer instance.
*/ */
Tracer get(String instrumentationName, String instrumentationVersion); Tracer get(String instrumentationName, String instrumentationVersion);
/**
* Creates a TracerBuilder for a named {@link Tracer} instance.
*
* @param instrumentationName The name of the instrumentation library, not the name of the
* instrument*ed* library.
* @return a TracerBuilder instance.
* @since 1.4.0
*/
default TracerBuilder tracerBuilder(String instrumentationName) {
return DefaultTracerBuilder.getInstance();
}
} }

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.metrics;
class DefaultMeterBuilder implements MeterBuilder {
private static final MeterBuilder INSTANCE = new DefaultMeterBuilder();
static MeterBuilder getInstance() {
return INSTANCE;
}
@Override
public MeterBuilder setSchemaUrl(String schemaUrl) {
return this;
}
@Override
public MeterBuilder setInstrumentationVersion(String instrumentationVersion) {
return this;
}
@Override
public Meter build() {
return DefaultMeter.getInstance();
}
}

View File

@ -68,4 +68,18 @@ public final class GlobalMeterProvider {
public static Meter getMeter(String instrumentationName, String instrumentationVersion) { public static Meter getMeter(String instrumentationName, String instrumentationVersion) {
return get().get(instrumentationName, instrumentationVersion); return get().get(instrumentationName, instrumentationVersion);
} }
/**
* Creates a {@link MeterBuilder} for a named meter instance.
*
* <p>This is a shortcut method for {@code get().meterBuilder(instrumentationName)}
*
* @param instrumentationName The name of the instrumentation library, not the name of the
* instrument*ed* library.
* @return a MeterBuilder instance.
* @since 1.4.0
*/
public static MeterBuilder meterBuilder(String instrumentationName) {
return get().meterBuilder(instrumentationName);
}
} }

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.metrics;
/**
* Builder class for creating {@link Meter} instances.
*
* @since 1.4.0
*/
public interface MeterBuilder {
/**
* Assign an OpenTelemetry schema URL to the resulting Meter.
*
* @param schemaUrl The URL of the OpenTelemetry schema being used by this instrumentation
* library.
* @return this
*/
MeterBuilder setSchemaUrl(String schemaUrl);
/**
* Assign a version to the instrumentation library that is using the resulting Meter.
*
* @param instrumentationVersion The version of the instrumentation library.
* @return this
*/
MeterBuilder setInstrumentationVersion(String instrumentationVersion);
/**
* Gets or creates a {@link Meter} instance.
*
* @return a {@link Meter} instance configured with the provided options.
*/
Meter build();
}

View File

@ -42,4 +42,16 @@ public interface MeterProvider {
* @return a tracer instance. * @return a tracer instance.
*/ */
Meter get(String instrumentationName, String instrumentationVersion); Meter get(String instrumentationName, String instrumentationVersion);
/**
* Creates a MeterBuilder for a named meter instance.
*
* @param instrumentationName The name of the instrumentation library, not the name of the
* instrument*ed* library.
* @return a MeterBuilder instance.
* @since 1.4.0
*/
default MeterBuilder meterBuilder(String instrumentationName) {
return DefaultMeterBuilder.getInstance();
}
} }

View File

@ -15,5 +15,12 @@ class DefaultMeterTest {
assertThat(MeterProvider.noop().get("test")).isInstanceOf(DefaultMeter.class); assertThat(MeterProvider.noop().get("test")).isInstanceOf(DefaultMeter.class);
assertThat(MeterProvider.noop().get("test")).isSameAs(DefaultMeter.getInstance()); assertThat(MeterProvider.noop().get("test")).isSameAs(DefaultMeter.getInstance());
assertThat(MeterProvider.noop().get("test", "0.1.0")).isSameAs(DefaultMeter.getInstance()); assertThat(MeterProvider.noop().get("test", "0.1.0")).isSameAs(DefaultMeter.getInstance());
assertThat(
MeterProvider.noop()
.meterBuilder("test")
.setInstrumentationVersion("0.1.0")
.setSchemaUrl("http://url")
.build())
.isSameAs(DefaultMeter.getInstance());
} }
} }

View File

@ -1,2 +1,16 @@
Comparing source compatibility of against Comparing source compatibility of against
No changes. *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.api.GlobalOpenTelemetry (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.api.trace.TracerBuilder tracerBuilder(java.lang.String)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.OpenTelemetry (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++! NEW METHOD: PUBLIC(+) io.opentelemetry.api.trace.TracerBuilder tracerBuilder(java.lang.String)
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.trace.TracerBuilder (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.trace.Tracer build()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.trace.TracerBuilder setInstrumentationVersion(java.lang.String)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.trace.TracerBuilder setSchemaUrl(java.lang.String)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.trace.TracerProvider (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++! NEW METHOD: PUBLIC(+) io.opentelemetry.api.trace.TracerBuilder tracerBuilder(java.lang.String)

View File

@ -1,2 +1,6 @@
Comparing source compatibility of against Comparing source compatibility of against
No changes. **** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.common.InstrumentationLibraryInfo (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.InstrumentationLibraryInfo create(java.lang.String, java.lang.String, java.lang.String)
+++* NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String getSchemaUrl()
+++ NEW ANNOTATION: javax.annotation.Nullable

View File

@ -1,2 +1,4 @@
Comparing source compatibility of against Comparing source compatibility of against
No changes. *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.SdkTracerProvider (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.trace.TracerBuilder tracerBuilder(java.lang.String)

View File

@ -58,12 +58,7 @@ public final class MetricAdapter {
new ArrayList<>(entryResource.getValue().size()); new ArrayList<>(entryResource.getValue().size());
for (Map.Entry<InstrumentationLibraryInfo, List<Metric>> entryLibrary : for (Map.Entry<InstrumentationLibraryInfo, List<Metric>> entryLibrary :
entryResource.getValue().entrySet()) { entryResource.getValue().entrySet()) {
instrumentationLibraryMetrics.add( instrumentationLibraryMetrics.add(buildInstrumentationLibraryMetrics(entryLibrary));
InstrumentationLibraryMetrics.newBuilder()
.setInstrumentationLibrary(
CommonAdapter.toProtoInstrumentationLibrary(entryLibrary.getKey()))
.addAllMetrics(entryLibrary.getValue())
.build());
} }
resourceMetrics.add( resourceMetrics.add(
ResourceMetrics.newBuilder() ResourceMetrics.newBuilder()
@ -74,6 +69,19 @@ public final class MetricAdapter {
return resourceMetrics; return resourceMetrics;
} }
private static InstrumentationLibraryMetrics buildInstrumentationLibraryMetrics(
Map.Entry<InstrumentationLibraryInfo, List<Metric>> entryLibrary) {
InstrumentationLibraryMetrics.Builder metricsBuilder =
InstrumentationLibraryMetrics.newBuilder()
.setInstrumentationLibrary(
CommonAdapter.toProtoInstrumentationLibrary(entryLibrary.getKey()))
.addAllMetrics(entryLibrary.getValue());
if (entryLibrary.getKey().getSchemaUrl() != null) {
metricsBuilder.setSchemaUrl(entryLibrary.getKey().getSchemaUrl());
}
return metricsBuilder.build();
}
private static Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>> private static Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>>
groupByResourceAndLibrary(Collection<MetricData> metricDataList) { groupByResourceAndLibrary(Collection<MetricData> metricDataList) {
Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>> result = new HashMap<>(); Map<Resource, Map<InstrumentationLibraryInfo, List<Metric>>> result = new HashMap<>();

View File

@ -75,18 +75,27 @@ public final class SpanAdapter {
ResourceSpans.Builder resourceSpansBuilder = ResourceSpans.Builder resourceSpansBuilder =
ResourceSpans.newBuilder().setResource(ResourceAdapter.toProtoResource(resource)); ResourceSpans.newBuilder().setResource(ResourceAdapter.toProtoResource(resource));
librarySpans.forEach( librarySpans.forEach(
(library, spans) -> (library, spans) -> {
resourceSpansBuilder.addInstrumentationLibrarySpans( resourceSpansBuilder.addInstrumentationLibrarySpans(
InstrumentationLibrarySpans.newBuilder() buildInstrumentationLibrarySpan(library, spans));
.setInstrumentationLibrary( });
CommonAdapter.toProtoInstrumentationLibrary(library))
.addAllSpans(spans)
.build()));
resourceSpans.add(resourceSpansBuilder.build()); resourceSpans.add(resourceSpansBuilder.build());
}); });
return resourceSpans; return resourceSpans;
} }
private static InstrumentationLibrarySpans buildInstrumentationLibrarySpan(
InstrumentationLibraryInfo library, List<Span> spans) {
InstrumentationLibrarySpans.Builder spansBuilder =
InstrumentationLibrarySpans.newBuilder()
.setInstrumentationLibrary(CommonAdapter.toProtoInstrumentationLibrary(library))
.addAllSpans(spans);
if (library.getSchemaUrl() != null) {
spansBuilder.setSchemaUrl(library.getSchemaUrl());
}
return spansBuilder.build();
}
private static Map<Resource, Map<InstrumentationLibraryInfo, List<Span>>> private static Map<Resource, Map<InstrumentationLibraryInfo, List<Span>>>
groupByResourceAndLibrary(Collection<SpanData> spanDataList) { groupByResourceAndLibrary(Collection<SpanData> spanDataList) {
Map<Resource, Map<InstrumentationLibraryInfo, List<Span>>> result = new HashMap<>(); Map<Resource, Map<InstrumentationLibraryInfo, List<Span>>> result = new HashMap<>();

View File

@ -568,7 +568,7 @@ class MetricAdapterTest {
io.opentelemetry.proto.resource.v1.Resource emptyResourceProto = io.opentelemetry.proto.resource.v1.Resource emptyResourceProto =
io.opentelemetry.proto.resource.v1.Resource.newBuilder().build(); io.opentelemetry.proto.resource.v1.Resource.newBuilder().build();
InstrumentationLibraryInfo instrumentationLibraryInfo = InstrumentationLibraryInfo instrumentationLibraryInfo =
InstrumentationLibraryInfo.create("name", "version"); InstrumentationLibraryInfo.create("name", "version", "http://url");
InstrumentationLibrary instrumentationLibraryProto = InstrumentationLibrary instrumentationLibraryProto =
InstrumentationLibrary.newBuilder().setName("name").setVersion("version").build(); InstrumentationLibrary.newBuilder().setName("name").setVersion("version").build();
InstrumentationLibrary emptyInstrumentationLibraryProto = InstrumentationLibrary emptyInstrumentationLibraryProto =
@ -652,6 +652,7 @@ class MetricAdapterTest {
InstrumentationLibraryMetrics.newBuilder() InstrumentationLibraryMetrics.newBuilder()
.setInstrumentationLibrary(instrumentationLibraryProto) .setInstrumentationLibrary(instrumentationLibraryProto)
.addAllMetrics(ImmutableList.of(metricDoubleSum, metricDoubleSum)) .addAllMetrics(ImmutableList.of(metricDoubleSum, metricDoubleSum))
.setSchemaUrl("http://url")
.build())) .build()))
.build(), .build(),
ResourceMetrics.newBuilder() ResourceMetrics.newBuilder()
@ -659,11 +660,12 @@ class MetricAdapterTest {
.addAllInstrumentationLibraryMetrics( .addAllInstrumentationLibraryMetrics(
ImmutableList.of( ImmutableList.of(
InstrumentationLibraryMetrics.newBuilder() InstrumentationLibraryMetrics.newBuilder()
.setInstrumentationLibrary(emptyInstrumentationLibraryProto) .setInstrumentationLibrary(instrumentationLibraryProto)
.addAllMetrics(singletonList(metricDoubleSum)) .addAllMetrics(singletonList(metricDoubleSum))
.setSchemaUrl("http://url")
.build(), .build(),
InstrumentationLibraryMetrics.newBuilder() InstrumentationLibraryMetrics.newBuilder()
.setInstrumentationLibrary(instrumentationLibraryProto) .setInstrumentationLibrary(emptyInstrumentationLibraryProto)
.addAllMetrics(singletonList(metricDoubleSum)) .addAllMetrics(singletonList(metricDoubleSum))
.build())) .build()))
.build()); .build());

View File

@ -29,14 +29,19 @@ import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.api.trace.TraceId;
import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.AnyValue;
import io.opentelemetry.proto.common.v1.InstrumentationLibrary;
import io.opentelemetry.proto.common.v1.KeyValue; import io.opentelemetry.proto.common.v1.KeyValue;
import io.opentelemetry.proto.trace.v1.InstrumentationLibrarySpans;
import io.opentelemetry.proto.trace.v1.ResourceSpans;
import io.opentelemetry.proto.trace.v1.Span; import io.opentelemetry.proto.trace.v1.Span;
import io.opentelemetry.proto.trace.v1.Status; import io.opentelemetry.proto.trace.v1.Status;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.testing.trace.TestSpanData; import io.opentelemetry.sdk.testing.trace.TestSpanData;
import io.opentelemetry.sdk.trace.data.EventData; import io.opentelemetry.sdk.trace.data.EventData;
import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -52,6 +57,35 @@ class SpanAdapterTest {
private static final SpanAdapter.ThreadLocalCache threadLocalCache = private static final SpanAdapter.ThreadLocalCache threadLocalCache =
new SpanAdapter.ThreadLocalCache(); new SpanAdapter.ThreadLocalCache();
@Test
void toProtoResourceSpans() {
List<ResourceSpans> resourceSpans =
SpanAdapter.toProtoResourceSpans(
Collections.singleton(
TestSpanData.builder()
.setHasEnded(true)
.setSpanContext(SPAN_CONTEXT)
.setParentSpanContext(SpanContext.getInvalid())
.setName("GET /api/endpoint")
.setKind(SpanKind.SERVER)
.setStartEpochNanos(12345)
.setEndEpochNanos(12349)
.setStatus(StatusData.unset())
.setInstrumentationLibraryInfo(
InstrumentationLibraryInfo.create("testLib", "1.0", "http://url"))
.build()));
assertThat(resourceSpans).hasSize(1);
ResourceSpans onlyResourceSpans = resourceSpans.get(0);
assertThat(onlyResourceSpans.getInstrumentationLibrarySpansCount()).isEqualTo(1);
InstrumentationLibrarySpans instrumentationLibrarySpans =
onlyResourceSpans.getInstrumentationLibrarySpans(0);
assertThat(instrumentationLibrarySpans.getSchemaUrl()).isEqualTo("http://url");
assertThat(instrumentationLibrarySpans.getInstrumentationLibrary())
.isEqualTo(
InstrumentationLibrary.newBuilder().setName("testLib").setVersion("1.0").build());
}
// Repeat to reuse the cache. If we forgot to clear any reused builder it will fail. // Repeat to reuse the cache. If we forgot to clear any reused builder it will fail.
@RepeatedTest(3) @RepeatedTest(3)
void toProtoSpan() { void toProtoSpan() {
@ -115,7 +149,8 @@ class SpanAdapterTest {
} }
@Test @Test
@SuppressWarnings("deprecation") // setDeprecatedCode is deprecated. @SuppressWarnings("deprecation")
// setDeprecatedCode is deprecated.
void toProtoStatus() { void toProtoStatus() {
assertThat(SpanAdapter.toStatusProto(StatusData.unset())) assertThat(SpanAdapter.toStatusProto(StatusData.unset()))
.isEqualTo( .isEqualTo(

View File

@ -28,6 +28,7 @@ import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import io.opentelemetry.proto.trace.v1.ResourceSpans; import io.opentelemetry.proto.trace.v1.ResourceSpans;
import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.testing.trace.TestSpanData; import io.opentelemetry.sdk.testing.trace.TestSpanData;
import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
@ -301,6 +302,8 @@ class OtlpGrpcSpanExporterTest {
.setLinks(Collections.emptyList()) .setLinks(Collections.emptyList())
.setTotalRecordedLinks(0) .setTotalRecordedLinks(0)
.setTotalRecordedEvents(0) .setTotalRecordedEvents(0)
.setInstrumentationLibraryInfo(
InstrumentationLibraryInfo.create("testLib", "1.0", "http://url"))
.build(); .build();
} }

View File

@ -31,7 +31,24 @@ public abstract class InstrumentationLibraryInfo {
*/ */
public static InstrumentationLibraryInfo create(String name, @Nullable String version) { public static InstrumentationLibraryInfo create(String name, @Nullable String version) {
requireNonNull(name, "name"); requireNonNull(name, "name");
return new AutoValue_InstrumentationLibraryInfo(name, version); return new AutoValue_InstrumentationLibraryInfo(name, version, null);
}
/**
* Creates a new instance of {@link InstrumentationLibraryInfo}.
*
* @param name name of the instrumentation library (e.g., "io.opentelemetry.contrib.mongodb"),
* must not be null
* @param version version of the instrumentation library (e.g., "1.0.0"), might be null
* @param schemaUrl the URL of the OpenTelemetry schema being used by this instrumentation
* library.
* @return the new instance
* @since 1.4.0
*/
public static InstrumentationLibraryInfo create(
String name, @Nullable String version, @Nullable String schemaUrl) {
requireNonNull(name, "name");
return new AutoValue_InstrumentationLibraryInfo(name, version, schemaUrl);
} }
/** /**
@ -58,6 +75,16 @@ public abstract class InstrumentationLibraryInfo {
@Nullable @Nullable
public abstract String getVersion(); public abstract String getVersion();
/**
* Returns the URL of the schema used by this instrumentation library, or {@code null} if not
* available.
*
* @return the URL of the schema used by this instrumentation library, or {@code null} if not
* available.
*/
@Nullable
public abstract String getSchemaUrl();
// Package protected ctor to avoid others to extend this class. // Package protected ctor to avoid others to extend this class.
InstrumentationLibraryInfo() {} InstrumentationLibraryInfo() {}
} }

View File

@ -30,8 +30,8 @@ public final class ComponentRegistry<V> {
/** /**
* Returns the registered value associated with this name and {@code null} version if any, * Returns the registered value associated with this name and {@code null} version if any,
* otherwise creates a new instance and associates it with the given name and {@code null} * otherwise creates a new instance and associates it with the given name and {@code null} version
* version. * and schemaUrl.
* *
* @param instrumentationName the name of the instrumentation library. * @param instrumentationName the name of the instrumentation library.
* @return the registered value associated with this name and {@code null} version. * @return the registered value associated with this name and {@code null} version.
@ -42,15 +42,33 @@ public final class ComponentRegistry<V> {
/** /**
* Returns the registered value associated with this name and version if any, otherwise creates a * Returns the registered value associated with this name and version if any, otherwise creates a
* new instance and associates it with the given name and version. * new instance and associates it with the given name and version. The schemaUrl will be set to
* null.
* *
* @param instrumentationName the name of the instrumentation library. * @param instrumentationName the name of the instrumentation library.
* @param instrumentationVersion the version of the instrumentation library. * @param instrumentationVersion the version of the instrumentation library.
* @return the registered value associated with this name and version. * @return the registered value associated with this name and version.
*/ */
public final V get(String instrumentationName, @Nullable String instrumentationVersion) { public final V get(String instrumentationName, @Nullable String instrumentationVersion) {
return get(instrumentationName, instrumentationVersion, null);
}
/**
* Returns the registered value associated with this name and version if any, otherwise creates a
* new instance and associates it with the given name and version.
*
* @param instrumentationName the name of the instrumentation library.
* @param instrumentationVersion the version of the instrumentation library.
* @param schemaUrl the URL of the OpenTelemetry schema used by the instrumentation library.
* @return the registered value associated with this name and version.
* @since 1.4.0
*/
public final V get(
String instrumentationName,
@Nullable String instrumentationVersion,
@Nullable String schemaUrl) {
InstrumentationLibraryInfo instrumentationLibraryInfo = InstrumentationLibraryInfo instrumentationLibraryInfo =
InstrumentationLibraryInfo.create(instrumentationName, instrumentationVersion); InstrumentationLibraryInfo.create(instrumentationName, instrumentationVersion, schemaUrl);
// Optimistic lookup, before creating the new component. // Optimistic lookup, before creating the new component.
V component = registry.get(instrumentationLibraryInfo); V component = registry.get(instrumentationLibraryInfo);

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
class SdkMeterBuilder implements MeterBuilder {
private final ComponentRegistry<SdkMeter> registry;
private final String instrumentationName;
private String instrumentationVersion;
private String schemaUrl;
SdkMeterBuilder(ComponentRegistry<SdkMeter> registry, String instrumentationName) {
this.registry = registry;
this.instrumentationName = instrumentationName;
}
@Override
public MeterBuilder setSchemaUrl(String schemaUrl) {
this.schemaUrl = schemaUrl;
return this;
}
@Override
public MeterBuilder setInstrumentationVersion(String instrumentationVersion) {
this.instrumentationVersion = instrumentationVersion;
return this;
}
@Override
public Meter build() {
return registry.get(instrumentationName, instrumentationVersion, schemaUrl);
}
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.internal.ComponentRegistry; import io.opentelemetry.sdk.internal.ComponentRegistry;
@ -47,17 +48,23 @@ public final class SdkMeterProvider implements MeterProvider, MetricProducer {
@Override @Override
public Meter get(String instrumentationName) { public Meter get(String instrumentationName) {
return get(instrumentationName, null); return meterBuilder(instrumentationName).build();
} }
@Override @Override
public Meter get(String instrumentationName, @Nullable String instrumentationVersion) { public Meter get(String instrumentationName, String instrumentationVersion) {
// Per the spec, both null and empty are "invalid" and a "default" should be used. return meterBuilder(instrumentationName)
.setInstrumentationVersion(instrumentationVersion)
.build();
}
@Override
public MeterBuilder meterBuilder(@Nullable String instrumentationName) {
if (instrumentationName == null || instrumentationName.isEmpty()) { if (instrumentationName == null || instrumentationName.isEmpty()) {
LOGGER.fine("Meter requested without instrumentation name."); LOGGER.fine("Meter requested without instrumentation name.");
instrumentationName = DEFAULT_METER_NAME; instrumentationName = DEFAULT_METER_NAME;
} }
return registry.get(instrumentationName, instrumentationVersion); return new SdkMeterBuilder(registry, instrumentationName);
} }
@Override @Override

View File

@ -62,18 +62,43 @@ class SdkMeterRegistryTest {
void getSameInstanceForSameName_WithoutVersion() { void getSameInstanceForSameName_WithoutVersion() {
assertThat(meterProvider.get("test")).isSameAs(meterProvider.get("test")); assertThat(meterProvider.get("test")).isSameAs(meterProvider.get("test"));
assertThat(meterProvider.get("test")).isSameAs(meterProvider.get("test", null)); assertThat(meterProvider.get("test")).isSameAs(meterProvider.get("test", null));
assertThat(meterProvider.get("test")).isSameAs(meterProvider.meterBuilder("test").build());
} }
@Test @Test
void getSameInstanceForSameName_WithVersion() { void getSameInstanceForSameName_WithVersion() {
assertThat(meterProvider.get("test", "version")).isSameAs(meterProvider.get("test", "version")); assertThat(meterProvider.get("test", "version"))
.isSameAs(meterProvider.get("test", "version"))
.isSameAs(meterProvider.meterBuilder("test").setInstrumentationVersion("version").build());
}
@Test
void getSameInstanceForSameName_WithVersionAndSchema() {
assertThat(
meterProvider
.meterBuilder("test")
.setInstrumentationVersion("version")
.setSchemaUrl("http://url")
.build())
.isSameAs(
meterProvider
.meterBuilder("test")
.setInstrumentationVersion("version")
.setSchemaUrl("http://url")
.build());
} }
@Test @Test
void propagatesInstrumentationLibraryInfoToMeter() { void propagatesInstrumentationLibraryInfoToMeter() {
InstrumentationLibraryInfo expected = InstrumentationLibraryInfo expected =
InstrumentationLibraryInfo.create("theName", "theVersion"); InstrumentationLibraryInfo.create("theName", "theVersion", "http://theschema");
SdkMeter meter = (SdkMeter) meterProvider.get(expected.getName(), expected.getVersion()); SdkMeter meter =
(SdkMeter)
meterProvider
.meterBuilder(expected.getName())
.setInstrumentationVersion(expected.getVersion())
.setSchemaUrl(expected.getSchemaUrl())
.build();
assertThat(meter.getInstrumentationLibraryInfo()).isEqualTo(expected); assertThat(meter.getInstrumentationLibraryInfo()).isEqualTo(expected);
} }
@ -123,6 +148,10 @@ class SdkMeterRegistryTest {
meter = (SdkMeter) meterProvider.get(null, null); meter = (SdkMeter) meterProvider.get(null, null);
assertThat(meter.getInstrumentationLibraryInfo().getName()) assertThat(meter.getInstrumentationLibraryInfo().getName())
.isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME); .isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME);
meter = (SdkMeter) meterProvider.meterBuilder(null).build();
assertThat(meter.getInstrumentationLibraryInfo().getName())
.isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME);
} }
@Test @Test
@ -134,5 +163,9 @@ class SdkMeterRegistryTest {
meter = (SdkMeter) meterProvider.get("", ""); meter = (SdkMeter) meterProvider.get("", "");
assertThat(meter.getInstrumentationLibraryInfo().getName()) assertThat(meter.getInstrumentationLibraryInfo().getName())
.isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME); .isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME);
meter = (SdkMeter) meterProvider.meterBuilder("").build();
assertThat(meter.getInstrumentationLibraryInfo().getName())
.isEqualTo(SdkMeterProvider.DEFAULT_METER_NAME);
} }
} }

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.trace;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
class SdkTracerBuilder implements TracerBuilder {
private final ComponentRegistry<SdkTracer> registry;
private final String instrumentationName;
private String instrumentationVersion;
private String schemaUrl;
SdkTracerBuilder(ComponentRegistry<SdkTracer> registry, String instrumentationName) {
this.registry = registry;
this.instrumentationName = instrumentationName;
}
@Override
public TracerBuilder setSchemaUrl(String schemaUrl) {
this.schemaUrl = schemaUrl;
return this;
}
@Override
public TracerBuilder setInstrumentationVersion(String instrumentationVersion) {
this.instrumentationVersion = instrumentationVersion;
return this;
}
@Override
public Tracer build() {
return registry.get(instrumentationName, instrumentationVersion, schemaUrl);
}
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.trace;
import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.CompletableResultCode;
@ -60,17 +61,24 @@ public final class SdkTracerProvider implements TracerProvider, Closeable {
@Override @Override
public Tracer get(String instrumentationName) { public Tracer get(String instrumentationName) {
return get(instrumentationName, null); return tracerBuilder(instrumentationName).build();
} }
@Override @Override
public Tracer get(String instrumentationName, @Nullable String instrumentationVersion) { public Tracer get(String instrumentationName, String instrumentationVersion) {
return tracerBuilder(instrumentationName)
.setInstrumentationVersion(instrumentationVersion)
.build();
}
@Override
public TracerBuilder tracerBuilder(@Nullable String instrumentationName) {
// Per the spec, both null and empty are "invalid" and a default value should be used. // Per the spec, both null and empty are "invalid" and a default value should be used.
if (instrumentationName == null || instrumentationName.isEmpty()) { if (instrumentationName == null || instrumentationName.isEmpty()) {
logger.fine("Tracer requested without instrumentation name."); logger.fine("Tracer requested without instrumentation name.");
instrumentationName = DEFAULT_TRACER_NAME; instrumentationName = DEFAULT_TRACER_NAME;
} }
return tracerSdkComponentRegistry.get(instrumentationName, instrumentationVersion); return new SdkTracerBuilder(tracerSdkComponentRegistry, instrumentationName);
} }
/** Returns the {@link SpanLimits} that are currently applied to created spans. */ /** Returns the {@link SpanLimits} that are currently applied to created spans. */

View File

@ -957,7 +957,7 @@ class SdkSpanBuilderTest {
+ "telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", " + "telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", "
+ "telemetry.sdk.version=\"\\d+.\\d+.\\d+(-SNAPSHOT)?\"}}, " + "telemetry.sdk.version=\"\\d+.\\d+.\\d+(-SNAPSHOT)?\"}}, "
+ "instrumentationLibraryInfo=InstrumentationLibraryInfo\\{" + "instrumentationLibraryInfo=InstrumentationLibraryInfo\\{"
+ "name=SpanBuilderSdkTest, version=null}, " + "name=SpanBuilderSdkTest, version=null, schemaUrl=null}, "
+ "name=span_name, " + "name=span_name, "
+ "kind=INTERNAL, " + "kind=INTERNAL, "
+ "startEpochNanos=[0-9]+, " + "startEpochNanos=[0-9]+, "

View File

@ -139,19 +139,44 @@ class SdkTracerProviderTest {
@Test @Test
void getSameInstanceForSameName_WithoutVersion() { void getSameInstanceForSameName_WithoutVersion() {
assertThat(tracerFactory.get("test")).isSameAs(tracerFactory.get("test")); assertThat(tracerFactory.get("test")).isSameAs(tracerFactory.get("test"));
assertThat(tracerFactory.get("test")).isSameAs(tracerFactory.get("test", null)); assertThat(tracerFactory.get("test"))
.isSameAs(tracerFactory.get("test", null))
.isSameAs(tracerFactory.tracerBuilder("test").build());
} }
@Test @Test
void getSameInstanceForSameName_WithVersion() { void getSameInstanceForSameName_WithVersion() {
assertThat(tracerFactory.get("test", "version")).isSameAs(tracerFactory.get("test", "version")); assertThat(tracerFactory.get("test", "version"))
.isSameAs(tracerFactory.get("test", "version"))
.isSameAs(tracerFactory.tracerBuilder("test").setInstrumentationVersion("version").build());
}
@Test
void getSameInstanceForSameName_WithVersionAndSchema() {
assertThat(
tracerFactory
.tracerBuilder("test")
.setInstrumentationVersion("version")
.setSchemaUrl("http://url")
.build())
.isSameAs(
tracerFactory
.tracerBuilder("test")
.setInstrumentationVersion("version")
.setSchemaUrl("http://url")
.build());
} }
@Test @Test
void propagatesInstrumentationLibraryInfoToTracer() { void propagatesInstrumentationLibraryInfoToTracer() {
InstrumentationLibraryInfo expected = InstrumentationLibraryInfo expected =
InstrumentationLibraryInfo.create("theName", "theVersion"); InstrumentationLibraryInfo.create("theName", "theVersion", "http://url");
Tracer tracer = tracerFactory.get(expected.getName(), expected.getVersion()); Tracer tracer =
tracerFactory
.tracerBuilder(expected.getName())
.setInstrumentationVersion(expected.getVersion())
.setSchemaUrl(expected.getSchemaUrl())
.build();
assertThat(((SdkTracer) tracer).getInstrumentationLibraryInfo()).isEqualTo(expected); assertThat(((SdkTracer) tracer).getInstrumentationLibraryInfo()).isEqualTo(expected);
} }

View File

@ -19,14 +19,7 @@ import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
/** Unit tests for {@link SdkTracer}. */
// Need to suppress warnings for MustBeClosed because Android 14 does not support
// try-with-resources.
@SuppressWarnings("MustBeClosedChecker")
@ExtendWith(MockitoExtension.class)
class SdkTracerTest { class SdkTracerTest {
private static final String SPAN_NAME = "span_name"; private static final String SPAN_NAME = "span_name";
@ -35,12 +28,15 @@ class SdkTracerTest {
private static final String INSTRUMENTATION_LIBRARY_VERSION = "0.2.0"; private static final String INSTRUMENTATION_LIBRARY_VERSION = "0.2.0";
private static final InstrumentationLibraryInfo instrumentationLibraryInfo = private static final InstrumentationLibraryInfo instrumentationLibraryInfo =
InstrumentationLibraryInfo.create( InstrumentationLibraryInfo.create(
INSTRUMENTATION_LIBRARY_NAME, INSTRUMENTATION_LIBRARY_VERSION); INSTRUMENTATION_LIBRARY_NAME, INSTRUMENTATION_LIBRARY_VERSION, "http://schemaurl");
private final SdkTracer tracer = private final SdkTracer tracer =
(SdkTracer) (SdkTracer)
SdkTracerProvider.builder() SdkTracerProvider.builder()
.build() .build()
.get(INSTRUMENTATION_LIBRARY_NAME, INSTRUMENTATION_LIBRARY_VERSION); .tracerBuilder(INSTRUMENTATION_LIBRARY_NAME)
.setInstrumentationVersion(INSTRUMENTATION_LIBRARY_VERSION)
.setSchemaUrl("http://schemaurl")
.build();
@Test @Test
void defaultSpanBuilder() { void defaultSpanBuilder() {