Skip to content

Commit

Permalink
in-process propagation resolves opentracing/specification#23
Browse files Browse the repository at this point in the history
  • Loading branch information
pavolloffay committed Mar 29, 2017
1 parent bc1983e commit ff879fb
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 18 deletions.
13 changes: 10 additions & 3 deletions opentracing-api/src/main/java/io/opentracing/Span.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2016 The OpenTracing Authors
* Copyright 2016-2017 The OpenTracing Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand All @@ -22,16 +22,21 @@
* <p>Spans are created by the {@link Tracer#buildSpan} interface.
*/
public interface Span extends Closeable {

SpanManager.Visibility visibility();

/**
* Retrieve the associated SpanContext.
*
* This may be called at any time, including after calls to finish().
* This may be called at any time, including after calls to markAsFinished().
*
* @return the SpanContext that encapsulates Span state that should propagate across process boundaries.
*/
SpanContext context();

/**
* Deactivates spans from spanManager.
*
* Sets the end timestamp to now and records the span.
*
* <p>With the exception of calls to Span.context(), this should be the last call made to the span instance, and to
Expand All @@ -42,12 +47,14 @@ public interface Span extends Closeable {
void finish();

/**
* Deactivates spans from spanManager.
*
* Sets an explicit end timestamp and records the span.
*
* <p>With the exception of calls to Span.context(), this should be the last call made to the span instance, and to
* do otherwise leads to undefined behavior.
*
* @param finishMicros an explicit finish time, in microseconds since the epoch
* @param finishMicros an explicit markAsFinished time, in microseconds since the epoch
*
* @see Span#context()
*/
Expand Down
41 changes: 41 additions & 0 deletions opentracing-api/src/main/java/io/opentracing/SpanManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright 2016-2017 The OpenTracing Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.opentracing;

/**
* @author Pavol Loffay
*/
public interface SpanManager {

/**
* @param span span to bundle into visibility
* @return visibility
*/
Visibility bundle(Span span);

/**
* @return not finished active span or null
*/
Visibility active();

interface Visibility {
void activate();
void deactivate();

Span span();
SpanContext context();
// should be called by span#finish();
void markAsFinished();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright 2016-2017 The OpenTracing Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.opentracing;

/**
* @author Pavol Loffay
*/
public class ThreadLocalSpanManager implements SpanManager {

private final ThreadLocal<ThreadLocalVisibility> activeContext = new ThreadLocal<ThreadLocalVisibility>();

public ThreadLocalSpanManager() {}

@Override
public Visibility bundle(Span span) {
return span.visibility() == null ? new ThreadLocalVisibility(span) : span.visibility();
}

@Override
public Visibility active() {
return activeContext.get() == null ? null : activeContext.get();
}

class ThreadLocalVisibility implements Visibility {
private final Span span;
private ThreadLocalVisibility previous;
private boolean finished;

public ThreadLocalVisibility(Span span) {
this.span = span;
}

@Override
public Span span() {
return finished ? null : span;
}

@Override
public SpanContext context() {
return span.context();
}

@Override
public void activate() {
previous = activeContext.get();
activeContext.set(this);
}

@Override
public void deactivate() {
if (this != activeContext.get()) {
// should not happen
return;
}
activeContext.set(previous);
}

@Override
public void markAsFinished() {
this.finished = true;
}
}
}
6 changes: 6 additions & 0 deletions opentracing-api/src/main/java/io/opentracing/Tracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
*/
public interface Tracer {

// span could be directly returned
SpanManager spanManager();

/**
* Return a new SpanBuilder for a Span with the given `operationName`.
*
Expand Down Expand Up @@ -113,6 +116,8 @@ interface SpanBuilder extends SpanContext {
*/
SpanBuilder addReference(String referenceType, SpanContext referencedContext);

SpanBuilder asRoot();

/** Same as {@link Span#setTag(String, String)}, but for the span being built. */
SpanBuilder withTag(String key, String value);

Expand All @@ -128,5 +133,6 @@ interface SpanBuilder extends SpanContext {
/** Returns the started Span. */
Span start();

Span startAndActivate();
}
}
2 changes: 1 addition & 1 deletion opentracing-api/src/main/java/io/opentracing/tag/Tags.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
package io.opentracing.tag;

/**
* The following span tags are recommended for instrumentors who are trying to capture more
* The following span tags are recommended for instrumentors who are trying to bundle more
* semantic information about the spans. Tracers may expose additional features based on these
* standardized data points. Tag names follow a general structure of namespacing.
*
Expand Down
22 changes: 19 additions & 3 deletions opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.SpanManager;

/**
* MockSpans are created via MockTracer.buildSpan(...), but they are also returned via calls to
Expand All @@ -29,6 +30,8 @@ public final class MockSpan implements Span {
// A simple-as-possible (consecutive for repeatability) id generator.
private static AtomicLong nextId = new AtomicLong(0);

private final SpanManager.Visibility visibility;

private final MockTracer mockTracer;
private MockContext context;
private final long parentId; // 0 if there's no parent.
Expand Down Expand Up @@ -66,10 +69,10 @@ public long startMicros() {
return startMicros;
}
/**
* @return the finish time of the Span; only valid after a call to finish().
* @return the markAsFinished time of the Span; only valid after a call to markAsFinished().
*/
public long finishMicros() {
assert finishMicros > 0 : "must call finish() before finishMicros()";
assert finishMicros > 0 : "must call markAsFinished() before finishMicros()";
return finishMicros;
}

Expand All @@ -93,6 +96,11 @@ public List<RuntimeException> generatedErrors() {
return new ArrayList<>(errors);
}

@Override
public SpanManager.Visibility visibility() {
return visibility;
}

@Override
public synchronized MockContext context() {
return this.context;
Expand All @@ -107,6 +115,7 @@ public void finish() {
public synchronized void finish(long finishMicros) {
finishedCheck("Finishing already finished span");
this.finishMicros = finishMicros;
visibility.markAsFinished();
this.mockTracer.appendFinishedSpan(this);
this.finished = true;
}
Expand Down Expand Up @@ -248,10 +257,17 @@ public long timestampMicros() {
}
}

MockSpan(MockTracer tracer, String operationName, long startMicros, Map<String, Object> initialTags, MockContext parent) {
MockSpan(MockTracer tracer, String operationName, long startMicros, Map<String, Object> initialTags, MockContext
parent, boolean activate) {
this.mockTracer = tracer;
this.operationName = operationName;
this.startMicros = startMicros;

this.visibility = tracer.spanManager().bundle(this);
if (activate) {
this.visibility.activate();
}

if (initialTags == null) {
this.tags = new HashMap<>();
} else {
Expand Down
49 changes: 42 additions & 7 deletions opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import io.opentracing.References;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.SpanManager;
import io.opentracing.ThreadLocalSpanManager;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
Expand All @@ -38,30 +40,36 @@
public class MockTracer implements Tracer {
private List<MockSpan> finishedSpans = new ArrayList<>();
private final Propagator propagator;
private final SpanManager spanManager;

public MockTracer() {
this(Propagator.PRINTER);
this(Propagator.PRINTER, new ThreadLocalSpanManager());
}

/**
* Create a new MockTracer that passes through any calls to inject() and/or extract().
*/
public MockTracer(Propagator propagator) {
this(propagator, new ThreadLocalSpanManager());
}

public MockTracer(Propagator propagator, SpanManager spanManager) {
this.propagator = propagator;
this.spanManager = spanManager;
}

/**
* Clear the finishedSpans() queue.
*
* Note that this does *not* have any effect on Spans created by MockTracer that have not finish()ed yet; those
* will still be enqueued in finishedSpans() when they finish().
* Note that this does *not* have any effect on Spans created by MockTracer that have not markAsFinished()ed yet; those
* will still be enqueued in finishedSpans() when they markAsFinished().
*/
public synchronized void reset() {
this.finishedSpans.clear();
}

/**
* @return a copy of all finish()ed MockSpans started by this MockTracer (since construction or the last call to
* @return a copy of all markAsFinished()ed MockSpans started by this MockTracer (since construction or the last call to
* MockTracer.reset()).
*
* @see MockTracer#reset()
Expand Down Expand Up @@ -144,9 +152,14 @@ public <C> MockSpan.MockContext extract(Format<C> format, C carrier) {
};
}

@Override
public SpanManager spanManager() {
return spanManager;
}

@Override
public SpanBuilder buildSpan(String operationName) {
return new SpanBuilder(operationName);
return new SpanBuilder(operationName, spanManager);
}

@Override
Expand All @@ -170,8 +183,14 @@ public final class SpanBuilder implements Tracer.SpanBuilder {
private MockSpan.MockContext firstParent;
private Map<String, Object> initialTags = new HashMap<>();

SpanBuilder(String operationName) {
SpanBuilder(String operationName, SpanManager spanManager) {
this.operationName = operationName;

SpanManager.Visibility inferredParent = spanManager.active();
if (inferredParent != null) {
addReference(inferredParent.span() == null ? References.FOLLOWS_FROM : References.CHILD_OF,
inferredParent.context());
}
}
@Override
public SpanBuilder asChildOf(SpanContext parent) {
Expand All @@ -192,6 +211,12 @@ public SpanBuilder addReference(String referenceType, SpanContext referencedCont
return this;
}

@Override
public Tracer.SpanBuilder asRoot() {
firstParent = null;
return this;
}

@Override
public SpanBuilder withTag(String key, String value) {
this.initialTags.put(key, value);
Expand Down Expand Up @@ -221,7 +246,17 @@ public MockSpan start() {
if (this.startMicros == 0) {
this.startMicros = MockSpan.nowMicros();
}
return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags, this.firstParent);
return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags,
this.firstParent, false);
}

@Override
public MockSpan startAndActivate() {
if (this.startMicros == 0) {
this.startMicros = MockSpan.nowMicros();
}
return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags,
this.firstParent, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
public class MockTracerTest {
@Test
public void testRootSpan() {
// Create and finish a root Span.
// Create and markAsFinished a root Span.
MockTracer tracer = new MockTracer();
{
Span span = tracer.buildSpan("tester").withStartTimestamp(1000).start();
Expand Down Expand Up @@ -87,7 +87,7 @@ public void testRootSpan() {

@Test
public void testChildSpan() {
// Create and finish a root Span.
// Create and markAsFinished a root Span.
MockTracer tracer = new MockTracer();
{
Span parent = tracer.buildSpan("parent").withStartTimestamp(1000).start();
Expand Down
Loading

0 comments on commit ff879fb

Please sign in to comment.