Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ <T, E extends Throwable> T startAndCall(
<E extends Throwable> void startAndRun(
SpanRunnable<E> runnable, BiConsumer<Span, Throwable> handleException) throws E;

/**
* Sets the span type, which identifies the class of spans this span belongs to, such as HTTP
* client requests or database calls, as defined by semantic conventions. The span type can't be
* changed after the span is started.
*
* <p>The span type is a case-sensitive ASCII string of at most 255 characters. It starts with a
* letter and contains only letters, digits, {@code _}, {@code .}, {@code -}, and {@code /}.
* Implementations may ignore span types that don't conform to this syntax. An empty span type is
* equivalent to an unset one.
*
* @param type the span type.
* @return this.
*/
default ExtendedSpanBuilder setType(String type) {
return this;
}

/** {@inheritDoc} */
@Override
ExtendedSpanBuilder setParent(Context context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ void incubatingApi() {
ExtendedSpanBuilder spanBuilder =
(ExtendedSpanBuilder) ExtendedDefaultTracer.getNoop().spanBuilder("test");
assertThat(spanBuilder.setParentFrom(null, null)).isSameAs(spanBuilder);
assertThat(spanBuilder.setType("http.client.request")).isSameAs(spanBuilder);
assertThat(spanBuilder.setType(null)).isSameAs(spanBuilder);

SpanRunnable<RuntimeException> spanRunnable = Mockito.mock(SpanRunnable.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package io.opentelemetry.sdk;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.incubator.trace.ExtendedSpanBuilder;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
Expand Down Expand Up @@ -39,6 +41,7 @@
* <ul>
* <li>{@link BenchmarkState#spanSize}: the size of the span, which is a composite of the number
* of attributes, events, and links attached to the span.
* <li>{@link BenchmarkState#withType}: whether the span type is set.
* </ul>
*
* <p>Each operation consists of recording {@link SpanRecordBenchmark#RECORDS_PER_INVOCATION} spans.
Expand All @@ -54,6 +57,8 @@ public class SpanRecordBenchmark {
private static final int RECORDS_PER_INVOCATION = BenchmarkUtils.RECORDS_PER_INVOCATION;
private static final int MAX_THREADS = 4;
private static final int QUEUE_SIZE = RECORDS_PER_INVOCATION * MAX_THREADS;
// Longest span type in semantic conventions.
private static final String SPAN_TYPE = "dotnet.http.request.wait_for_connection.internal";

@State(Scope.Benchmark)
public static class BenchmarkState {
Expand All @@ -62,6 +67,9 @@ public static class BenchmarkState {
// benchmark various shapes of spans without combinatorial explosion.
@Param SpanSize spanSize;

@Param({"false", "true"})
boolean withType;

SdkTracerProvider tracerProvider;
Tracer tracer;
List<AttributeKey<String>> attributeKeys;
Expand Down Expand Up @@ -144,7 +152,11 @@ public void record_MultipleThreads(BenchmarkState benchmarkState) {

private static void record(BenchmarkState benchmarkState) {
for (int i = 0; i < RECORDS_PER_INVOCATION; i++) {
Span span = benchmarkState.tracer.spanBuilder("test span name").startSpan();
SpanBuilder builder = benchmarkState.tracer.spanBuilder("test span name");
if (benchmarkState.withType) {
((ExtendedSpanBuilder) builder).setType(SPAN_TYPE);
}
Span span = builder.startSpan();
for (int j = 0; j < benchmarkState.attributeKeys.size(); j++) {
span.setAttribute(
benchmarkState.attributeKeys.get(j), benchmarkState.attributeValues.get(j));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ public ExtendedSpanBuilder setSpanKind(SpanKind spanKind) {
return this;
}

@Override
public ExtendedSpanBuilder setType(String type) {
super.setType(type);
return this;
}

@Override
public ExtendedSpanBuilder addLink(SpanContext spanContext) {
super.addLink(spanContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.internal.ExtendedReadableSpan;
import io.opentelemetry.sdk.trace.internal.ExtendedSpanProcessor;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -38,7 +39,7 @@

/** Implementation for the {@link Span} class that records trace events. */
@ThreadSafe
final class SdkSpan implements ReadWriteSpan {
final class SdkSpan implements ReadWriteSpan, ExtendedReadableSpan {

private static final Logger logger = Logger.getLogger(SdkSpan.class.getName());

Expand All @@ -54,6 +55,7 @@ final class SdkSpan implements ReadWriteSpan {
private final ExceptionAttributeResolver exceptionAttributeResolver;
// The kind of the span.
private final SpanKind kind;
@Nullable private final String type;
// The clock used to get the time.
private final AnchoredClock clock;
// The resource associated with this span.
Expand Down Expand Up @@ -127,6 +129,7 @@ private SdkSpan(
String name,
InstrumentationScopeInfo instrumentationScopeInfo,
SpanKind kind,
@Nullable String type,
SpanContext parentSpanContext,
SpanLimits spanLimits,
SpanProcessor spanProcessor,
Expand All @@ -145,6 +148,7 @@ private SdkSpan(
this.totalRecordedLinks = totalRecordedLinks;
this.name = name;
this.kind = kind;
this.type = type;
this.spanProcessor = spanProcessor;
this.exceptionAttributeResolver = exceptionAttributeResolver;
this.resource = resource;
Expand All @@ -162,6 +166,7 @@ private SdkSpan(
* @param context supplies the trace_id and span_id for the newly started span.
* @param name the displayed name for the new span.
* @param kind the span kind.
* @param type the validated span type, or {@code null} if not set.
* @param parentSpan the parent span, or {@link Span#getInvalid()} if this span is a root span.
* @param spanLimits limits applied to this span.
* @param spanProcessor handler called when the span starts and ends.
Expand All @@ -177,6 +182,7 @@ static SdkSpan startSpan(
String name,
InstrumentationScopeInfo instrumentationScopeInfo,
SpanKind kind,
@Nullable String type,
Span parentSpan,
Context parentContext,
SpanLimits spanLimits,
Expand Down Expand Up @@ -218,6 +224,7 @@ static SdkSpan startSpan(
name,
instrumentationScopeInfo,
kind,
type,
parentSpan.getSpanContext(),
spanLimits,
spanProcessor,
Expand Down Expand Up @@ -603,6 +610,12 @@ public SpanKind getKind() {
return kind;
}

@Override
@Nullable
public String getType() {
return type;
}

long getStartEpochNanos() {
return startEpochNanos;
}
Expand Down Expand Up @@ -670,6 +683,8 @@ public String toString() {
+ name
+ ", kind="
+ kind
+ ", type="
+ type
+ ", attributes="
+ attributes
+ ", status="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceId;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.common.impl.ApiUsageLogger;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.common.internal.AttributeUtil;
import io.opentelemetry.sdk.common.internal.AttributesMap;
import io.opentelemetry.sdk.common.internal.ThrottlingLogger;
import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/** {@link SdkSpanBuilder} is SDK implementation of {@link SpanBuilder}. */
class SdkSpanBuilder implements SpanBuilder {

private static final ThrottlingLogger logger =
new ThrottlingLogger(Logger.getLogger(SdkSpanBuilder.class.getName()));

private static final int MAX_TYPE_LENGTH = 255;

private static final Span RANDOM_TRACE_ID_PRIMORDIAL_SPAN =
Span.wrap(
SpanContext.create(
Expand All @@ -56,6 +65,7 @@ class SdkSpanBuilder implements SpanBuilder {

@Nullable private Context parent; // null means: Use current context.
private SpanKind spanKind = SpanKind.INTERNAL;
@Nullable private String type;
@Nullable private AttributesMap attributes;
@Nullable private List<LinkData> links;
private int totalNumberOfLinksAdded = 0;
Expand Down Expand Up @@ -96,6 +106,18 @@ public SpanBuilder setSpanKind(SpanKind spanKind) {
return this;
}

public SpanBuilder setType(String type) {
if (type == null) {
ApiUsageLogger.logNullParam(SpanBuilder.class, "setType", "type");
return this;
}
if (type.isEmpty()) {
return this;
}
this.type = type;
return this;
}

@Override
public SpanBuilder addLink(SpanContext spanContext) {
if (spanContext == null || (!spanContext.isValid() && spanContext.getTraceState().isEmpty())) {
Expand Down Expand Up @@ -219,6 +241,20 @@ public Span startSpan() {
// startSpan is called. If that happens all the links will be added in a new list.
links = null;
Attributes immutableAttributes = attributes == null ? Attributes.empty() : attributes;
String validatedType = type;
if (validatedType != null && !isValidType(validatedType)) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(
Level.WARNING,
"Span type \""
+ validatedType
+ "\" is invalid, creating span without a type. Span types must consist of "
+ MAX_TYPE_LENGTH
+ " or fewer characters including alphanumeric, _, ., -, /, and start with a"
+ " letter.");
}
validatedType = null;
}
SamplingResult samplingResult =
tracerSharedState
.getSampler()
Expand Down Expand Up @@ -266,6 +302,7 @@ public Span startSpan() {
spanName,
instrumentationScopeInfo,
spanKind,
validatedType,
parentSpan,
parentContext,
spanLimits,
Expand All @@ -291,6 +328,28 @@ private AttributesMap attributes() {
return attributes;
}

// Visible for testing
static boolean isValidType(String type) {
int length = type.length();
if (length > MAX_TYPE_LENGTH) {
return false;
}
for (int i = 0; i < length; i++) {
char c = type.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
continue;
}
if (i == 0) {
return false;
}
if ((c >= '0' && c <= '9') || c == '_' || c == '.' || c == '-' || c == '/') {
continue;
}
return false;
}
return true;
}

// Visible for testing
static boolean isRecording(SamplingDecision decision) {
return SamplingDecision.RECORD_ONLY.equals(decision)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.internal.ExtendedSpanData;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
Expand All @@ -31,7 +33,7 @@
*/
@Immutable
@AutoValue
abstract class SpanWrapper implements SpanData {
abstract class SpanWrapper implements ExtendedSpanData {
abstract SdkSpan delegate();

abstract List<LinkData> resolvedLinks();
Expand Down Expand Up @@ -121,6 +123,12 @@ public SpanKind getKind() {
return delegate().getKind();
}

@Override
@Nullable
public String getType() {
return delegate().getType();
}

@Override
public long getStartEpochNanos() {
return delegate().getStartEpochNanos();
Expand Down Expand Up @@ -192,6 +200,9 @@ public final String toString() {
+ "kind="
+ getKind()
+ ", "
+ "type="
+ getType()
+ ", "
+ "startEpochNanos="
+ getStartEpochNanos()
+ ", "
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.trace.internal;

import io.opentelemetry.sdk.trace.ReadableSpan;
import javax.annotation.Nullable;

/**
* Extended {@link ReadableSpan} with experimental APIs.
*
* <p>This class is internal and experimental. Its APIs are unstable and can change at any time. Its
* APIs (or a version of them) may be promoted to the public stable API in the future, but no
* guarantees are made.
*/
public interface ExtendedReadableSpan extends ReadableSpan {

// TODO: once span type stabilizes, move getType() to ReadableSpan as a default method

/**
* Returns the span type, or {@code null} if not set.
*
* @return the span type, or {@code null} if not set.
*/
@Nullable
String getType();
}
Loading
Loading