WingtipsToZipkinSpanConverterDefaultImpl implements WingtipsToZipkinSpanConverter { @Override public zipkin2.Span convertWingtipsSpanToZipkinSpan(Span wingtipsSpan, Endpoint zipkinEndpoint) { long durationMicros = TimeUnit.NANOSECONDS.toMicros(wingtipsSpan.getDurationNanos()); String spanId = sanitizeIdIfNecessary(wingtipsSpan.getSpanId(), false); String traceId = sanitizeIdIfNecessary(wingtipsSpan.getTraceId(), true); String parentId = sanitizeIdIfNecessary(wingtipsSpan.getParentSpanId(), false); final zipkin2.Span.Builder spanBuilder = zipkin2.Span .newBuilder() .id(spanId) .name(wingtipsSpan.getSpanName()) .parentId(parentId) .traceId(traceId) .timestamp(wingtipsSpan.getSpanStartTimeEpochMicros()) .duration(durationMicros) .localEndpoint(zipkinEndpoint) .kind(determineZipkinKind(wingtipsSpan)); for (Map.Entry<String, String> tagEntry : wingtipsSpan.getTags().entrySet()) { spanBuilder.putTag(tagEntry.getKey(), tagEntry.getValue()); } if (!spanId.equals(wingtipsSpan.getSpanId())) { spanBuilder.putTag("invalid.span_id", wingtipsSpan.getSpanId()); wingtipsSpan.putTag("sanitized_span_id", spanId); } if (!traceId.equals(wingtipsSpan.getTraceId())) { spanBuilder.putTag("invalid.trace_id", wingtipsSpan.getTraceId()); wingtipsSpan.putTag("sanitized_trace_id", traceId); } if (parentId != null && !parentId.equals(wingtipsSpan.getParentSpanId())) { spanBuilder.putTag("invalid.parent_id", wingtipsSpan.getParentSpanId()); wingtipsSpan.putTag("sanitized_parent_id", parentId); } for (TimestampedAnnotation wingtipsAnnotation : wingtipsSpan.getTimestampedAnnotations()) { spanBuilder.addAnnotation(wingtipsAnnotation.getTimestampEpochMicros(), wingtipsAnnotation.getValue()); } return spanBuilder.build(); } WingtipsToZipkinSpanConverterDefaultImpl();  WingtipsToZipkinSpanConverterDefaultImpl(boolean enableIdSanitization); @Override zipkin2.Span convertWingtipsSpanToZipkinSpan(Span wingtipsSpan, Endpoint zipkinEndpoint);  }
@DataProvider(value = { "SERVER", "CLIENT", "LOCAL_ONLY", "UNKNOWN" }, splitBy = "\\|") @Test public void convertWingtipsSpanToZipkinSpan_works_as_expected_for_all_non_null_info(SpanPurpose spanPurpose) { String spanName = UUID.randomUUID().toString(); String traceId = generateId(); String spanId = generateId(); String parentId = generateId(); long startTimeEpochMicros = Math.abs(random.nextLong()); long durationNanos = Math.abs(random.nextLong()); long durationMicros = TimeUnit.NANOSECONDS.toMicros(durationNanos); Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); Map<String, String> tags = createMultipleTagMap(); List<TimestampedAnnotation> annotations = createMultipleTimestampedAnnotationList(); Span wingtipsSpan = new Span( traceId, parentId, spanId, spanName, true, null, spanPurpose, startTimeEpochMicros, null, durationNanos, tags, annotations ); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.id()).isEqualTo(wingtipsSpan.getSpanId()); assertThat(zipkinSpan.name()).isEqualTo(wingtipsSpan.getSpanName()); assertThat(zipkinSpan.parentId()).isEqualTo(wingtipsSpan.getParentSpanId()); assertThat(zipkinSpan.timestamp()).isEqualTo(wingtipsSpan.getSpanStartTimeEpochMicros()); assertThat(zipkinSpan.traceId()).isEqualTo(wingtipsSpan.getTraceId()); assertThat(zipkinSpan.duration()).isEqualTo(durationMicros); assertThat(zipkinSpan.localEndpoint()).isEqualTo(zipkinEndpoint); assertThat(zipkinSpan.tags()).isEqualTo(wingtipsSpan.getTags()); assertThat(toWingtipsAnnotations(zipkinSpan.annotations())).isEqualTo(wingtipsSpan.getTimestampedAnnotations()); verifySpanPurposeRelatedStuff(zipkinSpan, wingtipsSpan); }
@DataProvider(value = { "SERVER", "CLIENT", "LOCAL_ONLY", "UNKNOWN" }, splitBy = "\\|") @Test public void convertWingtipsSpanToZipkinSpan_works_as_expected_for_all_nullable_info(SpanPurpose spanPurpose) { String spanName = UUID.randomUUID().toString(); String traceId = generateId(); String spanId = generateId(); long startTimeEpochMicros = Math.abs(random.nextLong()); long durationNanos = Math.abs(random.nextLong()); long durationMicros = TimeUnit.NANOSECONDS.toMicros(durationNanos); Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); Span wingtipsSpan = new Span( traceId, null, spanId, spanName, true, null, spanPurpose, startTimeEpochMicros, null, durationNanos, null, null ); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.id()).isEqualTo(wingtipsSpan.getSpanId()); assertThat(zipkinSpan.name()).isEqualTo(wingtipsSpan.getSpanName()); assertThat(zipkinSpan.parentId()).isNull(); assertThat(zipkinSpan.timestamp()).isEqualTo(wingtipsSpan.getSpanStartTimeEpochMicros()); assertThat(zipkinSpan.traceId()).isEqualTo(wingtipsSpan.getTraceId()); assertThat(zipkinSpan.duration()).isEqualTo(durationMicros); assertThat(zipkinSpan.localEndpoint()).isEqualTo(zipkinEndpoint); assertThat(zipkinSpan.tags()).isEqualTo(wingtipsSpan.getTags()).isEmpty(); assertThat(zipkinSpan.annotations()).isEqualTo(wingtipsSpan.getTimestampedAnnotations()).isEmpty(); verifySpanPurposeRelatedStuff(zipkinSpan, wingtipsSpan); }
@Test @SuppressWarnings("UnnecessaryLocalVariable") public void convertWingtipsSpanToZipkinSpan_works_as_expected_for_128_bit_trace_id() { String high64Bits = "463ac35c9f6413ad"; String low64Bits = "48485a3953bb6124"; String traceId128Bits = high64Bits + low64Bits; long startTimeEpochMicros = Math.abs(random.nextLong()); long durationNanos = Math.abs(random.nextLong()); Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTraceId(traceId128Bits) .withSpanStartTimeEpochMicros(startTimeEpochMicros) .withDurationNanos(durationNanos) .build(); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.traceId()).isEqualTo(traceId128Bits); }
@DataProvider(value = { " \t\n\r ", "" }, splitBy = "\\|") @Test @SuppressWarnings("UnnecessaryLocalVariable") public void convertWingtipsSpanToZipkinSpan_throws_IllegalArgumentException_when_passed_wingtipsSpan_with_empty_traceId_format( final String emptyString ) { String emptyTraceId = emptyString; long startTimeEpochMicros = Math.abs(random.nextLong()); long durationNanos = Math.abs(random.nextLong()); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTraceId(emptyTraceId) .withSpanStartTimeEpochMicros(startTimeEpochMicros) .withDurationNanos(durationNanos) .build(); Throwable ex = catchThrowable(() -> impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint)); assertThat(ex) .isInstanceOf(IllegalArgumentException.class) .hasMessage("traceId is empty"); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_traceId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { impl = new WingtipsToZipkinSpanConverterDefaultImpl(true); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTraceId(scenario.originalId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); String expectedZipkinInvalidIdTagValue = (scenario.expectedSanitizedResultForTraceId.equals(scenario.originalId)) ? null : scenario.originalId; String expectedWingtipsSanitizedIdTagValue = (scenario.expectedSanitizedResultForTraceId.equals(scenario.originalId)) ? null : scenario.expectedSanitizedResultForTraceId; zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.traceId()).isEqualTo(scenario.expectedSanitizedResultForTraceId); assertThat(zipkinSpan.tags().get("invalid.trace_id")).isEqualTo(expectedZipkinInvalidIdTagValue); assertThat(wingtipsSpan.getTags().get("sanitized_trace_id")).isEqualTo(expectedWingtipsSanitizedIdTagValue); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_spanId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { impl = new WingtipsToZipkinSpanConverterDefaultImpl(true); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withSpanId(scenario.originalId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.id()).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); assertThat(zipkinSpan.tags().get("invalid.span_id")).isEqualTo(scenario.originalId); assertThat(wingtipsSpan.getTags().get("sanitized_span_id")).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_parentSpanId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { impl = new WingtipsToZipkinSpanConverterDefaultImpl(true); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withParentSpanId(scenario.originalId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.parentId()).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); assertThat(zipkinSpan.tags().get("invalid.parent_id")).isEqualTo(scenario.originalId); assertThat(wingtipsSpan.getTags().get("sanitized_parent_id")).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); }
@Test public void convertWingtipsSpanToZipkinSpan_sanitizes_multiple_IDs_as_expected_when_sanitization_is_enabled() { impl = new WingtipsToZipkinSpanConverterDefaultImpl(true); String badTraceId = UUID.randomUUID().toString(); String expectedSanitizedTraceId = badTraceId.replace("-", ""); String badSpanId = "notahexstring"; String expectedSanitizedSpanId = "15afe1fe8fe52ed3"; String badParentSpanId = TraceAndSpanIdGenerator.generateId().toUpperCase(); String expectedSanitizedParentSpanId = badParentSpanId.toLowerCase(); assertThat(expectedSanitizedTraceId).isNotEqualTo(badTraceId); assertThat(expectedSanitizedSpanId).isNotEqualTo(badSpanId); assertThat(expectedSanitizedParentSpanId).isNotEqualTo(badParentSpanId); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTraceId(badTraceId) .withSpanId(badSpanId) .withParentSpanId(badParentSpanId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); assertThat(zipkinSpan.traceId()).isEqualTo(expectedSanitizedTraceId); assertThat(zipkinSpan.id()).isEqualTo(expectedSanitizedSpanId); assertThat(zipkinSpan.parentId()).isEqualTo(expectedSanitizedParentSpanId); assertThat(zipkinSpan.tags().get("invalid.trace_id")).isEqualTo(badTraceId); assertThat(zipkinSpan.tags().get("invalid.span_id")).isEqualTo(badSpanId); assertThat(zipkinSpan.tags().get("invalid.parent_id")).isEqualTo(badParentSpanId); assertThat(wingtipsSpan.getTags().get("sanitized_trace_id")).isEqualTo(expectedSanitizedTraceId); assertThat(wingtipsSpan.getTags().get("sanitized_span_id")).isEqualTo(expectedSanitizedSpanId); assertThat(wingtipsSpan.getTags().get("sanitized_parent_id")).isEqualTo(expectedSanitizedParentSpanId); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_does_not_sanitize_ids_if_enableIdSanitization_is_false( IdSanitizationScenario scenario ) { impl = new WingtipsToZipkinSpanConverterDefaultImpl(false); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTraceId(scenario.originalId) .withSpanId(scenario.originalId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); String expectedExceptionMessageSuffix = (scenario.originalId.length() > 16) ? "id.length > 16" : "should be lower-hex encoded with no prefix"; Throwable ex = catchThrowable(() -> impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint)); assertThat(ex) .isInstanceOf(IllegalArgumentException.class) .hasMessageEndingWith(expectedExceptionMessageSuffix); }