WingtipsToZipkinLifecycleListener implements SpanLifecycleListener { @Override public void spanCompleted(Span span) { if (!shouldReportCompletedSpan(span)) { return; } try { zipkin2.Span zipkinSpan = zipkinSpanConverter.convertWingtipsSpanToZipkinSpan(span, zipkinEndpoint); zipkinSpanReporter.report(zipkinSpan); } catch(Throwable ex) { long currentBadSpanCount = spanHandlingErrorCounter.incrementAndGet(); long currentTimeMillis = System.currentTimeMillis(); long timeSinceLastLogMsgMillis = currentTimeMillis - lastSpanHandlingErrorLogTimeEpochMillis; if (timeSinceLastLogMsgMillis >= MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS) { lastSpanHandlingErrorLogTimeEpochMillis = currentTimeMillis; zipkinConversionOrReportingErrorLogger.warn( "There have been {} spans that were not Zipkin compatible, or that experienced an error during span handling. Latest example: " + "wingtips_span_with_error=\"{}\", conversion_or_handling_error=\"{}\"", currentBadSpanCount, span.toKeyValueString(), ex.toString()); } } } WingtipsToZipkinLifecycleListener(String serviceName, WingtipsToZipkinSpanConverter zipkinSpanConverter, Reporter<zipkin2.Span> zipkinSpanReporter);  WingtipsToZipkinLifecycleListener(String serviceName, String postZipkinSpansBaseUrl); static Reporter<zipkin2.Span> generateBasicZipkinReporter(String postZipkinSpansBaseUrl); @Override void spanStarted(Span span); @Override void spanSampled(Span span); @Override void spanCompleted(Span span);  }
@Test public void spanCompleted_converts_to_zipkin_span_and_passes_it_to_zipkinSpanReporter() { zipkin2.Span zipkinSpan = zipkin2.Span.newBuilder().traceId("42").id("4242").name("foo").build(); doReturn(zipkinSpan).when(spanConverterMock).convertWingtipsSpanToZipkinSpan(any(Span.class), any(Endpoint.class)); listener.spanCompleted(spanMock); verify(spanConverterMock).convertWingtipsSpanToZipkinSpan(spanMock, listener.zipkinEndpoint); verify(spanReporterMock).report(zipkinSpan); }
@Test public void spanCompleted_does_not_propagate_exceptions_generated_by_span_converter() { doThrow(new RuntimeException("kaboom")).when(spanConverterMock).convertWingtipsSpanToZipkinSpan(any(Span.class), any(Endpoint.class)); Throwable ex = catchThrowable(() -> listener.spanCompleted(spanMock)); verify(spanConverterMock).convertWingtipsSpanToZipkinSpan(spanMock, listener.zipkinEndpoint); verifyZeroInteractions(spanReporterMock); assertThat(ex).isNull(); }
@Test public void spanCompleted_does_not_propagate_exceptions_generated_by_span_reporter() { doThrow(new RuntimeException("kaboom")).when(spanReporterMock).report(any()); Throwable ex = catchThrowable(() -> listener.spanCompleted(spanMock)); verify(spanReporterMock).report(any()); assertThat(ex).isNull(); }
@Test public void spanCompleted_logs_error_during_handling_if_time_since_lastSpanHandlingErrorLogTimeEpochMillis_is_greater_than_MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS() { Logger loggerMock = mock(Logger.class); Whitebox.setInternalState(listener, "zipkinConversionOrReportingErrorLogger", loggerMock); long lastLogTimeToSet = System.currentTimeMillis() - (MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS + 10); Whitebox.setInternalState(listener, "lastSpanHandlingErrorLogTimeEpochMillis", lastLogTimeToSet); doThrow(new RuntimeException("kaboom")).when(spanReporterMock).report(any()); long before = System.currentTimeMillis(); listener.spanCompleted(spanMock); long after = System.currentTimeMillis(); verify(loggerMock).warn(anyString(), anyLong(), any(), anyString()); assertThat((long)Whitebox.getInternalState(listener, "lastSpanHandlingErrorLogTimeEpochMillis")).isBetween(before, after); }
@Test public void spanCompleted_does_not_log_an_error_during_handling_if_time_since_lastSpanHandlingErrorLogTimeEpochMillis_is_less_than_MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS() { Logger loggerMock = mock(Logger.class); Whitebox.setInternalState(listener, "zipkinConversionOrReportingErrorLogger", loggerMock); long lastLogTimeToSet = System.currentTimeMillis() - (MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS - 1000); Whitebox.setInternalState(listener, "lastSpanHandlingErrorLogTimeEpochMillis", lastLogTimeToSet); doThrow(new RuntimeException("kaboom")).when(spanReporterMock).report(any(zipkin2.Span.class)); listener.spanCompleted(spanMock); verifyZeroInteractions(loggerMock); assertThat((long)Whitebox.getInternalState(listener, "lastSpanHandlingErrorLogTimeEpochMillis")).isEqualTo(lastLogTimeToSet); }
@Test public void spanCompleted_does_nothing_if_span_is_not_sampleable() { doReturn(false).when(spanMock).isSampleable(); listener.spanCompleted(spanMock); verify(spanMock).isSampleable(); verifyNoMoreInteractions(spanMock); verifyNoInteractions(listener.zipkinSpanConverter, listener.zipkinSpanReporter); }