Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement spec change to only accept Context as span parent. #1611

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
31 changes: 10 additions & 21 deletions api/src/main/java/io/opentelemetry/trace/DefaultTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,43 +67,32 @@ static NoopSpanBuilder create(String spanName) {
}

private boolean isRootSpan;
@Nullable private SpanContext spanContext;
@Nullable private Context parent;

@Override
public Span startSpan() {
if (spanContext == null && !isRootSpan) {
spanContext = TracingContextUtils.getCurrentSpan().getContext();
if (parent == null) {
parent = Context.current();
}
if (isRootSpan) {
parent = TracingContextUtils.withSpan(DefaultSpan.getInvalid(), parent);
}

return spanContext != null && !SpanContext.getInvalid().equals(spanContext)
? new DefaultSpan(spanContext)
: DefaultSpan.getInvalid();
}

@Override
public NoopSpanBuilder setParent(Span parent) {
Utils.checkNotNull(parent, "parent");
spanContext = parent.getContext();
return this;
}

@Override
public NoopSpanBuilder setParent(SpanContext remoteParent) {
Utils.checkNotNull(remoteParent, "remoteParent");
spanContext = remoteParent;
return this;
return new DefaultSpan(TracingContextUtils.getSpan(parent).getContext());
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public NoopSpanBuilder setParent(Context context) {
Utils.checkNotNull(context, "context");
spanContext = TracingContextUtils.getSpan(context).getContext();
isRootSpan = false; // TODO port this fix to mainline
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
parent = context;
return this;
}

@Override
public NoopSpanBuilder setNoParent() {
isRootSpan = true;
parent = null;
return this;
}

Expand Down
45 changes: 0 additions & 45 deletions api/src/main/java/io/opentelemetry/trace/Span.java
Original file line number Diff line number Diff line change
Expand Up @@ -383,51 +383,6 @@ enum Kind {
*/
interface Builder {

/**
* Sets the parent {@code Span} to use. If not set, the value of {@code Tracer.getCurrentSpan()}
* at {@link #startSpan()} time will be used as parent.
*
* <p>This <b>must</b> be used to create a {@code Span} when manual Context propagation is used
* OR when creating a root {@code Span} with a parent with an invalid {@link SpanContext}.
*
* <p>Observe this is the preferred method when the parent is a {@code Span} created within the
* process. Using its {@code SpanContext} as parent remains as a valid, albeit inefficient,
* operation.
*
* <p>If called multiple times, only the last specified value will be used. Observe that the
* state defined by a previous call to {@link #setNoParent()} will be discarded.
*
* @param parent the {@code Span} used as parent.
* @return this.
* @throws NullPointerException if {@code parent} is {@code null}.
* @see #setNoParent()
* @since 0.1.0
*/
Builder setParent(Span parent);

/**
* Sets the parent {@link SpanContext} to use. If not set, the value of {@code
* Tracer.getCurrentSpan()} at {@link #startSpan()} time will be used as parent.
*
* <p>Similar to {@link #setParent(Span parent)} but this <b>must</b> be used to create a {@code
* Span} when the parent is in a different process. This is only intended for use by RPC systems
* or similar.
*
* <p>If no {@link SpanContext} is available, users must call {@link #setNoParent()} in order to
* create a root {@code Span} for a new trace.
*
* <p>If called multiple times, only the last specified value will be used. Observe that the
* state defined by a previous call to {@link #setNoParent()} will be discarded.
*
* @param remoteParent the {@link SpanContext} used as parent.
* @return this.
* @throws NullPointerException if {@code remoteParent} is {@code null}.
* @see #setParent(Span parent)
* @see #setNoParent()
* @since 0.1.0
*/
Builder setParent(SpanContext remoteParent);

/**
* Sets the parent to use from the specified {@code Context}. If not set, the value of {@code
* Tracer.getCurrentSpan()} at {@link #startSpan()} time will be used as parent.
Expand Down
12 changes: 10 additions & 2 deletions api/src/test/java/io/opentelemetry/trace/DefaultTracerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,23 @@ void testInProcessContext() {

@Test
void testSpanContextPropagationExplicitParent() {
Span span = defaultTracer.spanBuilder(SPAN_NAME).setParent(spanContext).startSpan();
Span span =
defaultTracer
.spanBuilder(SPAN_NAME)
.setParent(TracingContextUtils.withSpan(DefaultSpan.create(spanContext), Context.ROOT))
.startSpan();
assertThat(span.getContext()).isSameAs(spanContext);
}

@Test
void testSpanContextPropagation() {
DefaultSpan parent = new DefaultSpan(spanContext);

Span span = defaultTracer.spanBuilder(SPAN_NAME).setParent(parent).startSpan();
Span span =
defaultTracer
.spanBuilder(SPAN_NAME)
.setParent(TracingContextUtils.withSpan(parent, Context.ROOT))
.startSpan();
assertThat(span.getContext()).isSameAs(spanContext);
}

Expand Down
15 changes: 5 additions & 10 deletions api/src/test/java/io/opentelemetry/trace/SpanBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import io.grpc.Context;
import io.opentelemetry.common.AttributeValue;
import io.opentelemetry.common.Attributes;
import io.opentelemetry.trace.Span.Kind;
Expand All @@ -32,8 +33,8 @@ class SpanBuilderTest {
void doNotCrash_NoopImplementation() {
Span.Builder spanBuilder = tracer.spanBuilder("MySpanName");
spanBuilder.setSpanKind(Kind.SERVER);
spanBuilder.setParent(DefaultSpan.getInvalid());
spanBuilder.setParent(DefaultSpan.getInvalid().getContext());
spanBuilder.setParent(TracingContextUtils.withSpan(DefaultSpan.create(null), Context.ROOT));
Copy link
Contributor

Choose a reason for hiding this comment

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

youch. this API sure is ugly! Remind me again how this is going to make things easier/better for our users?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a unit test. Normally you would just call setNoParent.

Copy link
Contributor

Choose a reason for hiding this comment

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

fair enough, but I think having to do parenting via the TCU is going to be pretty counter-intuitive to most users. :(

Copy link
Member Author

Choose a reason for hiding this comment

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

I fully agree, but at the same time, I don't think users should normally do that. Usually you would just use the implicit current parent, or sometimes you need to capture a context with Context.current() and forward that to somewhere else. If you start a span at one thread and need to continue it at another thread while not setting it as active in the current thread is the only place you would need TracingContextUtils. I also think there should be some may to make that more easy. I could imagine span.inCurrentContext() (Java 8 default methods?), or TracingContextUtils.spanInCurrent(span) to make this easier.

Remind me again how this is going to make things easier/better for our users?

As you said, using TracingContextUtils is rather unintuitive, and thus I think most users did not do it, they just passed around spans without the remaining context. However, a custom propagator should be able to set a value in the context in extract and rely on it still being available in inject.

BTW, using inject is a place where you already have to often muck around with TracingContextUtils currently.

Copy link
Member

Choose a reason for hiding this comment

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

Is it safe to assume the implicit current parent is accurate?

For instance, when dealing with asynchronous methods the thread being executed on is usually not the same thread that might have initiated/received the original request. In that situation, the implicit current parent from ThreadLocal is more than likely not the parent you want

Copy link
Member Author

@Oberon00 Oberon00 Sep 24, 2020

Choose a reason for hiding this comment

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

Maybe we can provide some convenience function (default interface implementation?), so one can do Context capturedParent = span.withCurrentContext(). But it would be interesting how common the cases where you would need this even are.

Copy link
Member

Choose a reason for hiding this comment

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

Personally, it's a lot messier, but more importantly way less obvious that that's what is needed to achieve the desired result.

With respect to a default interface implementation, that wouldn't be possible with the JDK 7 requirement, correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

(FWIW, we need more convenience functions in TracingContextUtils, or in whatever new name it ends up having ;) )

Copy link
Member Author

Choose a reason for hiding this comment

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

@kenfinnigan Java 7 was dropped recently, Java 8 is now required.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @Oberon00, don't follow things for a few weeks, and everything changes!

spanBuilder.setParent(Context.ROOT);
spanBuilder.setNoParent();
spanBuilder.addLink(DefaultSpan.getInvalid().getContext());
spanBuilder.addLink(DefaultSpan.getInvalid().getContext(), Attributes.empty());
Expand Down Expand Up @@ -61,15 +62,9 @@ public Attributes getAttributes() {
}

@Test
void setParent_NullSpan() {
void setParent_NullContext() {
Span.Builder spanBuilder = tracer.spanBuilder("MySpanName");
assertThrows(NullPointerException.class, () -> spanBuilder.setParent((Span) null));
}

@Test
void setParent_NullSpanContext() {
Span.Builder spanBuilder = tracer.spanBuilder("MySpanName");
assertThrows(NullPointerException.class, () -> spanBuilder.setParent((SpanContext) null));
assertThrows(NullPointerException.class, () -> spanBuilder.setParent(null));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ private static SpanData getSpanData(long startMs, long endMs) {
.setHasEnded(true)
.setTraceId(TraceId.fromLowerBase16(TRACE_ID, 0))
.setSpanId(SpanId.fromLowerBase16(SPAN_ID, 0))
.setParentSpanId(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0))
.setParent(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0), false)
.setName("GET /api/endpoint")
.setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs))
.setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void toProtoSpan() {
.setHasEnded(true)
.setTraceId(TRACE_ID)
.setSpanId(SPAN_ID)
.setParentSpanId(SpanId.getInvalid())
.setParent(SpanId.getInvalid(), false)
.setName("GET /api/endpoint")
.setKind(Kind.SERVER)
.setStartEpochNanos(12345)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,10 @@ private static TestSpanData.Builder buildStandardSpan() {
return TestSpanData.newBuilder()
.setTraceId(TraceId.fromLowerBase16(TRACE_ID, 0))
.setSpanId(SpanId.fromLowerBase16(SPAN_ID, 0))
.setParentSpanId(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0))
.setParent(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0), false)
.setTraceFlags(TraceFlags.builder().setIsSampled(true).build())
.setStatus(Status.OK)
.setKind(Kind.SERVER)
.setHasRemoteParent(true)
.setName(SPAN_NAME)
.setStartEpochNanos(START_EPOCH_NANOS)
.setAttributes(attributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,10 @@ private static TestSpanData.Builder buildStandardSpan() {
return TestSpanData.newBuilder()
.setTraceId(TraceId.fromLowerBase16(TRACE_ID, 0))
.setSpanId(SpanId.fromLowerBase16(SPAN_ID, 0))
.setParentSpanId(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0))
.setParent(SpanId.fromLowerBase16(PARENT_SPAN_ID, 0), true)
.setTraceFlags(TraceFlags.builder().setIsSampled(true).build())
.setStatus(Status.OK)
.setKind(Kind.SERVER)
.setHasRemoteParent(true)
.setName("Recv.helloworld.Greeter.SayHello")
.setStartEpochNanos(1505855794_194009601L)
.setAttributes(attributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package io.opentelemetry.opentracingshim;

import io.grpc.Context;
import io.opentelemetry.common.AttributeValue;
import io.opentelemetry.correlationcontext.CorrelationContext;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Status;
import io.opentelemetry.trace.TracingContextUtils;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer.SpanBuilder;
Expand Down Expand Up @@ -183,11 +186,15 @@ public Span start() {
if (ignoreActiveSpan && parentSpan == null && parentSpanContext == null) {
builder.setNoParent();
} else if (parentSpan != null) {
builder.setParent(parentSpan.getSpan());
// Note: We ignore the (potentially stored) parentSpan's (grand)parent context here.
builder.setParent(TracingContextUtils.withSpan(parentSpan.getSpan(), Context.current()));
SpanContextShim contextShim = spanContextTable().get(parentSpan);
distContext = contextShim == null ? null : contextShim.getCorrelationContext();
} else if (parentSpanContext != null) {
builder.setParent(parentSpanContext.getSpanContext());
// TODO: This might be wonky
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
builder.setParent(
TracingContextUtils.withSpan(
DefaultSpan.create(parentSpanContext.getSpanContext()), Context.current()));
distContext = parentSpanContext.getCorrelationContext();
}

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

import com.google.common.collect.EvictingQueue;
import io.grpc.Context;
import io.opentelemetry.common.AttributeValue;
import io.opentelemetry.common.Attributes;
import io.opentelemetry.common.ReadableAttributes;
Expand All @@ -36,6 +37,7 @@
import io.opentelemetry.trace.SpanId;
import io.opentelemetry.trace.Status;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.TracingContextUtils;
import io.opentelemetry.trace.attributes.SemanticAttributes;
import java.io.PrintWriter;
import java.io.StringWriter;
Expand All @@ -59,10 +61,8 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
private final TraceConfig traceConfig;
// Contains the identifiers associated with this Span.
private final SpanContext context;
// The parent SpanId of this span. Invalid if this is a root span.
private final SpanId parentSpanId;
// True if the parent is on a different process.
private final boolean hasRemoteParent;
// Parent context.
private final Context parent;
// Handler called when the span starts and ends.
private final SpanProcessor spanProcessor;
// The displayed name of the span.
Expand Down Expand Up @@ -112,8 +112,7 @@ private RecordEventsReadableSpan(
String name,
InstrumentationLibraryInfo instrumentationLibraryInfo,
Kind kind,
SpanId parentSpanId,
boolean hasRemoteParent,
Context parent,
TraceConfig traceConfig,
SpanProcessor spanProcessor,
Clock clock,
Expand All @@ -124,8 +123,7 @@ private RecordEventsReadableSpan(
long startEpochNanos) {
this.context = context;
this.instrumentationLibraryInfo = instrumentationLibraryInfo;
this.parentSpanId = parentSpanId;
this.hasRemoteParent = hasRemoteParent;
this.parent = parent;
this.links = links;
this.totalRecordedLinks = totalRecordedLinks;
this.name = name;
Expand All @@ -146,10 +144,7 @@ private RecordEventsReadableSpan(
* @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 parentSpanId the span_id of the parent span, or {@code Span.INVALID} if the new span is
* a root span.
* @param hasRemoteParent {@code true} if the parentContext is remote. {@code false} if this is a
* root span.
* @param parent parent Context.
* @param traceConfig trace parameters like sampler and probability.
* @param spanProcessor handler called when the span starts and ends.
* @param clock the clock used to get the time.
Expand All @@ -163,8 +158,7 @@ static RecordEventsReadableSpan startSpan(
String name,
InstrumentationLibraryInfo instrumentationLibraryInfo,
Kind kind,
SpanId parentSpanId,
boolean hasRemoteParent,
Context parent,
TraceConfig traceConfig,
SpanProcessor spanProcessor,
Clock clock,
Expand All @@ -179,8 +173,7 @@ static RecordEventsReadableSpan startSpan(
name,
instrumentationLibraryInfo,
kind,
parentSpanId,
hasRemoteParent,
parent,
traceConfig,
spanProcessor,
clock,
Expand Down Expand Up @@ -508,7 +501,7 @@ private Status getStatusWithDefault() {
}

SpanId getParentSpanId() {
return parentSpanId;
return TracingContextUtils.getSpan(parent).getContext().getSpanId();
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
}

Resource getResource() {
Expand All @@ -524,13 +517,17 @@ long getStartEpochNanos() {
}

boolean hasRemoteParent() {
return hasRemoteParent;
return TracingContextUtils.getSpan(parent).getContext().isRemote();
}

int getTotalRecordedLinks() {
return totalRecordedLinks;
}

Context getParent() {
return parent;
}

@GuardedBy("lock")
private List<Link> getImmutableLinks() {
if (links.isEmpty()) {
Expand Down Expand Up @@ -613,7 +610,7 @@ public String toString() {
sb.append(", spanId=");
sb.append(context.getSpanId());
sb.append(", parentSpanId=");
sb.append(parentSpanId);
sb.append(getParentSpanId());
sb.append(", name=");
sb.append(name);
sb.append(", kind=");
Expand Down
Loading