Span implements AutoCloseable, SpanData { public final void end() { duration = System.nanoTime() - duration; Optional<SpanContext> context = SpanHelper.currentContext(); if (context.isPresent()) { context.get().endSpan(this); afterClose(); } else { throw new IllegalStateException("Span.end() from a detached span."); } } protected  Span(Optional<BigInteger> parentSpanId, BigInteger spanId, String name, BigInteger traceId, long startTime, long startNanos, TraceLevel traceLevel); abstract void addAnnotation(String name); abstract void addAnnotation(String name, String message); final void end(); @Override final void close(); final BigInteger getTraceId(); final BigInteger getSpanId(); final Optional<BigInteger> getParentSpanId(); final String getName(); final String getHost(); @SuppressWarnings("UnusedDeclaration") final Integer getPid(); @Override final long getStartTime(); @Override final long getDuration(); @Override final List<AnnotationData> getAnnotations(); @Override String toString(); @Override boolean equals(Object o); @Override int hashCode();  }
@Test public void testRootlessSpans() { final Span outer = SpanHelper.startTrace("outerSpan"); final Span inner = SpanHelper.startSpan("innerSpan"); inner.end(); outer.end(); }
@Test public void testMultipleSinks() { final SpanSink first = mock(SpanSink.class); final SpanSink second = mock(SpanSink.class); SpanSinkRegistry.register(first); SpanSinkRegistry.register(second); final Span span = SpanHelper.startTrace("testSpan"); span.end(); verify(first).record(span); verify(second).record(span); }
@Test public void testSpansClosedInIncorrectOrderClearsContextButDoesNotLogUnclosedSpans() { final SpanSink sink = mock(SpanSink.class); SpanSinkRegistry.register(sink); Span trace = SpanHelper.startTrace("The Trace"); SpanHelper.startSpan("one"); SpanHelper.startSpan("two"); trace.end(); assertTrue(SpanHelper.captureSpans().isEmpty()); verify(sink).record(trace); verifyZeroInteractions(sink); }
@Test(expected = EmptyStackException.class) public void testEndingASpanMoreThanOnce() { final SpanSink sink = mock(SpanSink.class); SpanSinkRegistry.register(sink); Span trace = SpanHelper.startTrace("The Trace"); trace.end(); assertTrue(SpanHelper.captureSpans().isEmpty()); verify(sink).record(trace); trace.end(); }
@Test(expected = IllegalStateException.class) public void testEndingASpanFromInvalidThreadContext() throws Throwable { final SpanSink sink = mock(SpanSink.class); SpanSinkRegistry.register(sink); try (final Span trace = SpanHelper.startTrace("The Trace")) { final CountDownLatch successLatch = new CountDownLatch(1); final ArrayBlockingQueue<Throwable> expectedException = new ArrayBlockingQueue<>(1); new Thread(new Runnable() { @Override public void run() { try { trace.end(); successLatch.countDown(); } catch (Throwable t) { expectedException.offer(t.fillInStackTrace()); } } }).start(); throw expectedException.poll(100, TimeUnit.MILLISECONDS); } }