Skip to content

Commit da379fc

Browse files
authored
Translate Spring 6 HTTP spans to Stackdriver convention (#229)
From Spring Cloud Sleuth to Spring Framework 6, the tags on HTTP spans changed. This updates the SpanTranslator to handle the Spring 6 tags.
1 parent 575e443 commit da379fc

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

encoder-stackdriver-brave/src/main/java/zipkin2/reporter/stackdriver/brave/SpanTranslator.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
import com.google.devtools.cloudtrace.v2.Span.TimeEvent;
1111
import com.google.devtools.cloudtrace.v2.Span.TimeEvents;
1212
import com.google.protobuf.Timestamp;
13+
14+
import java.util.Collections;
1315
import java.util.LinkedHashMap;
1416
import java.util.Map;
1517
import java.util.logging.Logger;
1618

1719
import static java.util.logging.Level.FINE;
20+
import static zipkin2.reporter.stackdriver.brave.AttributesExtractor.toAttributeValue;
1821
import static zipkin2.reporter.stackdriver.brave.SpanUtil.toTruncatableString;
1922

2023
/** SpanTranslator converts a Zipkin Span to a Stackdriver Trace Span. */
@@ -33,6 +36,15 @@ final class SpanTranslator {
3336
RENAMED_LABELS.put("http.url", "/http/url");
3437
}
3538

39+
private static final Map<String, String> SPRING6_RENAMED_HTTP_LABELS;
40+
41+
static {
42+
Map<String, String> map = new LinkedHashMap<>();
43+
map.put("status", "/http/status_code");
44+
map.put("method", "/http/method");
45+
SPRING6_RENAMED_HTTP_LABELS = Collections.unmodifiableMap(map);
46+
}
47+
3648
private final AttributesExtractor attributesExtractor;
3749

3850
SpanTranslator(Tag<Throwable> errorTag) {
@@ -79,6 +91,16 @@ Span.Builder translate(Span.Builder spanBuilder, MutableSpan braveSpan) {
7991
}
8092
spanBuilder.setAttributes(attributesExtractor.extract(braveSpan));
8193

94+
// Spring 6 HTTP spans need mapping to Stackdriver conventional attribute names
95+
if (braveSpan.name() != null && braveSpan.name().contains("http")) {
96+
braveSpan.tags().forEach((key, value) -> {
97+
if (SPRING6_RENAMED_HTTP_LABELS.containsKey(key)) {
98+
spanBuilder.getAttributesBuilder()
99+
.putAttributeMap(SPRING6_RENAMED_HTTP_LABELS.get(key), toAttributeValue(value));
100+
}
101+
});
102+
}
103+
82104
if (braveSpan.annotationCount() > 0) {
83105
TimeEvents.Builder events = TimeEvents.newBuilder();
84106
braveSpan.forEachAnnotation(SpanTranslator::addAnnotation, events);

encoder-stackdriver-brave/src/test/java/zipkin2/reporter/stackdriver/brave/SpanTranslatorTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static zipkin2.reporter.stackdriver.brave.AttributesExtractor.toAttributeValue;
1616
import static zipkin2.reporter.stackdriver.brave.SpanUtil.toTruncatableString;
1717
import static zipkin2.reporter.stackdriver.brave.TestObjects.clientSpan;
18+
import static zipkin2.reporter.stackdriver.brave.TestObjects.spring6ServerSpan;
1819

1920
class SpanTranslatorTest {
2021
SpanTranslator spanTranslator = new SpanTranslator(Tags.ERROR);
@@ -73,4 +74,48 @@ class SpanTranslatorTest {
7374

7475
assertThat(translated.getDisplayName().getValue()).isEqualTo("unknown");
7576
}
77+
78+
@Test void translate_spring6ServerSpan() {
79+
MutableSpan braveSpan = spring6ServerSpan();
80+
Span translated = spanTranslator.translate(Span.newBuilder(), braveSpan).build();
81+
82+
assertThat(translated)
83+
.isEqualTo(
84+
Span.newBuilder()
85+
.setSpanId(braveSpan.id())
86+
.setDisplayName(toTruncatableString("http get /test"))
87+
.setStartTime(Timestamp.newBuilder().setSeconds(1472470996).setNanos(199_000_000).build())
88+
.setEndTime(Timestamp.newBuilder().setSeconds(1472470996).setNanos(406_000_000).build())
89+
.setAttributes(Span.Attributes.newBuilder()
90+
.putAttributeMap("/agent", toAttributeValue("zipkin-java"))
91+
.putAttributeMap("exception", toAttributeValue("none"))
92+
.putAttributeMap("/http/url", toAttributeValue("/test"))
93+
.putAttributeMap("/http/method", toAttributeValue("GET"))
94+
.putAttributeMap("outcome", toAttributeValue("SUCCESS"))
95+
.putAttributeMap("/http/status_code", toAttributeValue("200"))
96+
.putAttributeMap("uri", toAttributeValue("/test"))
97+
.putAttributeMap("method", toAttributeValue("GET"))
98+
.putAttributeMap("status", toAttributeValue("200"))
99+
.putAttributeMap("/kind", toAttributeValue("server"))
100+
.putAttributeMap("/component", toAttributeValue("backend"))
101+
.putAttributeMap("endpoint.ipv4", toAttributeValue("127.0.0.1"))
102+
.build())
103+
.setTimeEvents(Span.TimeEvents.newBuilder()
104+
.addTimeEvent(Span.TimeEvent.newBuilder()
105+
.setTime(Timestamp.newBuilder().setSeconds(1472470996).setNanos(238_000_000).build())
106+
.setAnnotation(
107+
Span.TimeEvent.Annotation.newBuilder()
108+
.setDescription(toTruncatableString("foo"))
109+
.build())
110+
.build())
111+
.addTimeEvent(Span.TimeEvent.newBuilder()
112+
.setTime(Timestamp.newBuilder().setSeconds(1472470996).setNanos(403_000_000).build())
113+
.setAnnotation(
114+
Span.TimeEvent.Annotation.newBuilder()
115+
.setDescription(toTruncatableString("bar"))
116+
.build())
117+
.build())
118+
.build())
119+
.build());
120+
}
76121
}

encoder-stackdriver-brave/src/test/java/zipkin2/reporter/stackdriver/brave/TestObjects.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,25 @@ static MutableSpan clientSpan() {
2626
braveSpan.tag("http.path", "/api");
2727
return braveSpan;
2828
}
29+
30+
static MutableSpan spring6ServerSpan() {
31+
MutableSpan braveSpan = new MutableSpan();
32+
braveSpan.traceId("673adb3c54aa03af9e941a86a679a9d5");
33+
braveSpan.id("9e941a86a679a9d5");
34+
braveSpan.name("http get /test");
35+
braveSpan.kind(brave.Span.Kind.SERVER);
36+
braveSpan.localServiceName("backend");
37+
braveSpan.localIp("127.0.0.1");
38+
braveSpan.startTimestamp(1472470996199000L);
39+
braveSpan.finishTimestamp(1472470996199000L + 207000L);
40+
braveSpan.annotate(1472470996238000L, "foo");
41+
braveSpan.annotate(1472470996403000L, "bar");
42+
braveSpan.tag("exception", "none");
43+
braveSpan.tag("http.url", "/test");
44+
braveSpan.tag("method", "GET");
45+
braveSpan.tag("outcome", "SUCCESS");
46+
braveSpan.tag("status", "200");
47+
braveSpan.tag("uri", "/test");
48+
return braveSpan;
49+
}
2950
}

translation-stackdriver/src/main/java/zipkin2/translation/stackdriver/SpanTranslator.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.devtools.cloudtrace.v2.Span.TimeEvents;
99
import com.google.protobuf.Timestamp;
1010
import java.util.ArrayList;
11+
import java.util.Collections;
1112
import java.util.LinkedHashMap;
1213
import java.util.List;
1314
import java.util.Map;
@@ -16,6 +17,7 @@
1617
import zipkin2.Span;
1718

1819
import static java.util.logging.Level.FINE;
20+
import static zipkin2.translation.stackdriver.AttributesExtractor.toAttributeValue;
1921
import static zipkin2.translation.stackdriver.SpanUtil.toTruncatableString;
2022

2123
/** SpanTranslator converts a Zipkin Span to a Stackdriver Trace Span. */
@@ -35,6 +37,15 @@ public final class SpanTranslator {
3537
ATTRIBUTES_EXTRACTOR = new AttributesExtractor(renamedLabels);
3638
}
3739

40+
private static final Map<String, String> SPRING6_RENAMED_HTTP_LABELS;
41+
42+
static {
43+
Map<String, String> map = new LinkedHashMap<>();
44+
map.put("status", "/http/status_code");
45+
map.put("method", "/http/method");
46+
SPRING6_RENAMED_HTTP_LABELS = Collections.unmodifiableMap(map);
47+
}
48+
3849
/**
3950
* Convert a Collection of Zipkin Spans into a Collection of Stackdriver Trace Spans.
4051
*
@@ -104,6 +115,16 @@ public static com.google.devtools.cloudtrace.v2.Span.Builder translate(
104115
}
105116
spanBuilder.setAttributes(ATTRIBUTES_EXTRACTOR.extract(zipkinSpan));
106117

118+
// Spring 6 HTTP spans need mapping to Stackdriver conventional attribute names
119+
if (zipkinSpan.name() != null && zipkinSpan.name().contains("http")) {
120+
zipkinSpan.tags().forEach((key, value) -> {
121+
if (SPRING6_RENAMED_HTTP_LABELS.containsKey(key)) {
122+
spanBuilder.getAttributesBuilder()
123+
.putAttributeMap(SPRING6_RENAMED_HTTP_LABELS.get(key), toAttributeValue(value));
124+
}
125+
});
126+
}
127+
107128
if (!zipkinSpan.annotations().isEmpty()) {
108129
TimeEvents.Builder events = TimeEvents.newBuilder();
109130
for (Annotation annotation : zipkinSpan.annotations()) {

translation-stackdriver/src/test/java/zipkin2/translation/stackdriver/SpanTranslatorTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,59 @@ public class SpanTranslatorTest {
116116
assertThat(stackdriverSpans.get(1).getDisplayName().getValue()).isEqualTo("unknown");
117117
assertThat(stackdriverSpans.get(2).getDisplayName().getValue()).isEqualTo("somename");
118118
}
119+
120+
@Test void translate_spring6ServerSpan() {
121+
Span zipkinSpan =
122+
Span.newBuilder()
123+
.traceId("7180c278b62e8f6a216a2aea45d08fc9")
124+
.id("5b4185666d50f68b")
125+
.name("http get /test")
126+
.kind(Span.Kind.SERVER)
127+
.localEndpoint(Endpoint.newBuilder().serviceName("backend").build())
128+
.timestamp(1_000_000L) // 1 second after epoch
129+
.duration(123_456L)
130+
.addAnnotation(1_123_000L, "foo")
131+
.putTag("exception", "none")
132+
.putTag("http.url", "/test")
133+
.putTag("method", "GET")
134+
.putTag("outcome", "SUCCESS")
135+
.putTag("status", "200")
136+
.putTag("uri", "/test")
137+
.build();
138+
139+
com.google.devtools.cloudtrace.v2.Span
140+
translated = SpanTranslator.translate(
141+
com.google.devtools.cloudtrace.v2.Span.newBuilder(), zipkinSpan).build();
142+
143+
assertThat(translated)
144+
.isEqualTo(
145+
com.google.devtools.cloudtrace.v2.Span.newBuilder()
146+
.setSpanId(zipkinSpan.id())
147+
.setDisplayName(toTruncatableString("http get /test"))
148+
.setStartTime(Timestamp.newBuilder().setSeconds(1).build())
149+
.setEndTime(Timestamp.newBuilder().setSeconds(1).setNanos(123_456_000).build())
150+
.setAttributes(com.google.devtools.cloudtrace.v2.Span.Attributes.newBuilder()
151+
.putAttributeMap("/agent", toAttributeValue("zipkin-java"))
152+
.putAttributeMap("exception", toAttributeValue("none"))
153+
.putAttributeMap("/http/url", toAttributeValue("/test"))
154+
.putAttributeMap("/http/method", toAttributeValue("GET"))
155+
.putAttributeMap("outcome", toAttributeValue("SUCCESS"))
156+
.putAttributeMap("/http/status_code", toAttributeValue("200"))
157+
.putAttributeMap("uri", toAttributeValue("/test"))
158+
.putAttributeMap("method", toAttributeValue("GET"))
159+
.putAttributeMap("status", toAttributeValue("200"))
160+
.putAttributeMap("/kind", toAttributeValue("server"))
161+
.putAttributeMap("/component", toAttributeValue("backend"))
162+
.build())
163+
.setTimeEvents(com.google.devtools.cloudtrace.v2.Span.TimeEvents.newBuilder()
164+
.addTimeEvent(com.google.devtools.cloudtrace.v2.Span.TimeEvent.newBuilder()
165+
.setTime(createTimestamp(1_123_000L))
166+
.setAnnotation(
167+
com.google.devtools.cloudtrace.v2.Span.TimeEvent.Annotation.newBuilder()
168+
.setDescription(toTruncatableString("foo"))
169+
.build())
170+
.build())
171+
.build())
172+
.build());
173+
}
119174
}

0 commit comments

Comments
 (0)