Skip to content

Commit 7dc7923

Browse files
mchandramouliashishagg
authored andcommitted
Add support single and dual span types (#67)
* adding support for single and dual span types * merge conflicts * renaming config to dualSpanMode instead of dualSpanType
1 parent d55d6d9 commit 7dc7923

File tree

5 files changed

+190
-34
lines changed

5 files changed

+190
-34
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ buildNumber.properties
3737
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
3838
hs_err_pid*
3939

40-
# IntelliJ Idea project files
40+
# IDE
4141
.idea/
4242
*.iml
43+
*.ipr
44+
*.iws
4345
*.classpath
4446
*.project
4547
*.settings

core/src/main/java/com/expedia/www/haystack/client/SpanContext.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,22 @@ public class SpanContext implements io.opentracing.SpanContext {
2828
private final UUID traceId;
2929
private final UUID spanId;
3030
private final UUID parentId;
31+
private boolean extractedContext;
3132

3233
public SpanContext(UUID traceId, UUID spanId, UUID parentId) {
33-
this(traceId, spanId, parentId, Collections.<String, String>emptyMap());
34+
this(traceId, spanId, parentId, false);
3435
}
3536

37+
public SpanContext(UUID traceId, UUID spanId, UUID parentId, boolean extractedContext) {
38+
this(traceId, spanId, parentId, Collections.emptyMap(), extractedContext);
39+
}
40+
41+
@Deprecated
3642
SpanContext(UUID traceId, UUID spanId, UUID parentId, Map<String, String> baggage) {
43+
this(traceId, spanId, parentId, baggage, false);
44+
}
45+
46+
SpanContext(UUID traceId, UUID spanId, UUID parentId, Map<String, String> baggage, boolean extractedContext) {
3747
if (baggage == null) {
3848
throw new NullPointerException();
3949
}
@@ -42,6 +52,7 @@ public SpanContext(UUID traceId, UUID spanId, UUID parentId) {
4252
this.spanId = spanId;
4353
this.parentId = parentId;
4454
this.baggage = Collections.unmodifiableMap(baggage);
55+
this.extractedContext = extractedContext;
4556
}
4657

4758
@Override
@@ -67,13 +78,13 @@ public String toString() {
6778
}
6879

6980
public SpanContext addBaggage(Map<String, String> newBaggage) {
70-
return new SpanContext(traceId, spanId, parentId, newBaggage);
81+
return new SpanContext(traceId, spanId, parentId, newBaggage, extractedContext);
7182
}
7283

7384
public SpanContext addBaggage(String key, String value) {
7485
Map<String, String> newBaggage = new HashMap<>(this.baggage);
7586
newBaggage.put(key, value);
76-
return new SpanContext(traceId, spanId, parentId, newBaggage);
87+
return new SpanContext(traceId, spanId, parentId, newBaggage, extractedContext);
7788
}
7889

7990
@Override
@@ -110,4 +121,8 @@ public UUID getSpanId() {
110121
public UUID getParentId() {
111122
return parentId;
112123
}
124+
125+
boolean isExtractedContext() {
126+
return extractedContext;
127+
}
113128
}

core/src/main/java/com/expedia/www/haystack/client/Tracer.java

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.opentracing.Span;
3131
import io.opentracing.propagation.Format;
3232
import io.opentracing.propagation.TextMap;
33+
import io.opentracing.tag.Tags;
3334
import io.opentracing.util.ThreadLocalScopeManager;
3435
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
3536
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -43,6 +44,7 @@ public class Tracer implements io.opentracing.Tracer {
4344
protected final PropagationRegistry registry;
4445
private final String serviceName;
4546
private final ScopeManager scopeManager;
47+
private final boolean dualSpanMode;
4648

4749
private final Counter spansCreatedCounter;
4850

@@ -60,12 +62,14 @@ public class Tracer implements io.opentracing.Tracer {
6062
private final Timer extractTimer;
6163
private final Counter extractFailureCounter;
6264

63-
public Tracer(String serviceName, ScopeManager scopeManager, Clock clock, Dispatcher dispatcher, PropagationRegistry registry, Metrics metrics) {
65+
public Tracer(String serviceName, ScopeManager scopeManager, Clock clock,
66+
Dispatcher dispatcher, PropagationRegistry registry, Metrics metrics, boolean dualSpanMode) {
6467
this.serviceName = serviceName;
6568
this.scopeManager = scopeManager;
6669
this.clock = clock;
6770
this.dispatcher = dispatcher;
6871
this.registry = registry;
72+
this.dualSpanMode = dualSpanMode;
6973

7074
this.dispatchTimer = Timer.builder("dispatch").register(metrics);
7175
this.closeTimer = Timer.builder("close").register(metrics);
@@ -250,21 +254,19 @@ public SpanBuilder withStartTimestamp(long microseconds) {
250254
return this;
251255
}
252256

253-
@Override
254-
public Scope startActive(boolean finishSpanOnClose) {
255-
return tracer.scopeManager().activate(start(), finishSpanOnClose);
257+
boolean isServerSpan() {
258+
return Tags.SPAN_KIND_SERVER.equals(tags.get(Tags.SPAN_KIND.getKey()));
256259
}
257260

258-
protected SpanContext createNewContext() {
259-
UUID randomId = UUID.randomUUID();
260-
return createContext(randomId, randomId, null, Collections.emptyMap());
261+
SpanContext createNewContext() {
262+
return createContext(UUID.randomUUID(), UUID.randomUUID(), null, Collections.emptyMap());
261263
}
262264

263-
protected SpanContext createContext(UUID traceId, UUID spanId, UUID parentId, Map<String, String> baggage) {
264-
return new SpanContext(traceId, spanId, parentId, baggage);
265+
SpanContext createContext(UUID traceId, UUID spanId, UUID parentId, Map<String, String> baggage) {
266+
return new SpanContext(traceId, spanId, parentId, baggage, false);
265267
}
266268

267-
protected SpanContext createDependentContext() {
269+
SpanContext createDependentContext() {
268270
Reference parent = references.get(0);
269271
for (Reference reference : references) {
270272
if (References.CHILD_OF.equals(reference.getReferenceType())) {
@@ -279,13 +281,25 @@ protected SpanContext createDependentContext() {
279281
baggage.putAll(reference.getContext().getBaggage());
280282
}
281283

284+
// This is a check to see if the tracer is configured to support single
285+
// single span type (Zipkin style shared span id) or
286+
// dual span type (client and server having their own span ids ).
287+
// a. If tracer is not of dualSpanType and if it is a server span then we
288+
// just return the parent context with the same shared span ids
289+
// b. If tracer is not of dualSpanType and if the parent context is an extracted one from the wire
290+
// then we assume this is the first span in the server and so just return the parent context
291+
// with the same shared span ids
292+
if (!tracer.dualSpanMode && (isServerSpan() || parent.getContext().isExtractedContext())) {
293+
return parent.getContext();
294+
}
295+
282296
return createContext(parent.getContext().getTraceId(),
283297
UUID.randomUUID(),
284298
parent.getContext().getSpanId(),
285299
baggage);
286300
}
287301

288-
protected SpanContext createContext() {
302+
SpanContext createContext() {
289303
// handle active spans if needed
290304
if (references.isEmpty() && !ignoreActive && tracer.activeSpan() != null) {
291305
asChildOf(tracer.activeSpan());
@@ -294,6 +308,7 @@ protected SpanContext createContext() {
294308
if (references.isEmpty()) {
295309
return createNewContext();
296310
}
311+
297312
return createDependentContext();
298313
}
299314

@@ -304,6 +319,11 @@ private long calculateStartTime() {
304319
return startTime;
305320
}
306321

322+
@Override
323+
public Scope startActive(boolean finishSpanOnClose) {
324+
return tracer.scopeManager().activate(start(), finishSpanOnClose);
325+
}
326+
307327
@Override
308328
@Deprecated
309329
public com.expedia.www.haystack.client.Span startManual() {
@@ -324,6 +344,7 @@ public static class Builder {
324344
protected Dispatcher dispatcher;
325345
protected PropagationRegistry registry = new PropagationRegistry();
326346
protected Metrics metrics;
347+
private boolean dualSpanMode;
327348

328349
public Builder(MetricsRegistry registry, String serviceName, Dispatcher dispatcher) {
329350
this(new Metrics(registry, Tracer.class.getName(), Collections.emptyList()), serviceName, dispatcher);
@@ -341,7 +362,6 @@ public Builder(Metrics metrics, String serviceName, Dispatcher dispatcher) {
341362
TextMapPropagator httpPropagator = new TextMapPropagator.Builder().withURLCodex().build();
342363
withFormat(Format.Builtin.HTTP_HEADERS, (Injector<TextMap>) httpPropagator);
343364
withFormat(Format.Builtin.HTTP_HEADERS, (Extractor<TextMap>) httpPropagator);
344-
345365
}
346366

347367
public Builder withScopeManager(ScopeManager scope) {
@@ -375,8 +395,18 @@ public Builder clearAllFormats() {
375395
return this;
376396
}
377397

398+
/**
399+
* Enables production of client and server spans with two different span-ids. If not enabled,
400+
* this will cause the Tracer to produce client and server spans with shared span-ids (Zipkin style)
401+
* @return this builder instance
402+
*/
403+
public Builder withDualSpanMode() {
404+
dualSpanMode = true;
405+
return this;
406+
}
407+
378408
public Tracer build() {
379-
return new Tracer(serviceName, scopeManager, clock, dispatcher, registry, metrics);
409+
return new Tracer(serviceName, scopeManager, clock, dispatcher, registry, metrics, dualSpanMode);
380410
}
381411

382412
}

core/src/main/java/com/expedia/www/haystack/client/propagation/TextMapPropagator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ public SpanContext extract(TextMap carrier) {
103103
}
104104

105105
SpanContext context = new SpanContext(UUID.fromString(traceId),
106-
UUID.fromString(spanId),
107-
parentId == null ? null : UUID.fromString(parentId));
106+
UUID.fromString(spanId),
107+
parentId == null ? null : UUID.fromString(parentId),
108+
true);
108109
return context.addBaggage(baggage);
109110
}
110111

core/src/test/java/com/expedia/www/haystack/client/SpanBuilderTest.java

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@
1616
*/
1717
package com.expedia.www.haystack.client;
1818

19-
import java.util.Map;
20-
21-
import org.junit.Assert;
22-
import org.junit.Before;
23-
import org.junit.Test;
24-
2519
import com.expedia.www.haystack.client.dispatchers.Dispatcher;
2620
import com.expedia.www.haystack.client.dispatchers.NoopDispatcher;
2721
import com.expedia.www.haystack.client.metrics.NoopMetricsRegistry;
28-
2922
import io.opentracing.References;
23+
import io.opentracing.propagation.Format;
24+
import io.opentracing.propagation.TextMap;
25+
import io.opentracing.tag.Tags;
26+
import java.util.HashMap;
27+
import java.util.Iterator;
28+
import java.util.Map;
29+
import org.junit.Assert;
30+
import org.junit.Before;
31+
import org.junit.Test;
3032

3133
public class SpanBuilderTest {
3234

@@ -53,23 +55,112 @@ public void testReferences() {
5355
Span following = tracer.buildSpan("following").start();
5456

5557
Span child = tracer.buildSpan("child")
56-
.asChildOf(parent)
57-
.addReference(References.FOLLOWS_FROM, following.context())
58-
.start();
59-
58+
.asChildOf(parent)
59+
.addReference(References.FOLLOWS_FROM, following.context())
60+
.start();
6061

6162
Assert.assertEquals(2, child.getReferences().size());
6263
Assert.assertEquals(child.getReferences().get(0), new Reference(References.CHILD_OF, parent.context()));
6364
Assert.assertEquals(child.getReferences().get(1), new Reference(References.FOLLOWS_FROM, following.context()));
6465
}
6566

67+
@Test
68+
public void testChildOfWithDualSpanType() {
69+
//create a client span
70+
final Tracer clientTracer = new Tracer.Builder(new NoopMetricsRegistry(),
71+
"ClientService",
72+
dispatcher).withDualSpanMode().build();
73+
final Span clientSpan = clientTracer.buildSpan("Api_call")
74+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
75+
.start();
76+
final MapBackedTextMap wireData = new MapBackedTextMap();
77+
clientTracer.inject(clientSpan.context(), Format.Builtin.TEXT_MAP, wireData);
78+
79+
//create a server
80+
final Tracer serverTracer = new Tracer.Builder(new NoopMetricsRegistry(),
81+
"ServerService",
82+
dispatcher).withDualSpanMode().build();
83+
final SpanContext wireContext = serverTracer.extract(Format.Builtin.TEXT_MAP, wireData);
84+
final Span serverSpan = serverTracer.buildSpan("Api")
85+
.asChildOf(wireContext)
86+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
87+
.start();
88+
89+
Assert.assertEquals("trace-ids are not matching",
90+
clientSpan.context().getTraceId().toString(),
91+
serverSpan.context().getTraceId().toString());
92+
Assert.assertEquals("server's parent id - client's span id do not match",
93+
clientSpan.context().getSpanId().toString(),
94+
serverSpan.context().getParentId().toString());
95+
}
96+
97+
@Test
98+
public void testChildOfWithSingleSpanType() {
99+
//create a client span
100+
final Tracer clientTracer = new Tracer.Builder(new NoopMetricsRegistry(),
101+
"ClientService",
102+
dispatcher).build();
103+
final Span clientSpan = clientTracer.buildSpan("Api_call")
104+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
105+
.start();
106+
final MapBackedTextMap wireData = new MapBackedTextMap();
107+
clientTracer.inject(clientSpan.context(), Format.Builtin.TEXT_MAP, wireData);
108+
109+
//create a server
110+
final Tracer serverTracer = new Tracer.Builder(new NoopMetricsRegistry(),
111+
"ServerService",
112+
dispatcher).build();
113+
final SpanContext wireContext = serverTracer.extract(Format.Builtin.TEXT_MAP, wireData);
114+
final Span serverSpan = serverTracer.buildSpan("Api")
115+
.asChildOf(wireContext)
116+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
117+
.start();
118+
119+
Assert.assertEquals("trace-ids are not matching",
120+
clientSpan.context().getTraceId().toString(),
121+
serverSpan.context().getTraceId().toString());
122+
Assert.assertEquals("server - client spans do not match",
123+
clientSpan.context().getSpanId().toString(),
124+
serverSpan.context().getSpanId().toString());
125+
}
126+
127+
128+
@Test
129+
public void testChildOfWithSingleSpanTypeAndExtractedContext() {
130+
//create a client span
131+
final Tracer clientTracer = new Tracer.Builder(new NoopMetricsRegistry(),
132+
"ClientService",
133+
dispatcher).build();
134+
final Span clientSpan = clientTracer.buildSpan("Api_call")
135+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
136+
.start();
137+
final MapBackedTextMap wireData = new MapBackedTextMap();
138+
clientTracer.inject(clientSpan.context(), Format.Builtin.TEXT_MAP, wireData);
139+
140+
//create a server
141+
final Tracer serverTracer = new Tracer.Builder(new NoopMetricsRegistry(),
142+
"ServerService",
143+
dispatcher).build();
144+
final SpanContext wireContext = serverTracer.extract(Format.Builtin.TEXT_MAP, wireData);
145+
final Span serverSpan = serverTracer.buildSpan("Api")
146+
.asChildOf(wireContext)
147+
.start();
148+
149+
Assert.assertEquals("trace-ids are not matching",
150+
clientSpan.context().getTraceId().toString(),
151+
serverSpan.context().getTraceId().toString());
152+
Assert.assertEquals("server - client spans do not match",
153+
clientSpan.context().getSpanId().toString(),
154+
serverSpan.context().getSpanId().toString());
155+
}
156+
66157
@Test
67158
public void testWithTags() {
68159
Span child = tracer.buildSpan("child")
69-
.withTag("string-key", "string-value")
70-
.withTag("boolean-key", false)
71-
.withTag("number-key", 1l)
72-
.start();
160+
.withTag("string-key", "string-value")
161+
.withTag("boolean-key", false)
162+
.withTag("number-key", 1l)
163+
.start();
73164

74165
Map<String, ?> tags = child.getTags();
75166

@@ -82,4 +173,21 @@ public void testWithTags() {
82173
Assert.assertEquals(1l, tags.get("number-key"));
83174
}
84175

176+
private class MapBackedTextMap implements TextMap {
177+
private final Map<String, String> map = new HashMap<>();
178+
179+
@Override
180+
public Iterator<Map.Entry<String, String>> iterator() {
181+
return map.entrySet().iterator();
182+
}
183+
184+
@Override
185+
public void put(String key, String value) {
186+
map.put(key, value);
187+
}
188+
189+
public Map<String, String> getMap() {
190+
return map;
191+
}
192+
}
85193
}

0 commit comments

Comments
 (0)