Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
10 changes: 9 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Comparing source compatibility of opentelemetry-sdk-trace-1.58.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.57.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.export.SimpleSpanProcessorBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.export.SimpleSpanProcessorBuilder setMeterProvider(io.opentelemetry.api.metrics.MeterProvider)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.SdkTracerProviderBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.SdkTracerProviderBuilder setMeterProvider(io.opentelemetry.api.metrics.MeterProvider)
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static void configureTracerProvider(
List<Closeable> closeables) {

tracerProviderBuilder.setSpanLimits(configureSpanLimits(config));
tracerProviderBuilder.setMeterProvider(meterProvider);

String sampler = config.getString("otel.traces.sampler", PARENTBASED_ALWAYS_ON);
tracerProviderBuilder.setSampler(
Expand Down Expand Up @@ -80,7 +81,8 @@ static List<SpanProcessor> configureSpanProcessors(
for (String simpleProcessorExporterNames : simpleProcessorExporterNames) {
SpanExporter exporter = exportersByNameCopy.remove(simpleProcessorExporterNames);
if (exporter != null) {
SpanProcessor spanProcessor = SimpleSpanProcessor.create(exporter);
SpanProcessor spanProcessor =
SimpleSpanProcessor.builder(exporter).setMeterProvider(meterProvider).build();
closeables.add(spanProcessor);
spanProcessors.add(spanProcessor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessorBuilder;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.time.Duration;
import java.util.Map;
Expand Down Expand Up @@ -62,7 +63,12 @@ public SpanProcessor create(SpanProcessorModel model, DeclarativeConfigContext c
FileConfigUtil.requireNonNull(
simpleModel.getExporter(), "simple span processor exporter");
SpanExporter spanExporter = SpanExporterFactory.getInstance().create(exporterModel, context);
return context.addCloseable(SimpleSpanProcessor.create(spanExporter));
SimpleSpanProcessorBuilder builder = SimpleSpanProcessor.builder(spanExporter);
MeterProvider meterProvider = context.getMeterProvider();
if (meterProvider != null) {
builder.setMeterProvider(meterProvider);
}
return context.addCloseable(builder.build());
}

Map.Entry<String, Object> keyValue =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ void create_Configured() throws NoSuchFieldException, IllegalAccessException {
.extracting("sharedState")
.extracting("activeSpanProcessor")
.extracting("worker")
.extracting("processedSpansCounter")
.extracting("spanProcessorMetrics")
.extracting("processedSpans")
.extracting("sdkMeter")
.extracting("meterProviderSharedState")
.isEqualTo(sharedState);
Expand Down
15 changes: 12 additions & 3 deletions sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ final class SdkSpan implements ReadWriteSpan {
private final InstrumentationScopeInfo instrumentationScopeInfo;
// The start time of the span.
private final long startEpochNanos;
// Callback to run when span ends to record metrics.
private final Runnable recordEndMetrics;

// Lock used to internally guard the mutable state of this instance
private final Object lock = new Object();

Expand Down Expand Up @@ -132,7 +135,8 @@ private SdkSpan(
@Nullable AttributesMap attributes,
@Nullable List<LinkData> links,
int totalRecordedLinks,
long startEpochNanos) {
long startEpochNanos,
Runnable recordEndMetrics) {
this.context = context;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.parentSpanContext = parentSpanContext;
Expand All @@ -148,6 +152,7 @@ private SdkSpan(
this.startEpochNanos = startEpochNanos;
this.attributes = attributes;
this.spanLimits = spanLimits;
this.recordEndMetrics = recordEndMetrics;
}

/**
Expand All @@ -163,6 +168,7 @@ private SdkSpan(
* @param resource the resource associated with this span.
* @param attributes the attributes set during span creation.
* @param links the links set during span creation, may be truncated. The list MUST be immutable.
* @param recordEndMetrics a {@link Runnable} to run when the span is ended to record metrics.
* @return a new and started span.
*/
static SdkSpan startSpan(
Expand All @@ -180,7 +186,8 @@ static SdkSpan startSpan(
@Nullable AttributesMap attributes,
@Nullable List<LinkData> links,
int totalRecordedLinks,
long userStartEpochNanos) {
long userStartEpochNanos,
Runnable recordEndMetrics) {
boolean createdAnchoredClock;
AnchoredClock clock;
if (parentSpan instanceof SdkSpan) {
Expand Down Expand Up @@ -219,7 +226,8 @@ static SdkSpan startSpan(
attributes,
links,
totalRecordedLinks,
startEpochNanos);
startEpochNanos,
recordEndMetrics);
// Call onStart here instead of calling in the constructor to make sure the span is completely
// initialized.
if (spanProcessor.isStartRequired()) {
Expand Down Expand Up @@ -557,6 +565,7 @@ private void endInternal(long endEpochNanos) {
spanEndingThread = Thread.currentThread();
hasEnded = EndState.ENDING;
}
recordEndMetrics.run();
if (spanProcessor instanceof ExtendedSpanProcessor) {
ExtendedSpanProcessor extendedSpanProcessor = (ExtendedSpanProcessor) spanProcessor;
if (extendedSpanProcessor.isOnEndingRequired()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ public Span startSpan() {
/* remote= */ false,
tracerSharedState.isIdGeneratorSafeToSkipIdValidation());

Runnable recordEndSpanMetrics =
tracerSharedState.getTracerMetrics().startSpan(parentSpanContext, samplingDecision);

if (!isRecording(samplingDecision)) {
return Span.wrap(spanContext);
}
Expand Down Expand Up @@ -232,7 +235,8 @@ public Span startSpan() {
recordedAttributes,
currentLinks,
totalNumberOfLinksAdded,
startEpochNanos);
startEpochNanos,
recordEndSpanMetrics);
}

private AttributesMap attributes() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.trace;

import static io.opentelemetry.api.common.AttributeKey.stringKey;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;

/**
* SDK metrics exported for started and ended spans as defined in the <a
* href="https://opentelemetry.io/docs/specs/semconv/otel/sdk-metrics/#span-metrics">semantic
* conventions</a>.
*/
final class SdkTracerMetrics {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

#nit For alignment with ExporterInstrumentation

Suggested change
final class SdkTracerMetrics {
final class SdkTracerInstrumentation {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If it's possible, I'd like to suggest the Metrics name, and maybe renaming that other one. I think instrumentation is something else, usually using interceptors or such to add telemetry in a somewhat black-box way. These are just shared state + helper methods and don't seem like instrumentation to me (admittedly a personal take).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's fine. Personally, I think any code that records spans, metrics, or logs is instrumentation. With that said, for the foreseeable future, these do only record metrics so *Metrics probably does more intuitively capture what's going on. I'm more interested in consistency than the name.


// Visible for testing
static final AttributeKey<String> OTEL_SPAN_PARENT_ORIGIN = stringKey("otel.span.parent.origin");
// Visible for testing
static final AttributeKey<String> OTEL_SPAN_SAMPLING_RESULT =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You should probably move those into SemConvAttributes with the corresponding tests for up-to-date-ness.

stringKey("otel.span.sampling_result");

private static final Attributes noParentDrop =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN, "none", OTEL_SPAN_SAMPLING_RESULT, SamplingDecision.DROP.name());
private static final Attributes noParentRecordOnly =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"none",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_ONLY.name());
private static final Attributes noParentRecordAndSample =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"none",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_AND_SAMPLE.name());

private static final Attributes remoteParentDrop =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"remote",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.DROP.name());
private static final Attributes remoteParentRecordOnly =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"remote",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_ONLY.name());
private static final Attributes remoteParentRecordAndSample =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"remote",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_AND_SAMPLE.name());

private static final Attributes localParentDrop =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"local",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.DROP.name());
private static final Attributes localParentRecordOnly =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"local",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_ONLY.name());
private static final Attributes localParentRecordAndSample =
Attributes.of(
OTEL_SPAN_PARENT_ORIGIN,
"local",
OTEL_SPAN_SAMPLING_RESULT,
SamplingDecision.RECORD_AND_SAMPLE.name());

private static final Attributes recordOnly =
Attributes.of(OTEL_SPAN_SAMPLING_RESULT, SamplingDecision.RECORD_ONLY.name());
private static final Attributes recordAndSample =
Attributes.of(OTEL_SPAN_SAMPLING_RESULT, SamplingDecision.RECORD_AND_SAMPLE.name());

private final LongCounter startedSpans;
private final LongUpDownCounter liveSpans;

SdkTracerMetrics(MeterProvider meterProvider) {
Meter meter = meterProvider.get("io.opentelemetry.sdk.trace");

startedSpans =
meter
.counterBuilder("otel.sdk.span.started")
.setUnit("{span}")
.setDescription("The number of created spans.")
.build();
liveSpans =
meter
.upDownCounterBuilder("otel.sdk.span.live")
.setUnit("{span}")
.setDescription(
"The number of created spans with recording=true for which the end operation has not been called yet.")
.build();
}

/**
* Records metrics for when a span starts and returns a {@link Runnable} to execute when ending
* the span.
*/
Runnable startSpan(SpanContext parentSpanContext, SamplingDecision samplingDecision) {
if (!parentSpanContext.isValid()) {
switch (samplingDecision) {
case DROP:
startedSpans.add(1, noParentDrop);
return SdkTracerMetrics::noop;
case RECORD_ONLY:
startedSpans.add(1, noParentRecordOnly);
liveSpans.add(1, recordOnly);
return this::decrementRecordOnly;
case RECORD_AND_SAMPLE:
startedSpans.add(1, noParentRecordAndSample);
liveSpans.add(1, recordAndSample);
return this::decrementRecordAndSample;
}
throw new IllegalArgumentException("Unrecognized sampling decision: " + samplingDecision);
} else if (parentSpanContext.isRemote()) {
switch (samplingDecision) {
case DROP:
startedSpans.add(1, remoteParentDrop);
return SdkTracerMetrics::noop;
case RECORD_ONLY:
startedSpans.add(1, remoteParentRecordOnly);
liveSpans.add(1, recordOnly);
return this::decrementRecordOnly;
case RECORD_AND_SAMPLE:
startedSpans.add(1, remoteParentRecordAndSample);
liveSpans.add(1, recordAndSample);
return this::decrementRecordAndSample;
}
throw new IllegalArgumentException("Unrecognized sampling decision: " + samplingDecision);
}
// local parent
switch (samplingDecision) {
case DROP:
startedSpans.add(1, localParentDrop);
return SdkTracerMetrics::noop;
case RECORD_ONLY:
startedSpans.add(1, localParentRecordOnly);
liveSpans.add(1, recordOnly);
return this::decrementRecordOnly;
case RECORD_AND_SAMPLE:
startedSpans.add(1, localParentRecordAndSample);
liveSpans.add(1, recordAndSample);
return this::decrementRecordAndSample;
}
throw new IllegalArgumentException("Unrecognized sampling decision: " + samplingDecision);
}

private static void noop() {}

private void decrementRecordOnly() {
liveSpans.add(-1, recordOnly);
}

private void decrementRecordAndSample() {
liveSpans.add(-1, recordAndSample);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.sdk.trace;

import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.api.trace.TracerProvider;
Expand Down Expand Up @@ -54,7 +55,8 @@ public static SdkTracerProviderBuilder builder() {
Sampler sampler,
List<SpanProcessor> spanProcessors,
ScopeConfigurator<TracerConfig> tracerConfigurator,
ExceptionAttributeResolver exceptionAttributeResolver) {
ExceptionAttributeResolver exceptionAttributeResolver,
MeterProvider meterProvider) {
this.sharedState =
new TracerSharedState(
clock,
Expand All @@ -63,7 +65,8 @@ public static SdkTracerProviderBuilder builder() {
spanLimitsSupplier,
sampler,
spanProcessors,
exceptionAttributeResolver);
exceptionAttributeResolver,
new SdkTracerMetrics(meterProvider));
this.tracerSdkComponentRegistry =
new ComponentRegistry<>(
instrumentationScopeInfo ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static java.util.Objects.requireNonNull;

import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
Expand Down Expand Up @@ -38,6 +39,7 @@ public final class SdkTracerProviderBuilder {
TracerConfig.configuratorBuilder();
private ExceptionAttributeResolver exceptionAttributeResolver =
ExceptionAttributeResolver.getDefault();
private MeterProvider meterProvider = MeterProvider.noop();

/**
* Assign a {@link Clock}. {@link Clock} will be used each time a {@link Span} is started, ended
Expand Down Expand Up @@ -232,6 +234,17 @@ SdkTracerProviderBuilder setExceptionAttributeResolver(
return this;
}

/**
* Sets the {@link MeterProvider} to use to generate <a
* href="https://opentelemetry.io/docs/specs/semconv/otel/sdk-metrics/#span-metrics">SDK Span
* Metrics</a>.
*/
public SdkTracerProviderBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");
this.meterProvider = meterProvider;
return this;
}

/**
* Create a new {@link SdkTracerProvider} instance with the configuration.
*
Expand All @@ -246,7 +259,8 @@ public SdkTracerProvider build() {
sampler,
spanProcessors,
tracerConfiguratorBuilder.build(),
exceptionAttributeResolver);
exceptionAttributeResolver,
meterProvider);
}

SdkTracerProviderBuilder() {}
Expand Down
Loading
Loading