private void verifyDurationBetweenLowerAndUpperBounds(Span span, long beforeCompletionCallNanoTime, long afterCompletionCallNanoTime) { long durationLowerBound = beforeCompletionCallNanoTime - span.getSpanStartTimeNanos(); long durationUpperBound = afterCompletionCallNanoTime - span.getSpanStartTimeNanos(); assertThat(span.getDurationNanos()).isNotNull(); assertThat(span.getDurationNanos()).isBetween(durationLowerBound, durationUpperBound); }
@Test public void getDuration_should_be_null_until_span_is_completed() { // given Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); assertThat(validSpan.getDurationNanos()).isNull(); // when validSpan.complete(); // then assertThat(validSpan.getDurationNanos()).isNotNull(); }
@Test public void doChannelInactive_does_not_try_to_recomplete_span_if_already_completed() throws Exception { // given Span span = setupTracingForChannelInactive(false); Deque<Span> deque = new LinkedList<>(); deque.add(span); Tracer.getInstance().registerWithThread(deque); Tracer.getInstance().completeRequestSpan(); Assertions.assertThat(span.isCompleted()).isTrue(); long durationNanosBefore = span.getDurationNanos(); // when PipelineContinuationBehavior result = handler.doChannelInactive(ctxMock); // then Assertions.assertThat(span.isCompleted()).isTrue(); // no change Assertions.assertThat(span.getDurationNanos()).isEqualTo(durationNanosBefore); verify(requestInfoMock).releaseAllResources(); verify(proxyRouterStateMock).cancelRequestStreaming(any(), any()); verify(proxyRouterStateMock).cancelDownstreamRequest(any()); Assertions.assertThat(result).isEqualTo(PipelineContinuationBehavior.CONTINUE); }
@Test public void convertSpanToJSON_should_function_properly_for_non_completed_spans() throws IOException { // given: valid span and JSON string from SpanParser.convertSpanToJSON() Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); String json = SpanParser.convertSpanToJSON(validSpan); assertThat(validSpan.isCompleted()).isFalse(); assertThat(validSpan.getDurationNanos()).isNull(); // when: jackson is used to deserialize that JSON Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); // then: the original span and jackson's span values should be exactly the same verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
@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 complete_method_should_complete_the_span_with_correct_duration() throws InterruptedException { for (int i = 0; i < 10; i++) { // given Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); assertThat(validSpan.isCompleted()).isFalse(); // when Thread.sleep((long) (Math.random() * 10)); long beforeCompleteNanoTime = System.nanoTime(); validSpan.complete(); long afterCompleteNanoTime = System.nanoTime(); // then assertThat(validSpan.isCompleted()).isTrue(); long lowerBoundDuration = beforeCompleteNanoTime - validSpan.getSpanStartTimeNanos(); long upperBoundDuration = afterCompleteNanoTime - validSpan.getSpanStartTimeNanos(); assertThat(validSpan.getDurationNanos()).isBetween(lowerBoundDuration, upperBoundDuration); } }
private void verifySingleSpanCompletedAndReturnedInResponse(ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } }
@Test public void equals_returns_false_and_hashCode_different_if_durationNanos_is_different() { // given Span fullSpan1 = createFilledOutSpan(true); Span fullSpan2 = createFilledOutSpan(true); List<Long> badDataList = Arrays.asList(fullSpan1.getDurationNanos() + 1, null); for (Long badData : badDataList) { Whitebox.setInternalState(fullSpan2, "durationNanos", badData); // expect assertThat(fullSpan1.equals(fullSpan2)).isFalse(); assertThat(fullSpan2.equals(fullSpan1)).isFalse(); assertThat(fullSpan1.hashCode()).isNotEqualTo(fullSpan2.hashCode()); } }
private Span verifySingleSpanCompletedAndReturnedInResponse(ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(completedSpan.getSpanName()).doesNotContain("?"); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } return completedSpan; }
private Span verifySingleSpanCompletedAndReturnedInResponse( ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan ) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } return completedSpan; }
@Test public void convertSpanToJSON_should_function_properly_for_completed_spans() throws IOException { // given: valid span and completed, and JSON string from SpanParser.convertSpanToJSON() Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); completeSpan(validSpan); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getDurationNanos()).isNotNull(); 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 and jackson's span values should be exactly the same verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
private Span verifySingleSpanCompletedAndReturnedInResponse(ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(completedSpan.getSpanName()).doesNotContain("?"); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } return completedSpan; }
private Span verifySingleSpanCompletedAndReturnedInResponse(ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(completedSpan.getSpanName()).doesNotContain("?"); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } return completedSpan; }
private Span verifySingleSpanCompletedAndReturnedInResponse(ExtractableResponse response, long expectedMinSpanDurationMillis, Span expectedUpstreamSpan) { // We can have a race condition where the response is sent and we try to verify here before the servlet filter // has had a chance to complete the span. Wait a few milliseconds to give the servlet filter time to // finish. waitUntilSpanRecorderHasExpectedNumSpans(1); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); String traceIdFromResponse = response.header(TraceHeaders.TRACE_ID); assertThat(traceIdFromResponse).isNotNull(); assertThat(completedSpan.getTraceId()).isEqualTo(traceIdFromResponse); assertThat(completedSpan.getSpanName()).doesNotContain("?"); assertThat(TimeUnit.NANOSECONDS.toMillis(completedSpan.getDurationNanos())) .isGreaterThanOrEqualTo(expectedMinSpanDurationMillis); if (expectedUpstreamSpan != null) { assertThat(completedSpan.getTraceId()).isEqualTo(expectedUpstreamSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(expectedUpstreamSpan.getSpanId()); } return completedSpan; }
@Test public void convertSpanToJSON_should_function_properly_when_there_are_null_values() throws IOException { // given: valid span with null values and JSON string from SpanParser.convertSpanToJSON() Span validSpan = Span.generateRootSpanForNewTrace(spanName, null).build(); assertThat(validSpan.getParentSpanId()).isNull(); assertThat(validSpan.getUserId()).isNull(); assertThat(validSpan.getDurationNanos()).isNull(); 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); }
@Test public void fromJson_should_function_properly_when_there_are_null_values() { // given: valid span with null values and JSON string from SpanParser.convertSpanToJSON() Span validSpan = Span.generateRootSpanForNewTrace(spanName, null).build(); assertThat(validSpan.getParentSpanId()).isNull(); assertThat(validSpan.getUserId()).isNull(); assertThat(validSpan.getDurationNanos()).isNull(); 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); }
@Test public void convertSpanToKeyValueFormat_should_function_properly_when_there_are_null_values() { // given: valid span with null values and key/value string from SpanParser.convertSpanToKeyValueFormat() Span validSpan = Span.generateRootSpanForNewTrace(spanName, null).build(); assertThat(validSpan.getParentSpanId()).isNull(); assertThat(validSpan.getUserId()).isNull(); assertThat(validSpan.getDurationNanos()).isNull(); 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); }
@Test public void fromKeyValueString_should_function_properly_when_there_are_null_values() { // given: valid span with null values and key/value string from Span.fromKeyValueString() Span validSpan = Span.generateRootSpanForNewTrace(spanName, null).build(); assertThat(validSpan.getParentSpanId()).isNull(); assertThat(validSpan.getUserId()).isNull(); assertThat(validSpan.getDurationNanos()).isNull(); 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); }
@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(); }