Skip to content

Commit e3b403d

Browse files
committed
Implement span live and ended metrics
1 parent cc2844d commit e3b403d

File tree

15 files changed

+666
-13
lines changed

15 files changed

+666
-13
lines changed

sdk/common/src/main/java/io/opentelemetry/sdk/internal/SemConvAttributes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ private SemConvAttributes() {}
2525
AttributeKey.stringKey("otel.component.name");
2626
public static final AttributeKey<String> ERROR_TYPE = AttributeKey.stringKey("error.type");
2727

28+
public static final AttributeKey<String> OTEL_SPAN_SAMPLING_RESULT =
29+
AttributeKey.stringKey("otel.span.sampling_result");
30+
2831
public static final AttributeKey<String> SERVER_ADDRESS =
2932
AttributeKey.stringKey("server.address");
3033
public static final AttributeKey<Long> SERVER_PORT = AttributeKey.longKey("server.port");

sdk/common/src/test/java/io/opentelemetry/sdk/internal/SemConvAttributesTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ void testAttributeKeys() {
2222
.isEqualTo(OtelIncubatingAttributes.OTEL_COMPONENT_NAME);
2323
assertThat(SemConvAttributes.OTEL_COMPONENT_TYPE)
2424
.isEqualTo(OtelIncubatingAttributes.OTEL_COMPONENT_TYPE);
25+
assertThat(SemConvAttributes.OTEL_SPAN_SAMPLING_RESULT)
26+
.isEqualTo(OtelIncubatingAttributes.OTEL_SPAN_SAMPLING_RESULT);
2527

2628
assertThat(SemConvAttributes.ERROR_TYPE).isEqualTo(ErrorAttributes.ERROR_TYPE);
2729

sdk/trace/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
testImplementation(project(":sdk:testing"))
3333
testImplementation("com.google.guava:guava")
3434
testImplementation("com.google.guava:guava-testlib")
35+
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
3536

3637
jmh(project(":sdk:metrics"))
3738
jmh(project(":sdk:testing")) {
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.trace;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.Attributes;
10+
import io.opentelemetry.api.trace.Span;
11+
import io.opentelemetry.api.trace.SpanContext;
12+
import io.opentelemetry.api.trace.StatusCode;
13+
import io.opentelemetry.sdk.trace.internal.metrics.SpanMetrics;
14+
import java.util.concurrent.TimeUnit;
15+
import javax.annotation.Nullable;
16+
import javax.annotation.concurrent.Immutable;
17+
18+
/**
19+
* Span implementation used from {@link io.opentelemetry.sdk.trace.SdkTracer} when starting a span
20+
* which is not recording. All operations are noop, except for {@link #end()}, which ensures health
21+
* metrics are still collected.
22+
*/
23+
@Immutable
24+
final class NonRecordingSpan implements Span {
25+
26+
private final SpanContext spanContext;
27+
private final SpanMetrics.Recording metricRecording;
28+
29+
NonRecordingSpan(SpanContext spanContext, SpanMetrics.Recording metricRecording) {
30+
this.spanContext = spanContext;
31+
this.metricRecording = metricRecording;
32+
}
33+
34+
@Override
35+
public Span setAttribute(String key, @Nullable String value) {
36+
return this;
37+
}
38+
39+
@Override
40+
public Span setAttribute(String key, long value) {
41+
return this;
42+
}
43+
44+
@Override
45+
public Span setAttribute(String key, double value) {
46+
return this;
47+
}
48+
49+
@Override
50+
public Span setAttribute(String key, boolean value) {
51+
return this;
52+
}
53+
54+
@Override
55+
public <T> Span setAttribute(AttributeKey<T> key, @Nullable T value) {
56+
return this;
57+
}
58+
59+
@Override
60+
public Span setAllAttributes(Attributes attributes) {
61+
return this;
62+
}
63+
64+
@Override
65+
public Span addEvent(String name) {
66+
return this;
67+
}
68+
69+
@Override
70+
public Span addEvent(String name, long timestamp, TimeUnit unit) {
71+
return this;
72+
}
73+
74+
@Override
75+
public Span addEvent(String name, Attributes attributes) {
76+
return this;
77+
}
78+
79+
@Override
80+
public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
81+
return this;
82+
}
83+
84+
@Override
85+
public Span setStatus(StatusCode statusCode) {
86+
return this;
87+
}
88+
89+
@Override
90+
public Span setStatus(StatusCode statusCode, String description) {
91+
return this;
92+
}
93+
94+
@Override
95+
public Span recordException(Throwable exception) {
96+
return this;
97+
}
98+
99+
@Override
100+
public Span recordException(Throwable exception, Attributes additionalAttributes) {
101+
return this;
102+
}
103+
104+
@Override
105+
public Span updateName(String name) {
106+
return this;
107+
}
108+
109+
@Override
110+
public void end() {
111+
metricRecording.recordSpanEnd();
112+
}
113+
114+
@Override
115+
public void end(long timestamp, TimeUnit unit) {
116+
end();
117+
}
118+
119+
@Override
120+
public SpanContext getSpanContext() {
121+
return spanContext;
122+
}
123+
124+
@Override
125+
public boolean isRecording() {
126+
return false;
127+
}
128+
129+
@Override
130+
public String toString() {
131+
return "NonRecordingSpan{" + spanContext + '}';
132+
}
133+
}

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.opentelemetry.sdk.trace.data.SpanData;
2727
import io.opentelemetry.sdk.trace.data.StatusData;
2828
import io.opentelemetry.sdk.trace.internal.ExtendedSpanProcessor;
29+
import io.opentelemetry.sdk.trace.internal.metrics.SpanMetrics;
2930
import java.util.ArrayList;
3031
import java.util.Collections;
3132
import java.util.List;
@@ -99,6 +100,8 @@ final class SdkSpan implements ReadWriteSpan {
99100
@GuardedBy("lock")
100101
private long endEpochNanos;
101102

103+
private final SpanMetrics.Recording metricRecording;
104+
102105
private enum EndState {
103106
NOT_ENDED,
104107
ENDING,
@@ -132,7 +135,8 @@ private SdkSpan(
132135
@Nullable AttributesMap attributes,
133136
@Nullable List<LinkData> links,
134137
int totalRecordedLinks,
135-
long startEpochNanos) {
138+
long startEpochNanos,
139+
SpanMetrics.Recording metricRecording) {
136140
this.context = context;
137141
this.instrumentationScopeInfo = instrumentationScopeInfo;
138142
this.parentSpanContext = parentSpanContext;
@@ -143,6 +147,7 @@ private SdkSpan(
143147
this.spanProcessor = spanProcessor;
144148
this.exceptionAttributeResolver = exceptionAttributeResolver;
145149
this.resource = resource;
150+
this.metricRecording = metricRecording;
146151
this.hasEnded = EndState.NOT_ENDED;
147152
this.clock = clock;
148153
this.startEpochNanos = startEpochNanos;
@@ -180,7 +185,8 @@ static SdkSpan startSpan(
180185
@Nullable AttributesMap attributes,
181186
@Nullable List<LinkData> links,
182187
int totalRecordedLinks,
183-
long userStartEpochNanos) {
188+
long userStartEpochNanos,
189+
SpanMetrics.Recording metricsRecording) {
184190
boolean createdAnchoredClock;
185191
AnchoredClock clock;
186192
if (parentSpan instanceof SdkSpan) {
@@ -219,7 +225,8 @@ static SdkSpan startSpan(
219225
attributes,
220226
links,
221227
totalRecordedLinks,
222-
startEpochNanos);
228+
startEpochNanos,
229+
metricsRecording);
223230
// Call onStart here instead of calling in the constructor to make sure the span is completely
224231
// initialized.
225232
if (spanProcessor.isStartRequired()) {
@@ -557,6 +564,7 @@ private void endInternal(long endEpochNanos) {
557564
spanEndingThread = Thread.currentThread();
558565
hasEnded = EndState.ENDING;
559566
}
567+
metricRecording.recordSpanEnd();
560568
if (spanProcessor instanceof ExtendedSpanProcessor) {
561569
ExtendedSpanProcessor extendedSpanProcessor = (ExtendedSpanProcessor) spanProcessor;
562570
if (extendedSpanProcessor.isOnEndingRequired()) {

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.sdk.internal.AttributeUtil;
2525
import io.opentelemetry.sdk.internal.AttributesMap;
2626
import io.opentelemetry.sdk.trace.data.LinkData;
27+
import io.opentelemetry.sdk.trace.internal.metrics.SpanMetrics;
2728
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
2829
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
2930
import java.util.ArrayList;
@@ -204,8 +205,10 @@ public Span startSpan() {
204205
/* remote= */ false,
205206
tracerSharedState.isIdGeneratorSafeToSkipIdValidation());
206207

208+
SpanMetrics.Recording metricsRecording =
209+
tracerSharedState.getSpanMetrics().recordSpanStart(samplingResult);
207210
if (!isRecording(samplingDecision)) {
208-
return Span.wrap(spanContext);
211+
return new NonRecordingSpan(spanContext, metricsRecording);
209212
}
210213
Attributes samplingAttributes = samplingResult.getAttributes();
211214
if (!samplingAttributes.isEmpty()) {
@@ -232,7 +235,8 @@ public Span startSpan() {
232235
recordedAttributes,
233236
currentLinks,
234237
totalNumberOfLinksAdded,
235-
startEpochNanos);
238+
startEpochNanos,
239+
metricsRecording);
236240
}
237241

238242
private AttributesMap attributes() {

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@
55

66
package io.opentelemetry.sdk.trace;
77

8+
import io.opentelemetry.api.metrics.MeterProvider;
89
import io.opentelemetry.api.trace.Tracer;
910
import io.opentelemetry.api.trace.TracerBuilder;
1011
import io.opentelemetry.api.trace.TracerProvider;
1112
import io.opentelemetry.sdk.common.Clock;
1213
import io.opentelemetry.sdk.common.CompletableResultCode;
1314
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
15+
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
1416
import io.opentelemetry.sdk.internal.ComponentRegistry;
1517
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
1618
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1719
import io.opentelemetry.sdk.resources.Resource;
1820
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
1921
import io.opentelemetry.sdk.trace.internal.TracerConfig;
22+
import io.opentelemetry.sdk.trace.internal.metrics.SemConvSpanMetrics;
23+
import io.opentelemetry.sdk.trace.internal.metrics.SpanMetrics;
2024
import io.opentelemetry.sdk.trace.samplers.Sampler;
2125
import java.io.Closeable;
2226
import java.util.List;
@@ -54,7 +58,9 @@ public static SdkTracerProviderBuilder builder() {
5458
Sampler sampler,
5559
List<SpanProcessor> spanProcessors,
5660
ScopeConfigurator<TracerConfig> tracerConfigurator,
57-
ExceptionAttributeResolver exceptionAttributeResolver) {
61+
ExceptionAttributeResolver exceptionAttributeResolver,
62+
InternalTelemetryVersion internalTelemetryVersion,
63+
Supplier<MeterProvider> meterProviderSupplier) {
5864
this.sharedState =
5965
new TracerSharedState(
6066
clock,
@@ -63,7 +69,8 @@ public static SdkTracerProviderBuilder builder() {
6369
spanLimitsSupplier,
6470
sampler,
6571
spanProcessors,
66-
exceptionAttributeResolver);
72+
exceptionAttributeResolver,
73+
createSpanMetrics(internalTelemetryVersion, meterProviderSupplier));
6774
this.tracerSdkComponentRegistry =
6875
new ComponentRegistry<>(
6976
instrumentationScopeInfo ->
@@ -74,6 +81,21 @@ public static SdkTracerProviderBuilder builder() {
7481
this.tracerConfigurator = tracerConfigurator;
7582
}
7683

84+
private static SpanMetrics createSpanMetrics(
85+
InternalTelemetryVersion internalTelemetryVersion,
86+
Supplier<MeterProvider> meterProviderSupplier) {
87+
switch (internalTelemetryVersion) {
88+
case LEGACY:
89+
case DISABLED:
90+
return SpanMetrics.noop();
91+
case V1_33:
92+
case LATEST:
93+
return new SemConvSpanMetrics(meterProviderSupplier);
94+
}
95+
throw new IllegalStateException(
96+
"Unhandled telemetry schema version: " + internalTelemetryVersion);
97+
}
98+
7799
private TracerConfig getTracerConfig(InstrumentationScopeInfo instrumentationScopeInfo) {
78100
TracerConfig tracerConfig = tracerConfigurator.apply(instrumentationScopeInfo);
79101
return tracerConfig == null ? TracerConfig.defaultConfig() : tracerConfig;

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77

88
import static java.util.Objects.requireNonNull;
99

10+
import io.opentelemetry.api.GlobalOpenTelemetry;
11+
import io.opentelemetry.api.metrics.MeterProvider;
1012
import io.opentelemetry.api.trace.Span;
1113
import io.opentelemetry.sdk.common.Clock;
1214
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1315
import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver;
1416
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
17+
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
1518
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1619
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
1720
import io.opentelemetry.sdk.resources.Resource;
@@ -35,6 +38,9 @@ public final class SdkTracerProviderBuilder {
3538
private Resource resource = Resource.getDefault();
3639
private Supplier<SpanLimits> spanLimitsSupplier = SpanLimits::getDefault;
3740
private Sampler sampler = DEFAULT_SAMPLER;
41+
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
42+
private InternalTelemetryVersion internalTelemetryVersion =
43+
InternalTelemetryVersion.DISABLED;
3844
private ScopeConfiguratorBuilder<TracerConfig> tracerConfiguratorBuilder =
3945
TracerConfig.configuratorBuilder();
4046
private ExceptionAttributeResolver exceptionAttributeResolver =
@@ -177,6 +183,27 @@ public SdkTracerProviderBuilder addSpanProcessorFirst(SpanProcessor spanProcesso
177183
return this;
178184
}
179185

186+
/**
187+
* Sets the {@link MeterProvider} supplier used to collect self-monitoring metrics. If not set,
188+
* uses {@link GlobalOpenTelemetry#getMeterProvider()}.
189+
*/
190+
public SdkTracerProviderBuilder setMeterProvider(Supplier<MeterProvider> meterProviderSupplier) {
191+
requireNonNull(meterProviderSupplier, "meterProviderSupplier");
192+
this.meterProviderSupplier = meterProviderSupplier;
193+
return this;
194+
}
195+
196+
/**
197+
* Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics the
198+
* tracers originating from this provider collect.
199+
*/
200+
public SdkTracerProviderBuilder setInternalTelemetry(
201+
InternalTelemetryVersion internalTelemetryVersion) {
202+
requireNonNull(internalTelemetryVersion, "internalTelemetryVersion");
203+
this.internalTelemetryVersion = internalTelemetryVersion;
204+
return this;
205+
}
206+
180207
/**
181208
* Set the tracer configurator, which computes {@link TracerConfig} for each {@link
182209
* InstrumentationScopeInfo}.
@@ -247,7 +274,9 @@ public SdkTracerProvider build() {
247274
sampler,
248275
spanProcessors,
249276
tracerConfiguratorBuilder.build(),
250-
exceptionAttributeResolver);
277+
exceptionAttributeResolver,
278+
internalTelemetryVersion,
279+
meterProviderSupplier);
251280
}
252281

253282
SdkTracerProviderBuilder() {}

0 commit comments

Comments
 (0)