diff --git a/translation-stackdriver/src/main/java/zipkin2/translation/stackdriver/SpanTranslator.java b/translation-stackdriver/src/main/java/zipkin2/translation/stackdriver/SpanTranslator.java index 35a99c1..ee0797c 100644 --- a/translation-stackdriver/src/main/java/zipkin2/translation/stackdriver/SpanTranslator.java +++ b/translation-stackdriver/src/main/java/zipkin2/translation/stackdriver/SpanTranslator.java @@ -7,15 +7,14 @@ import com.google.devtools.cloudtrace.v2.Span.TimeEvent; import com.google.devtools.cloudtrace.v2.Span.TimeEvents; import com.google.protobuf.Timestamp; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; + +import java.util.*; import java.util.logging.Logger; import zipkin2.Annotation; import zipkin2.Span; import static java.util.logging.Level.FINE; +import static zipkin2.translation.stackdriver.AttributesExtractor.toAttributeValue; import static zipkin2.translation.stackdriver.SpanUtil.toTruncatableString; /** SpanTranslator converts a Zipkin Span to a Stackdriver Trace Span. */ @@ -35,6 +34,15 @@ public final class SpanTranslator { ATTRIBUTES_EXTRACTOR = new AttributesExtractor(renamedLabels); } + private static final Map SPRING6_RENAMED_HTTP_LABELS; + + static { + Map map = new LinkedHashMap<>(); + map.put("status", "/http/status_code"); + map.put("method", "/http/method"); + SPRING6_RENAMED_HTTP_LABELS = Collections.unmodifiableMap(map); + } + /** * Convert a Collection of Zipkin Spans into a Collection of Stackdriver Trace Spans. * @@ -104,6 +112,16 @@ public static com.google.devtools.cloudtrace.v2.Span.Builder translate( } spanBuilder.setAttributes(ATTRIBUTES_EXTRACTOR.extract(zipkinSpan)); + // Spring 6 HTTP spans need mapping to Stackdriver conventional attribute names + if (zipkinSpan.name() != null && zipkinSpan.name().contains("http")) { + zipkinSpan.tags().forEach((key, value) -> { + if (SPRING6_RENAMED_HTTP_LABELS.containsKey(key)) { + spanBuilder.getAttributesBuilder() + .putAttributeMap(SPRING6_RENAMED_HTTP_LABELS.get(key), toAttributeValue(value)); + } + }); + } + if (!zipkinSpan.annotations().isEmpty()) { TimeEvents.Builder events = TimeEvents.newBuilder(); for (Annotation annotation : zipkinSpan.annotations()) { diff --git a/translation-stackdriver/src/test/java/zipkin2/translation/stackdriver/SpanTranslatorTest.java b/translation-stackdriver/src/test/java/zipkin2/translation/stackdriver/SpanTranslatorTest.java index 0ddcc15..3b78863 100644 --- a/translation-stackdriver/src/test/java/zipkin2/translation/stackdriver/SpanTranslatorTest.java +++ b/translation-stackdriver/src/test/java/zipkin2/translation/stackdriver/SpanTranslatorTest.java @@ -116,4 +116,59 @@ public class SpanTranslatorTest { assertThat(stackdriverSpans.get(1).getDisplayName().getValue()).isEqualTo("unknown"); assertThat(stackdriverSpans.get(2).getDisplayName().getValue()).isEqualTo("somename"); } + + @Test void translate_spring6ServerSpan() { + Span zipkinSpan = + Span.newBuilder() + .traceId("7180c278b62e8f6a216a2aea45d08fc9") + .id("5b4185666d50f68b") + .name("http get /test") + .kind(Span.Kind.SERVER) + .localEndpoint(Endpoint.newBuilder().serviceName("backend").build()) + .timestamp(1_000_000L) // 1 second after epoch + .duration(123_456L) + .addAnnotation(1_123_000L, "foo") + .putTag("exception", "none") + .putTag("http.url", "/test") + .putTag("method", "GET") + .putTag("outcome", "SUCCESS") + .putTag("status", "200") + .putTag("uri", "/test") + .build(); + + com.google.devtools.cloudtrace.v2.Span + translated = SpanTranslator.translate( + com.google.devtools.cloudtrace.v2.Span.newBuilder(), zipkinSpan).build(); + + assertThat(translated) + .isEqualTo( + com.google.devtools.cloudtrace.v2.Span.newBuilder() + .setSpanId(zipkinSpan.id()) + .setDisplayName(toTruncatableString("http get /test")) + .setStartTime(Timestamp.newBuilder().setSeconds(1).build()) + .setEndTime(Timestamp.newBuilder().setSeconds(1).setNanos(123_456_000).build()) + .setAttributes(com.google.devtools.cloudtrace.v2.Span.Attributes.newBuilder() + .putAttributeMap("/agent", toAttributeValue("zipkin-java")) + .putAttributeMap("exception", toAttributeValue("none")) + .putAttributeMap("/http/url", toAttributeValue("/test")) + .putAttributeMap("/http/method", toAttributeValue("GET")) + .putAttributeMap("outcome", toAttributeValue("SUCCESS")) + .putAttributeMap("/http/status_code", toAttributeValue("200")) + .putAttributeMap("uri", toAttributeValue("/test")) + .putAttributeMap("method", toAttributeValue("GET")) + .putAttributeMap("status", toAttributeValue("200")) + .putAttributeMap("/kind", toAttributeValue("server")) + .putAttributeMap("/component", toAttributeValue("backend")) + .build()) + .setTimeEvents(com.google.devtools.cloudtrace.v2.Span.TimeEvents.newBuilder() + .addTimeEvent(com.google.devtools.cloudtrace.v2.Span.TimeEvent.newBuilder() + .setTime(createTimestamp(1_123_000L)) + .setAnnotation( + com.google.devtools.cloudtrace.v2.Span.TimeEvent.Annotation.newBuilder() + .setDescription(toTruncatableString("foo")) + .build()) + .build()) + .build()) + .build()); + } }