private TimestampedAnnotation findAnnotationInSpan(Span span, String annotationValue) { return span .getTimestampedAnnotations() .stream() .filter(ta -> annotationValue.equals(ta.getValue())) .findFirst() .orElseThrow(() -> new RuntimeException( "Expected to find annotation with value: \"" + annotationValue + "\", but none was found" )); }
private TimestampedAnnotation findSpanAnnotation(Span span, String annotationName) { return span.getTimestampedAnnotations().stream() .filter(a -> annotationName.equalsIgnoreCase(a.getValue())) .findFirst() .orElse(null); }
@Test public void public_constructor_uses_empty_annotations_list_when_annotations_argument_is_null() { // when Span span = new Span(traceId, parentSpanId, spanId, spanName, true, userId, null, 42, null, null, null, null); // then assertThat(span.getTimestampedAnnotations()) .isNotNull() .isEmpty(); }
@Test public void builder_withTimestampedAnnotations_does_nothing_if_passed_null() { // given Span.Builder builder = Span.newBuilder("foo", SpanPurpose.UNKNOWN); List<TimestampedAnnotation> annotationsListSpy = spy(new ArrayList<>()); Whitebox.setInternalState(builder, "annotations", annotationsListSpy); // when Span.Builder resultingBuilder = builder.withTimestampedAnnotations(null); // then assertThat(resultingBuilder).isSameAs(builder); verifyZeroInteractions(annotationsListSpy); // and when Span resultingSpan = resultingBuilder.build(); // then assertThat(resultingSpan.getTimestampedAnnotations()).isEmpty(); }
@Test public void public_constructor_works_as_expected_for_completed_span() { // when Span span = new Span( traceId, parentSpanId, spanId, spanName, sampleableForFullyCompleteSpan, userId, spanPurposeForFullyCompletedSpan, startTimeEpochMicrosForFullyCompleteSpan, startTimeNanosForFullyCompleteSpan, durationNanosForFullyCompletedSpan, tags, annotations ); // then assertThat(span.getTraceId()).isEqualTo(traceId); assertThat(span.getParentSpanId()).isEqualTo(parentSpanId); assertThat(span.getSpanId()).isEqualTo(spanId); assertThat(span.getSpanName()).isEqualTo(spanName); assertThat(span.isSampleable()).isEqualTo(sampleableForFullyCompleteSpan); assertThat(span.getUserId()).isEqualTo(userId); assertThat(span.getSpanStartTimeEpochMicros()).isEqualTo(startTimeEpochMicrosForFullyCompleteSpan); assertThat(span.getSpanStartTimeNanos()).isEqualTo(startTimeNanosForFullyCompleteSpan); assertThat(span.getSpanPurpose()).isEqualTo(spanPurposeForFullyCompletedSpan); assertThat(span.isCompleted()).isTrue(); assertThat(span.getDurationNanos()).isEqualTo(durationNanosForFullyCompletedSpan); assertThat(span.getTags()).isEqualTo(tags); assertThat(span.getTimestampedAnnotations()).isEqualTo(annotations); }
@Test public void public_constructor_works_as_expected_for_incomplete_span() { // when Span span = new Span( traceId, parentSpanId, spanId, spanName, sampleableForFullyCompleteSpan, userId, spanPurposeForFullyCompletedSpan, startTimeEpochMicrosForFullyCompleteSpan, startTimeNanosForFullyCompleteSpan, null, tags, annotations ); // then assertThat(span.getTraceId()).isEqualTo(traceId); assertThat(span.getParentSpanId()).isEqualTo(parentSpanId); assertThat(span.getSpanId()).isEqualTo(spanId); assertThat(span.getSpanName()).isEqualTo(spanName); assertThat(span.isSampleable()).isEqualTo(sampleableForFullyCompleteSpan); assertThat(span.getUserId()).isEqualTo(userId); assertThat(span.getSpanStartTimeEpochMicros()).isEqualTo(startTimeEpochMicrosForFullyCompleteSpan); assertThat(span.getSpanStartTimeNanos()).isEqualTo(startTimeNanosForFullyCompleteSpan); assertThat(span.getSpanPurpose()).isEqualTo(spanPurposeForFullyCompletedSpan); assertThat(span.isCompleted()).isFalse(); assertThat(span.getDurationNanos()).isNull(); assertThat(span.getTags()).isEqualTo(tags); assertThat(span.getTimestampedAnnotations()).isEqualTo(annotations); }
@Test public void addTimestampedAnnotation_works_as_expected() { // given Span span = Span.newBuilder("foo", SpanPurpose.CLIENT).build(); TimestampedAnnotation annotationMock = mock(TimestampedAnnotation.class); // when span.addTimestampedAnnotation(annotationMock); // then assertThat(span.getTimestampedAnnotations()) .hasSize(1) .containsExactly(annotationMock); }
long nanoTimeAfterSpanCreation = System.nanoTime(); assertThat(span.getTimestampedAnnotations()).isEmpty(); long expectedMaxTimestamp = span.getSpanStartTimeEpochMicros() + maxPossibleOffsetMicros; assertThat(span.getTimestampedAnnotations().get(0).getTimestampEpochMicros()) .isBetween(expectedMinTimestamp, expectedMaxTimestamp);
public static void verifySpanDeepEquals( Span spanToVerify, Span expectedSpan, boolean allowStartTimeNanosFudgeFactor ) { assertThat(spanToVerify.getSpanStartTimeEpochMicros()).isEqualTo(expectedSpan.getSpanStartTimeEpochMicros()); if (allowStartTimeNanosFudgeFactor) { assertThat(spanToVerify.getSpanStartTimeNanos()) .isCloseTo(expectedSpan.getSpanStartTimeNanos(), Offset.offset(TimeUnit.MILLISECONDS.toNanos(1))); } else { assertThat(spanToVerify.getSpanStartTimeNanos()).isEqualTo(expectedSpan.getSpanStartTimeNanos()); } assertThat(spanToVerify.isCompleted()).isEqualTo(expectedSpan.isCompleted()); assertThat(spanToVerify.getTraceId()).isEqualTo(expectedSpan.getTraceId()); assertThat(spanToVerify.getSpanId()).isEqualTo(expectedSpan.getSpanId()); assertThat(spanToVerify.getParentSpanId()).isEqualTo(expectedSpan.getParentSpanId()); assertThat(spanToVerify.getSpanName()).isEqualTo(expectedSpan.getSpanName()); assertThat(spanToVerify.isSampleable()).isEqualTo(expectedSpan.isSampleable()); assertThat(spanToVerify.getUserId()).isEqualTo(expectedSpan.getUserId()); assertThat(spanToVerify.getDurationNanos()).isEqualTo(expectedSpan.getDurationNanos()); assertThat(spanToVerify.getSpanPurpose()).isEqualTo(expectedSpan.getSpanPurpose()); assertThat(spanToVerify.getTags()).isEqualTo(expectedSpan.getTags()); assertThat(spanToVerify.getTimestampedAnnotations()).isEqualTo(expectedSpan.getTimestampedAnnotations()); }
assertThat(result.isCompleted()).isFalse(); assertThat(result.getTags()).isEmpty(); assertThat(result.getTimestampedAnnotations()).isEmpty();
@UseDataProvider("escapedAndUnescapedQuotesBeforeKeyOrValueEndScenarioDataProvider") @Test public void fromJSON_properly_handles_escaped_quotes_and_unescaped_quotes_preceded_by_backslashes( EscapedAndUnescapedQuotesBeforeKeyOrValueEndScenario scenario ) { // given Span span = Span.newBuilder("someSpan", SpanPurpose.CLIENT) .withTag(scenario.unescapedKey, scenario.unescapedValue) .withTimestampedAnnotation( TimestampedAnnotation.forEpochMicros(1234, scenario.unescapedValue) ) .build(); String json = SpanParser.convertSpanToJSON(span); // when Span result = SpanParser.fromJSON(json); // then assertThat(result.getTags().get(scenario.unescapedKey)).isEqualTo(scenario.unescapedValue); assertThat(result.getTimestampedAnnotations().get(0).getValue()).isEqualTo(scenario.unescapedValue); }
assertThat(result.getDurationNanos()).isNull(); assertThat(result.getTags()).isEmpty(); assertThat(result.getTimestampedAnnotations()).isEmpty();
assertThat(zipkinSpan.localEndpoint()).isEqualTo(zipkinEndpoint); assertThat(zipkinSpan.tags()).isEqualTo(wingtipsSpan.getTags()).isEmpty(); assertThat(zipkinSpan.annotations()).isEqualTo(wingtipsSpan.getTimestampedAnnotations()).isEmpty();
@UseDataProvider("tagAndAnnotationScenarioDataProvider") @Test public void fromJson_should_function_properly_when_there_are_no_null_values(TagAndAnnotationScenario scenario) { // given: valid span without any null values, completed (so that end time is not null) and JSON string // from SpanParser.convertSpanToJSON() Span validSpan = createFilledOutSpan(true, scenario.tags, scenario.annotations); assertThat(validSpan).isNotNull(); assertThat(validSpan.getTraceId()).isNotNull(); assertThat(validSpan.getUserId()).isNotNull(); assertThat(validSpan.getParentSpanId()).isNotNull(); assertThat(validSpan.getSpanName()).isNotNull(); assertThat(validSpan.getSpanId()).isNotNull(); assertThat(validSpan.getDurationNanos()).isNotNull(); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getSpanPurpose()).isNotNull(); assertThat(validSpan.getTags()).isEqualTo(scenario.tags); assertThat(validSpan.getTimestampedAnnotations()).isEqualTo(scenario.annotations); String json = SpanParser.convertSpanToJSON(validSpan); // when: fromJson is called Span spanFromJson = SpanParser.fromJSON(json); // then: the original span and the fromJson() span values should be exactly the same verifySpanDeepEquals(spanFromJson, validSpan, true); }
@UseDataProvider("tagAndAnnotationScenarioDataProvider") @Test public void convertSpanToJSON_should_function_properly_when_there_are_no_null_values( TagAndAnnotationScenario scenario ) throws IOException { // given: valid span without any null values, span completed (so that end time is not null), and JSON string // from SpanParser.convertSpanToJSON() Span validSpan = createFilledOutSpan(true, scenario.tags, scenario.annotations); assertThat(validSpan.getTraceId()).isNotEmpty(); assertThat(validSpan.getUserId()).isNotEmpty(); assertThat(validSpan.getParentSpanId()).isNotEmpty(); assertThat(validSpan.getSpanName()).isNotEmpty(); assertThat(validSpan.getSpanId()).isNotEmpty(); assertThat(validSpan.getDurationNanos()).isNotNull(); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getSpanPurpose()).isNotNull(); assertThat(validSpan.getTags()).isEqualTo(scenario.tags); assertThat(validSpan.getTimestampedAnnotations()).isEqualTo(scenario.annotations); String json = SpanParser.convertSpanToJSON(validSpan); // when: jackson is used to deserialize that JSON Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); // then: the original span context and jackson's span context values should be exactly the same verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
@UseDataProvider("tagAndAnnotationScenarioDataProvider") @Test public void fromKeyValueString_should_function_properly_when_there_are_no_null_values( TagAndAnnotationScenario scenario) { // given: valid span without any null values, completed (so that end time is not null) and key/value string // from Span.fromKeyValueString() Span validSpan = createFilledOutSpan(true, scenario.tags, scenario.annotations); assertThat(validSpan).isNotNull(); assertThat(validSpan.getTraceId()).isNotNull(); assertThat(validSpan.getUserId()).isNotNull(); assertThat(validSpan.getParentSpanId()).isNotNull(); assertThat(validSpan.getSpanName()).isNotNull(); assertThat(validSpan.getSpanId()).isNotNull(); assertThat(validSpan.getDurationNanos()).isNotNull(); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getSpanPurpose()).isNotNull(); assertThat(validSpan.getTags()).isNotNull(); assertThat(validSpan.getTags()).isEqualTo(scenario.tags); assertThat(validSpan.getTimestampedAnnotations()).isEqualTo(scenario.annotations); String keyValStr = SpanParser.convertSpanToKeyValueFormat(validSpan); // when: fromKeyValueString is called Span spanFromKeyValStr = SpanParser.fromKeyValueString(keyValStr); // then: the original span and the fromKeyValueString() span values should be exactly the same verifySpanDeepEquals(spanFromKeyValStr, validSpan, true); }
for (TimestampedAnnotation wingtipsAnnotation : wingtipsSpan.getTimestampedAnnotations()) { spanBuilder.addAnnotation(wingtipsAnnotation.getTimestampEpochMicros(), wingtipsAnnotation.getValue());
@Override public zipkin.Span convertWingtipsSpanToZipkinSpan(Span wingtipsSpan, Endpoint zipkinEndpoint, String localComponentNamespace) { String traceId = wingtipsSpan.getTraceId(); long startEpochMicros = wingtipsSpan.getSpanStartTimeEpochMicros(); long durationMicros = TimeUnit.NANOSECONDS.toMicros(wingtipsSpan.getDurationNanos()); zipkin.Span.Builder builder = createNewZipkinSpanBuilderWithSpanPurposeAnnotations(wingtipsSpan, startEpochMicros, durationMicros, zipkinEndpoint, localComponentNamespace) .id(nullSafeLong(wingtipsSpan.getSpanId())) .name(wingtipsSpan.getSpanName()) .parentId(nullSafeLong(wingtipsSpan.getParentSpanId())) .timestamp(startEpochMicros) .traceIdHigh(traceId.length() == 32 ? nullSafeLong(traceId, 0) : 0) .traceId(nullSafeLong(traceId)) .duration(durationMicros); addAllTagsToBuilderAsBinaryAnnotations(builder, wingtipsSpan.getTags(), zipkinEndpoint); addAllAnnotationsToBuilder(builder, wingtipsSpan.getTimestampedAnnotations(), zipkinEndpoint); return builder.build(); }
@Override public zipkin.Span convertWingtipsSpanToZipkinSpan(Span wingtipsSpan, Endpoint zipkinEndpoint, String localComponentNamespace) { String traceId = wingtipsSpan.getTraceId(); long startEpochMicros = wingtipsSpan.getSpanStartTimeEpochMicros(); long durationMicros = TimeUnit.NANOSECONDS.toMicros(wingtipsSpan.getDurationNanos()); zipkin.Span.Builder builder = createNewZipkinSpanBuilderWithSpanPurposeAnnotations(wingtipsSpan, startEpochMicros, durationMicros, zipkinEndpoint, localComponentNamespace) .id(nullSafeLong(wingtipsSpan.getSpanId())) .name(wingtipsSpan.getSpanName()) .parentId(nullSafeLong(wingtipsSpan.getParentSpanId())) .timestamp(startEpochMicros) .traceIdHigh(traceId.length() == 32 ? nullSafeLong(traceId, 0) : 0) .traceId(nullSafeLong(traceId)) .duration(durationMicros); addAllTagsToBuilderAsBinaryAnnotations(builder, wingtipsSpan.getTags(), zipkinEndpoint); addAllAnnotationsToBuilder(builder, wingtipsSpan.getTimestampedAnnotations(), zipkinEndpoint); return builder.build(); }
@UseDataProvider("tagAndAnnotationScenarioDataProvider") @Test public void convertSpanToKeyValueFormat_should_function_properly_when_there_are_no_null_values( TagAndAnnotationScenario scenario ) { // given: valid known span without any null values, span completed (so that end time is not null) // and key/value string from SpanParser.convertSpanToKeyValueFormat() Span validSpan = createFilledOutSpan(true, scenario.tags, scenario.annotations); assertThat(validSpan.getTraceId()).isNotEmpty(); assertThat(validSpan.getUserId()).isNotEmpty(); assertThat(validSpan.getParentSpanId()).isNotEmpty(); assertThat(validSpan.getSpanName()).isNotEmpty(); assertThat(validSpan.getSpanId()).isNotEmpty(); assertThat(validSpan.getDurationNanos()).isNotNull(); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getSpanPurpose()).isNotNull(); assertThat(validSpan.getTags()).isEqualTo(scenario.tags); assertThat(validSpan.getTimestampedAnnotations()).isEqualTo(scenario.annotations); String keyValueStr = SpanParser.convertSpanToKeyValueFormat(validSpan); // when: the string is deserialized into a map Map<String, Object> deserializedValues = deserializeKeyValueSpanString(keyValueStr); // then: the original span and deserialized map values should be exactly the same verifySpanEqualsDeserializedValues(validSpan, deserializedValues); }