/** * Convenience constructor that uses {@link WingtipsToZipkinSpanConverterDefaultImpl} for converting Wingtips spans * to Zipkin spans, {@link AsyncReporter} for the Zipkin reporter, and {@link URLConnectionSender} as the underlying * {@link Sender} used by the {@link AsyncReporter}. If you need more flexibility in the span converter or * reporter/sender, then use the {@link * WingtipsToZipkinLifecycleListener#WingtipsToZipkinLifecycleListener(String, WingtipsToZipkinSpanConverter, Reporter)} * constructor instead. * * @param serviceName The name of this service. This is used to build the Zipkin {@link Endpoint}, which tells * Zipkin which service generated the spans we're going to send it. * @param postZipkinSpansBaseUrl The base URL of the Zipkin server. This should include the scheme, host, and port * (if non-standard for the scheme). e.g. {@code http://localhost:9411}, or * {@code https://zipkinserver.doesnotexist.com/}. This assumes you want to send spans to Zipkin over HTTP - if * you want to use something else then you'll need to specify your own reporter/sender using the {@link * WingtipsToZipkinLifecycleListener#WingtipsToZipkinLifecycleListener(String, WingtipsToZipkinSpanConverter, Reporter)} * constructor instead. */ public WingtipsToZipkinLifecycleListener(String serviceName, String postZipkinSpansBaseUrl) { this(serviceName, new WingtipsToZipkinSpanConverterDefaultImpl(), generateBasicZipkinReporter(postZipkinSpansBaseUrl) ); }
/** * Convenience constructor that uses {@link WingtipsToZipkinSpanConverterDefaultImpl} for converting Wingtips spans * to Zipkin spans, {@link AsyncReporter} for the Zipkin reporter, and {@link URLConnectionSender} as the underlying * {@link Sender} used by the {@link AsyncReporter}. If you need more flexibility in the span converter or * reporter/sender, then use the {@link * WingtipsToZipkinLifecycleListener#WingtipsToZipkinLifecycleListener(String, WingtipsToZipkinSpanConverter, Reporter)} * constructor instead. * * @param serviceName The name of this service. This is used to build the Zipkin {@link Endpoint}, which tells * Zipkin which service generated the spans we're going to send it. * @param postZipkinSpansBaseUrl The base URL of the Zipkin server. This should include the scheme, host, and port * (if non-standard for the scheme). e.g. {@code http://localhost:9411}, or * {@code https://zipkinserver.doesnotexist.com/}. This assumes you want to send spans to Zipkin over HTTP - if * you want to use something else then you'll need to specify your own reporter/sender using the {@link * WingtipsToZipkinLifecycleListener#WingtipsToZipkinLifecycleListener(String, WingtipsToZipkinSpanConverter, Reporter)} * constructor instead. */ public WingtipsToZipkinLifecycleListener(String serviceName, String postZipkinSpansBaseUrl) { this(serviceName, new WingtipsToZipkinSpanConverterDefaultImpl(), generateBasicZipkinReporter(postZipkinSpansBaseUrl) ); }
@Test public void default_constructor_sets_fields_as_expected() { // given impl = new WingtipsToZipkinSpanConverterDefaultImpl(); // expect assertThat(impl.enableIdSanitization).isFalse(); }
@DataProvider(value = { "true", "false" }) @Test public void constructor_with_args_sets_fields_as_expected(boolean enableSanitization) { // given impl = new WingtipsToZipkinSpanConverterDefaultImpl(enableSanitization); // expect assertThat(impl.enableIdSanitization).isEqualTo(enableSanitization); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_spanId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { // given 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(); // when zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); // then assertThat(zipkinSpan.id()).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); assertThat(zipkinSpan.tags().get("invalid.span_id")).isEqualTo(scenario.originalId); }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_parentSpanId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { // given 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(); // when zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); // then assertThat(zipkinSpan.parentId()).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); assertThat(zipkinSpan.tags().get("invalid.parent_id")).isEqualTo(scenario.originalId); }
@Test public void convertWingtipsSpanToZipkinSpan_sanitizes_multiple_IDs_as_expected_when_sanitization_is_enabled() { impl = new WingtipsToZipkinSpanConverterDefaultImpl(true);
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_traceId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { // given 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; // when zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); // then assertThat(zipkinSpan.traceId()).isEqualTo(scenario.expectedSanitizedResultForTraceId); assertThat(zipkinSpan.tags().get("invalid.trace_id")).isEqualTo(expectedZipkinInvalidIdTagValue); }
/** * Initialize configuration. * Add Zipkin listener if our {@link WingtipsZipkinProperties} indicates it has the necessary properties specified. */ private void init() { if (wingtipsZipkinProperties.shouldApplyWingtipsToZipkinLifecycleListener()) { Reporter<zipkin2.Span> zipkinSpanReporter = (zipkinReporterOverride != null) ? zipkinReporterOverride : WingtipsToZipkinLifecycleListener.generateBasicZipkinReporter(wingtipsZipkinProperties.getBaseUrl()); WingtipsToZipkinSpanConverter zipkinSpanConverter = (zipkinSpanConverterOverride != null) ? zipkinSpanConverterOverride : new WingtipsToZipkinSpanConverterDefaultImpl(); WingtipsToZipkinLifecycleListener listenerToRegister = new WingtipsToZipkinLifecycleListener( wingtipsZipkinProperties.getServiceName(), zipkinSpanConverter, zipkinSpanReporter ); Tracer.getInstance().addSpanLifecycleListener(listenerToRegister); } }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_does_not_sanitize_ids_if_enableIdSanitization_is_false( IdSanitizationScenario scenario ) { // given 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"; // when Throwable ex = catchThrowable(() -> impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint)); // then assertThat(ex) .isInstanceOf(IllegalArgumentException.class) .hasMessageEndingWith(expectedExceptionMessageSuffix); }