/** * Uses {@link #spanLoggingRepresentation} to decide how to serialize the given span, and then returns the result of the serialization. */ protected String serializeSpanToDesiredStringRepresentation(Span span) { switch(spanLoggingRepresentation) { case JSON: return span.toJSON(); case KEY_VALUE: return span.toKeyValueString(); default: throw new IllegalStateException("Unknown span logging representation type: " + spanLoggingRepresentation); } }
@Override public void spanCompleted(Span span) { try { zipkin2.Span zipkinSpan = zipkinSpanConverter.convertWingtipsSpanToZipkinSpan(span, zipkinEndpoint); zipkinSpanReporter.report(zipkinSpan); } catch(Throwable ex) { long currentBadSpanCount = spanHandlingErrorCounter.incrementAndGet(); // Only log once every MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS time interval to prevent log spam from a malicious (or broken) caller. long currentTimeMillis = System.currentTimeMillis(); long timeSinceLastLogMsgMillis = currentTimeMillis - lastSpanHandlingErrorLogTimeEpochMillis; if (timeSinceLastLogMsgMillis >= MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS) { // We're not synchronizing the read and write to lastSpanHandlingErrorLogTimeEpochMillis, and that's ok. If we get a few extra // log messages due to a race condition it's not the end of the world - we're still satisfying the goal of not allowing a // malicious caller to endlessly spam the logs. 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()); } } } }
@Override public void spanCompleted(Span span) { try { zipkin2.Span zipkinSpan = zipkinSpanConverter.convertWingtipsSpanToZipkinSpan(span, zipkinEndpoint); zipkinSpanReporter.report(zipkinSpan); } catch(Throwable ex) { long currentBadSpanCount = spanHandlingErrorCounter.incrementAndGet(); // Only log once every MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS time interval to prevent log spam from a malicious (or broken) caller. long currentTimeMillis = System.currentTimeMillis(); long timeSinceLastLogMsgMillis = currentTimeMillis - lastSpanHandlingErrorLogTimeEpochMillis; if (timeSinceLastLogMsgMillis >= MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS) { // We're not synchronizing the read and write to lastSpanHandlingErrorLogTimeEpochMillis, and that's ok. If we get a few extra // log messages due to a race condition it's not the end of the world - we're still satisfying the goal of not allowing a // malicious caller to endlessly spam the logs. 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()); } } } }
@Override public void spanCompleted(Span span) { try { zipkin.Span zipkinSpan = zipkinSpanConverter.convertWingtipsSpanToZipkinSpan(span, zipkinEndpoint, localComponentNamespace); zipkinSpanSender.handleSpan(zipkinSpan); } catch(Throwable ex) { long currentBadSpanCount = spanHandlingErrorCounter.incrementAndGet(); // Only log once every MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS time interval to prevent log spam from a malicious (or broken) caller. long currentTimeMillis = System.currentTimeMillis(); long timeSinceLastLogMsgMillis = currentTimeMillis - lastSpanHandlingErrorLogTimeEpochMillis; if (timeSinceLastLogMsgMillis >= MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS) { // We're not synchronizing the read and write to lastSpanHandlingErrorLogTimeEpochMillis, and that's ok. If we get a few extra // log messages due to a race condition it's not the end of the world - we're still satisfying the goal of not allowing a // malicious caller to endlessly spam the logs. 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()); } } } }
@Override public void spanCompleted(Span span) { try { zipkin.Span zipkinSpan = zipkinSpanConverter.convertWingtipsSpanToZipkinSpan(span, zipkinEndpoint, localComponentNamespace); zipkinSpanSender.handleSpan(zipkinSpan); } catch(Throwable ex) { long currentBadSpanCount = spanHandlingErrorCounter.incrementAndGet(); // Only log once every MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS time interval to prevent log spam from a malicious (or broken) caller. long currentTimeMillis = System.currentTimeMillis(); long timeSinceLastLogMsgMillis = currentTimeMillis - lastSpanHandlingErrorLogTimeEpochMillis; if (timeSinceLastLogMsgMillis >= MIN_SPAN_HANDLING_ERROR_LOG_INTERVAL_MILLIS) { // We're not synchronizing the read and write to lastSpanHandlingErrorLogTimeEpochMillis, and that's ok. If we get a few extra // log messages due to a race condition it's not the end of the world - we're still satisfying the goal of not allowing a // malicious caller to endlessly spam the logs. 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()); } } } }
@Test public void toKeyValueString_should_use_cached_key_value_string() { // given Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); String uuidString = UUID.randomUUID().toString(); Whitebox.setInternalState(validSpan, "cachedKeyValueRepresentation", uuidString); // when String toKeyValueStringResult = validSpan.toKeyValueString(); // then assertThat(toKeyValueStringResult).isEqualTo(uuidString); }
@Test public void fromKeyValueString_delegates_to_span_parser() { // given Span span = Span.newBuilder("foo", SpanPurpose.CLIENT) .withTag("blahtag", UUID.randomUUID().toString()) .build(); String keyValueStr = span.toKeyValueString(); // when Span result = span.fromKeyValueString(keyValueStr); // then verifySpanDeepEquals(result, span, true); }
@Test public void complete_should_reset_cached_key_value_string() { // given Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); String uuidString = UUID.randomUUID().toString(); Whitebox.setInternalState(validSpan, "cachedKeyValueRepresentation", uuidString); // when String beforeCompleteKeyValueString = validSpan.toKeyValueString(); completeSpan(validSpan); // then String afterCompleteKeyValueString = validSpan.toKeyValueString(); assertThat(afterCompleteKeyValueString).isNotEqualTo(beforeCompleteKeyValueString); assertThat(afterCompleteKeyValueString).isNotEqualTo(uuidString); Map<String, Object> deserializedValues = deserializeKeyValueSpanString(afterCompleteKeyValueString); verifySpanEqualsDeserializedValues(validSpan, deserializedValues); }
@DataProvider(value = { "JSON", "KEY_VALUE" }, splitBy = "\\|") @Test public void verify_span_serialization_methods(Tracer.SpanLoggingRepresentation serializationOption) { // given Span span = Span.generateRootSpanForNewTrace(UUID.randomUUID().toString(), SpanPurpose.LOCAL_ONLY).build(); String expectedOutput; switch(serializationOption) { case JSON: expectedOutput = span.toJSON(); break; case KEY_VALUE: expectedOutput = span.toKeyValueString(); break; default: throw new IllegalArgumentException("Unhandled option: " + serializationOption); } Tracer.getInstance().setSpanLoggingRepresentation(serializationOption); // then assertThat(Tracer.getInstance().getSpanLoggingRepresentation()).isEqualTo(serializationOption); // and when String serializedString = Tracer.getInstance().serializeSpanToDesiredStringRepresentation(span); // then assertThat(serializedString).isEqualTo(expectedOutput); }