Skip to content

Commit 1947c0f

Browse files
author
Mateusz Rzeszutek
authored
Add support for schemaUrls auto-computed from AttributesExtrators (#8864)
1 parent ba2f8d2 commit 1947c0f

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.instrumentation.api.instrumenter;
77

88
import static java.util.Objects.requireNonNull;
9+
import static java.util.logging.Level.WARNING;
910

1011
import com.google.errorprone.annotations.CanIgnoreReturnValue;
1112
import io.opentelemetry.api.OpenTelemetry;
@@ -22,11 +23,13 @@
2223
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
2324
import io.opentelemetry.instrumentation.api.internal.InstrumenterBuilderAccess;
2425
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil;
26+
import io.opentelemetry.instrumentation.api.internal.SchemaUrlProvider;
2527
import io.opentelemetry.instrumentation.api.internal.SpanKey;
2628
import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
2729
import java.util.ArrayList;
2830
import java.util.List;
2931
import java.util.Set;
32+
import java.util.logging.Logger;
3033
import java.util.stream.Collectors;
3134
import java.util.stream.Stream;
3235
import javax.annotation.Nullable;
@@ -39,6 +42,8 @@
3942
*/
4043
public final class InstrumenterBuilder<REQUEST, RESPONSE> {
4144

45+
private static final Logger logger = Logger.getLogger(InstrumenterBuilder.class.getName());
46+
4247
private static final SpanSuppressionStrategy spanSuppressionStrategy =
4348
SpanSuppressionStrategy.fromConfig(
4449
ConfigPropertiesUtil.getString(
@@ -285,6 +290,7 @@ Tracer buildTracer() {
285290
if (instrumentationVersion != null) {
286291
tracerBuilder.setInstrumentationVersion(instrumentationVersion);
287292
}
293+
String schemaUrl = getSchemaUrl();
288294
if (schemaUrl != null) {
289295
tracerBuilder.setSchemaUrl(schemaUrl);
290296
}
@@ -305,6 +311,7 @@ List<OperationListener> buildOperationListeners() {
305311
if (instrumentationVersion != null) {
306312
meterBuilder.setInstrumentationVersion(instrumentationVersion);
307313
}
314+
String schemaUrl = getSchemaUrl();
308315
if (schemaUrl != null) {
309316
meterBuilder.setSchemaUrl(schemaUrl);
310317
}
@@ -316,6 +323,36 @@ List<OperationListener> buildOperationListeners() {
316323
return listeners;
317324
}
318325

326+
@Nullable
327+
private String getSchemaUrl() {
328+
// url set explicitly overrides url computed using attributes extractors
329+
if (schemaUrl != null) {
330+
return schemaUrl;
331+
}
332+
Set<String> computedSchemaUrls =
333+
attributesExtractors.stream()
334+
.filter(SchemaUrlProvider.class::isInstance)
335+
.map(SchemaUrlProvider.class::cast)
336+
.flatMap(
337+
provider -> {
338+
String url = provider.internalGetSchemaUrl();
339+
return url == null ? Stream.of() : Stream.of(url);
340+
})
341+
.collect(Collectors.toSet());
342+
switch (computedSchemaUrls.size()) {
343+
case 0:
344+
return null;
345+
case 1:
346+
return computedSchemaUrls.iterator().next();
347+
default:
348+
logger.log(
349+
WARNING,
350+
"Multiple schemaUrls were detected: {0}. The built Instrumenter will have no schemaUrl assigned.",
351+
computedSchemaUrls);
352+
return null;
353+
}
354+
}
355+
319356
SpanSuppressor buildSpanSuppressor() {
320357
return spanSuppressionStrategy.create(getSpanKeysFromAttributesExtractors());
321358
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.internal;
7+
8+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
9+
import javax.annotation.Nullable;
10+
11+
/**
12+
* Returns the OpenTelemetry schema URL associated with the {@link AttributesExtractor} that
13+
* implements this interface.
14+
*
15+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
16+
* at any time.
17+
*/
18+
public interface SchemaUrlProvider {
19+
20+
@Nullable
21+
String internalGetSchemaUrl();
22+
}

instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.opentelemetry.context.Context;
2626
import io.opentelemetry.context.ContextKey;
2727
import io.opentelemetry.context.propagation.TextMapGetter;
28+
import io.opentelemetry.instrumentation.api.internal.SchemaUrlProvider;
2829
import io.opentelemetry.instrumentation.api.internal.SpanKey;
2930
import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
3031
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
@@ -113,6 +114,30 @@ public void onEnd(
113114
}
114115
}
115116

117+
static class AttributesExtractorWithSchemaUrl
118+
implements AttributesExtractor<Map<String, String>, Map<String, String>>, SchemaUrlProvider {
119+
120+
@Override
121+
public void onStart(
122+
AttributesBuilder attributes, Context parentContext, Map<String, String> request) {
123+
attributes.put("key", "value");
124+
}
125+
126+
@Override
127+
public void onEnd(
128+
AttributesBuilder attributes,
129+
Context context,
130+
Map<String, String> request,
131+
@Nullable Map<String, String> response,
132+
@Nullable Throwable error) {}
133+
134+
@Nullable
135+
@Override
136+
public String internalGetSchemaUrl() {
137+
return "schemaUrl from extractor";
138+
}
139+
}
140+
116141
static class LinksExtractor implements SpanLinksExtractor<Map<String, String>> {
117142

118143
@Override
@@ -583,11 +608,60 @@ void instrumentationVersion_custom() {
583608
}
584609

585610
@Test
586-
void schemaUrl() {
611+
void schemaUrl_setExplicitly() {
612+
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
613+
Instrumenter.<Map<String, String>, Map<String, String>>builder(
614+
otelTesting.getOpenTelemetry(), "test", name -> "span")
615+
.setSchemaUrl("https://opentelemetry.io/schemas/1.0.0")
616+
.buildInstrumenter();
617+
618+
Context context = instrumenter.start(Context.root(), emptyMap());
619+
assertThat(Span.fromContext(context)).isNotNull();
620+
621+
instrumenter.end(context, emptyMap(), emptyMap(), null);
622+
623+
InstrumentationScopeInfo expectedLibraryInfo =
624+
InstrumentationScopeInfo.builder("test")
625+
.setSchemaUrl("https://opentelemetry.io/schemas/1.0.0")
626+
.build();
627+
otelTesting
628+
.assertTraces()
629+
.hasTracesSatisfyingExactly(
630+
trace ->
631+
trace.hasSpansSatisfyingExactly(
632+
span -> span.hasName("span").hasInstrumentationScopeInfo(expectedLibraryInfo)));
633+
}
634+
635+
@Test
636+
void schemaUrl_computedFromExtractors() {
637+
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
638+
Instrumenter.<Map<String, String>, Map<String, String>>builder(
639+
otelTesting.getOpenTelemetry(), "test", name -> "span")
640+
.addAttributesExtractor(new AttributesExtractorWithSchemaUrl())
641+
.buildInstrumenter();
642+
643+
Context context = instrumenter.start(Context.root(), emptyMap());
644+
assertThat(Span.fromContext(context)).isNotNull();
645+
646+
instrumenter.end(context, emptyMap(), emptyMap(), null);
647+
648+
InstrumentationScopeInfo expectedLibraryInfo =
649+
InstrumentationScopeInfo.builder("test").setSchemaUrl("schemaUrl from extractor").build();
650+
otelTesting
651+
.assertTraces()
652+
.hasTracesSatisfyingExactly(
653+
trace ->
654+
trace.hasSpansSatisfyingExactly(
655+
span -> span.hasName("span").hasInstrumentationScopeInfo(expectedLibraryInfo)));
656+
}
657+
658+
@Test
659+
void schemaUrl_schemaSetExplicitlyOverridesSchemaComputedFromExtractors() {
587660
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
588661
Instrumenter.<Map<String, String>, Map<String, String>>builder(
589662
otelTesting.getOpenTelemetry(), "test", name -> "span")
590663
.setSchemaUrl("https://opentelemetry.io/schemas/1.0.0")
664+
.addAttributesExtractor(new AttributesExtractorWithSchemaUrl())
591665
.buildInstrumenter();
592666

593667
Context context = instrumenter.start(Context.root(), emptyMap());

0 commit comments

Comments
 (0)