diff --git a/api/src/main/java/io/opentelemetry/api/trace/DefaultTracer.java b/api/src/main/java/io/opentelemetry/api/trace/DefaultTracer.java index 6ee6ae54c2..a18c697602 100644 --- a/api/src/main/java/io/opentelemetry/api/trace/DefaultTracer.java +++ b/api/src/main/java/io/opentelemetry/api/trace/DefaultTracer.java @@ -10,6 +10,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.internal.Utils; import io.opentelemetry.context.Context; import java.util.Objects; +import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; @@ -107,7 +108,7 @@ final class DefaultTracer implements Tracer { } @Override - public NoopSpanBuilder setStartTimestamp(long startTimestamp) { + public NoopSpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) { Utils.checkArgument(startTimestamp >= 0, "Negative startTimestamp"); return this; } diff --git a/api/src/main/java/io/opentelemetry/api/trace/PropagatedSpan.java b/api/src/main/java/io/opentelemetry/api/trace/PropagatedSpan.java index a69540dd44..18dda502f7 100644 --- a/api/src/main/java/io/opentelemetry/api/trace/PropagatedSpan.java +++ b/api/src/main/java/io/opentelemetry/api/trace/PropagatedSpan.java @@ -7,6 +7,7 @@ package io.opentelemetry.api.trace; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import java.util.concurrent.TimeUnit; import javax.annotation.concurrent.Immutable; /** @@ -71,7 +72,7 @@ final class PropagatedSpan implements Span { } @Override - public Span addEvent(String name, long timestamp) { + public Span addEvent(String name, long timestamp, TimeUnit unit) { return this; } @@ -81,7 +82,7 @@ final class PropagatedSpan implements Span { } @Override - public Span addEvent(String name, Attributes attributes, long timestamp) { + public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { return this; } @@ -114,7 +115,7 @@ final class PropagatedSpan implements Span { public void end() {} @Override - public void end(long timestamp) {} + public void end(long timestamp, TimeUnit unit) {} @Override public SpanContext getSpanContext() { diff --git a/api/src/main/java/io/opentelemetry/api/trace/Span.java b/api/src/main/java/io/opentelemetry/api/trace/Span.java index 1bc5aa1e9c..0fa7bc41a2 100644 --- a/api/src/main/java/io/opentelemetry/api/trace/Span.java +++ b/api/src/main/java/io/opentelemetry/api/trace/Span.java @@ -5,10 +5,15 @@ package io.opentelemetry.api.trace; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.context.Context; import io.opentelemetry.context.ImplicitContextKeyed; +import java.time.Instant; +import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; @@ -199,10 +204,32 @@ public interface Span extends ImplicitContextKeyed { * occurred. * * @param name the name of the event. - * @param timestamp the explicit event timestamp in nanos since epoch. + * @param timestamp the explicit event timestamp since epoch. + * @param unit the unit of the timestamp * @return this. */ - Span addEvent(String name, long timestamp); + Span addEvent(String name, long timestamp, TimeUnit unit); + + /** + * Adds an event to the {@link Span} with the given {@code timestamp}, as nanos since epoch. Note, + * this {@code timestamp} is not the same as {@link System#nanoTime()} but may be computed using + * it, for example, by taking a difference of readings from {@link System#nanoTime()} and adding + * to the span start time. + * + *
When possible, it is preferred to use {@link #addEvent(String)} at the time the event + * occurred. + * + * @param name the name of the event. + * @param timestamp the explicit event timestamp since epoch. + * @return this. + */ + default Span addEvent(String name, Instant timestamp) { + if (timestamp == null) { + return addEvent(name); + } + return addEvent( + name, SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), NANOSECONDS); + } /** * Adds an event to the {@link Span} with the given {@link Attributes}. The timestamp of the event @@ -227,10 +254,37 @@ public interface Span extends ImplicitContextKeyed { * @param name the name of the event. * @param attributes the attributes that will be added; these are associated with this event, not * the {@code Span} as for {@code setAttribute()}. - * @param timestamp the explicit event timestamp in nanos since epoch. + * @param timestamp the explicit event timestamp since epoch. + * @param unit the unit of the timestamp * @return this. */ - Span addEvent(String name, Attributes attributes, long timestamp); + Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit); + + /** + * Adds an event to the {@link Span} with the given {@link Attributes} and {@code timestamp}. + * Note, this {@code timestamp} is not the same as {@link System#nanoTime()} but may be computed + * using it, for example, by taking a difference of readings from {@link System#nanoTime()} and + * adding to the span start time. + * + *
When possible, it is preferred to use {@link #addEvent(String)} at the time the event + * occurred. + * + * @param name the name of the event. + * @param attributes the attributes that will be added; these are associated with this event, not + * the {@code Span} as for {@code setAttribute()}. + * @param timestamp the explicit event timestamp since epoch. + * @return this. + */ + default Span addEvent(String name, Attributes attributes, Instant timestamp) { + if (timestamp == null) { + return addEvent(name, attributes); + } + return addEvent( + name, + attributes, + SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), + NANOSECONDS); + } /** * Sets the status to the {@code Span}. @@ -312,10 +366,31 @@ public interface Span extends ImplicitContextKeyed { *
Use this method for specifying explicit end options, such as end {@code Timestamp}. When no * explicit values are required, use {@link #end()}. * - * @param timestamp the explicit timestamp, as nanos from the epoch, for this {@code Span}. {@code - * 0} indicates current time should be used. + * @param timestamp the explicit timestamp from the epoch, for this {@code Span}. {@code 0} + * indicates current time should be used. + * @param unit the unit of the timestamp */ - void end(long timestamp); + void end(long timestamp, TimeUnit unit); + + /** + * Marks the end of {@code Span} execution with the specified timestamp. + * + *
Only the timing of the first end call for a given {@code Span} will be recorded, and + * implementations are free to ignore all further calls. + * + *
Use this method for specifying explicit end options, such as end {@code Timestamp}. When no + * explicit values are required, use {@link #end()}. + * + * @param timestamp the explicit timestamp from the epoch, for this {@code Span}. {@code 0} + * indicates current time should be used. + */ + default void end(Instant timestamp) { + if (timestamp == null) { + end(); + return; + } + end(SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), NANOSECONDS); + } /** * Returns the {@code SpanContext} associated with this {@code Span}. @@ -570,11 +645,32 @@ public interface Span extends ImplicitContextKeyed { * *
Important this is NOT equivalent with System.nanoTime(). * - * @param startTimestamp the explicit start timestamp of the newly created {@code Span} in nanos - * since epoch. + * @param startTimestamp the explicit start timestamp from the epoch of the newly created {@code + * Span}. + * @param unit the unit of the timestamp. * @return this. */ - Builder setStartTimestamp(long startTimestamp); + Builder setStartTimestamp(long startTimestamp, TimeUnit unit); + + /** + * Sets an explicit start timestamp for the newly created {@code Span}. + * + *
Use this method to specify an explicit start timestamp. If not called, the implementation + * will use the timestamp value at {@link #startSpan()} time, which should be the default case. + * + *
Important this is NOT equivalent with System.nanoTime().
+     *
+     * @param startTimestamp the explicit start timestamp from the epoch of the newly created {@code
+     *     Span}.
+     * @return this.
+     */
+    default Builder setStartTimestamp(Instant startTimestamp) {
+      if (startTimestamp == null) {
+        return this;
+      }
+      return setStartTimestamp(
+          SECONDS.toNanos(startTimestamp.getEpochSecond()) + startTimestamp.getNano(), NANOSECONDS);
+    }
 
     /**
      * Starts a new {@link Span}.
diff --git a/api/src/test/java/io/opentelemetry/api/trace/PropagatedSpanTest.java b/api/src/test/java/io/opentelemetry/api/trace/PropagatedSpanTest.java
index c6f26c3e5b..6f28085ccd 100644
--- a/api/src/test/java/io/opentelemetry/api/trace/PropagatedSpanTest.java
+++ b/api/src/test/java/io/opentelemetry/api/trace/PropagatedSpanTest.java
@@ -15,6 +15,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import io.opentelemetry.api.common.Attributes;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
 import org.junit.jupiter.api.Test;
 
 class PropagatedSpanTest {
@@ -41,15 +43,18 @@ class PropagatedSpanTest {
     span.setAttribute(doubleArrayKey("NullArrayDouble"), null);
     span.setAttribute((String) null, null);
     span.addEvent("event");
-    span.addEvent("event", 0);
+    span.addEvent("event", 0, TimeUnit.NANOSECONDS);
+    span.addEvent("event", Instant.EPOCH);
     span.addEvent("event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true));
-    span.addEvent("event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true), 0);
+    span.addEvent(
+        "event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true), 0, TimeUnit.NANOSECONDS);
     span.setStatus(StatusCode.OK);
     span.setStatus(StatusCode.OK, "null");
     span.recordException(new IllegalStateException());
     span.recordException(new IllegalStateException(), Attributes.empty());
     span.end();
-    span.end(0);
+    span.end(0, TimeUnit.NANOSECONDS);
+    span.end(Instant.EPOCH);
   }
 
   @Test
diff --git a/api/src/test/java/io/opentelemetry/api/trace/SpanBuilderTest.java b/api/src/test/java/io/opentelemetry/api/trace/SpanBuilderTest.java
index b822d2cb12..64c59bdcc6 100644
--- a/api/src/test/java/io/opentelemetry/api/trace/SpanBuilderTest.java
+++ b/api/src/test/java/io/opentelemetry/api/trace/SpanBuilderTest.java
@@ -12,6 +12,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.trace.Span.Kind;
 import io.opentelemetry.context.Context;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
 import org.junit.jupiter.api.Test;
 
 /** Unit tests for {@link Span.Builder}. */
@@ -32,7 +34,8 @@ class SpanBuilderTest {
     spanBuilder.setAttribute("key", .12345);
     spanBuilder.setAttribute("key", true);
     spanBuilder.setAttribute(stringKey("key"), "value");
-    spanBuilder.setStartTimestamp(12345L);
+    spanBuilder.setStartTimestamp(12345L, TimeUnit.NANOSECONDS);
+    spanBuilder.setStartTimestamp(Instant.EPOCH);
     assertThat(spanBuilder.startSpan().getSpanContext().isValid()).isFalse();
   }
 
@@ -47,7 +50,7 @@ class SpanBuilderTest {
     Span.Builder spanBuilder = tracer.spanBuilder("MySpanName");
     assertThrows(
         IllegalArgumentException.class,
-        () -> spanBuilder.setStartTimestamp(-1),
+        () -> spanBuilder.setStartTimestamp(-1, TimeUnit.NANOSECONDS),
         "Negative startTimestamp");
   }
 }
diff --git a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/SpanConverter.java b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/SpanConverter.java
index d413dae339..363982a35f 100644
--- a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/SpanConverter.java
+++ b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/SpanConverter.java
@@ -82,7 +82,8 @@ class SpanConverter {
             .spanBuilder(ocSpanData.getName())
             .setStartTimestamp(
                 TimeUnit.SECONDS.toNanos(ocSpanData.getStartTimestamp().getSeconds())
-                    + ocSpanData.getStartTimestamp().getNanos());
+                    + ocSpanData.getStartTimestamp().getNanos(),
+                TimeUnit.NANOSECONDS);
     if (ocSpanData.getKind() != null) {
       builder.setSpanKind(mapKind(ocSpanData.getKind()));
     }
@@ -167,7 +168,8 @@ class SpanConverter {
               AttributeKey.longKey(MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_COMPRESSED),
               event.getEvent().getCompressedMessageSize()),
           TimeUnit.SECONDS.toNanos(event.getTimestamp().getSeconds())
-              + event.getTimestamp().getNanos());
+              + event.getTimestamp().getNanos(),
+          TimeUnit.NANOSECONDS);
     }
   }
 
@@ -190,7 +192,8 @@ class SpanConverter {
           annotation.getEvent().getDescription(),
           attributesBuilder.build(),
           TimeUnit.SECONDS.toNanos(annotation.getTimestamp().getSeconds())
-              + annotation.getTimestamp().getNanos());
+              + annotation.getTimestamp().getNanos(),
+          TimeUnit.NANOSECONDS);
     }
   }
 
diff --git a/opencensus-shim/src/test/java/io/opentelemetry/opencensusshim/SpanConverterTest.java b/opencensus-shim/src/test/java/io/opentelemetry/opencensusshim/SpanConverterTest.java
index b2602c94d9..4a735d6a91 100644
--- a/opencensus-shim/src/test/java/io/opentelemetry/opencensusshim/SpanConverterTest.java
+++ b/opencensus-shim/src/test/java/io/opentelemetry/opencensusshim/SpanConverterTest.java
@@ -105,7 +105,8 @@ class SpanConverterTest {
             "First annotation!",
             Attributes.builder().put("Attribute1", false).put("Attribute2", 123).build(),
             TimeUnit.SECONDS.toNanos(annotations.get(0).getTimestamp().getSeconds())
-                + annotations.get(0).getTimestamp().getNanos());
+                + annotations.get(0).getTimestamp().getNanos(),
+            TimeUnit.NANOSECONDS);
     verify(spanSpy, times(1))
         .addEvent(
             "Second annotation!",
@@ -114,7 +115,8 @@ class SpanConverterTest {
                 .put("Attribute2", "attributeValue")
                 .build(),
             TimeUnit.SECONDS.toNanos(annotations.get(1).getTimestamp().getSeconds())
-                + annotations.get(1).getTimestamp().getNanos());
+                + annotations.get(1).getTimestamp().getNanos(),
+            TimeUnit.NANOSECONDS);
   }
 
   @Test
@@ -145,7 +147,8 @@ class SpanConverterTest {
                 .put("message.event.size.compressed", 34)
                 .build(),
             TimeUnit.SECONDS.toNanos(messageEvents.get(0).getTimestamp().getSeconds())
-                + messageEvents.get(0).getTimestamp().getNanos());
+                + messageEvents.get(0).getTimestamp().getNanos(),
+            TimeUnit.NANOSECONDS);
     verify(spanSpy, times(1))
         .addEvent(
             "8",
@@ -155,7 +158,8 @@ class SpanConverterTest {
                 .put("message.event.size.compressed", 180)
                 .build(),
             TimeUnit.SECONDS.toNanos(messageEvents.get(1).getTimestamp().getSeconds())
-                + messageEvents.get(1).getTimestamp().getNanos());
+                + messageEvents.get(1).getTimestamp().getNanos(),
+            TimeUnit.NANOSECONDS);
   }
 
   private static RecordEventsSpanImpl createOpenCensusSpan() {
diff --git a/sdk-extensions/zpages/src/test/java/io/opentelemetry/sdk/extension/zpages/TracezZPageHandlerTest.java b/sdk-extensions/zpages/src/test/java/io/opentelemetry/sdk/extension/zpages/TracezZPageHandlerTest.java
index c611b878dd..e91dab936e 100644
--- a/sdk-extensions/zpages/src/test/java/io/opentelemetry/sdk/extension/zpages/TracezZPageHandlerTest.java
+++ b/sdk-extensions/zpages/src/test/java/io/opentelemetry/sdk/extension/zpages/TracezZPageHandlerTest.java
@@ -20,6 +20,7 @@ import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -59,8 +60,9 @@ class TracezZPageHandlerTest {
 
     Span runningSpan = tracer.spanBuilder(RUNNING_SPAN).startSpan();
 
-    Span latencySpan = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan.end(10002);
+    Span latencySpan =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan.end(10002, TimeUnit.NANOSECONDS);
 
     Span errorSpan = tracer.spanBuilder(ERROR_SPAN).startSpan();
     errorSpan.setStatus(StatusCode.ERROR);
@@ -142,32 +144,41 @@ class TracezZPageHandlerTest {
   void summaryTable_linkForLatencyBasedSpans_OnePerBoundary() {
     OutputStream output = new ByteArrayOutputStream();
     // Boundary 0, >1us
-    Span latencySpanSubtype0 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype0.end(1002);
+    Span latencySpanSubtype0 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype0.end(1002, TimeUnit.NANOSECONDS);
     // Boundary 1, >10us
-    Span latencySpanSubtype1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype1.end(10002);
+    Span latencySpanSubtype1 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype1.end(10002, TimeUnit.NANOSECONDS);
     // Boundary 2, >100us
-    Span latencySpanSubtype2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype2.end(100002);
+    Span latencySpanSubtype2 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype2.end(100002, TimeUnit.NANOSECONDS);
     // Boundary 3, >1ms
-    Span latencySpanSubtype3 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype3.end(1000002);
+    Span latencySpanSubtype3 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype3.end(1000002, TimeUnit.NANOSECONDS);
     // Boundary 4, >10ms
-    Span latencySpanSubtype4 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype4.end(10000002);
+    Span latencySpanSubtype4 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype4.end(10000002, TimeUnit.NANOSECONDS);
     // Boundary 5, >100ms
-    Span latencySpanSubtype5 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype5.end(100000002);
+    Span latencySpanSubtype5 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype5.end(100000002, TimeUnit.NANOSECONDS);
     // Boundary 6, >1s
-    Span latencySpanSubtype6 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype6.end(1000000002);
+    Span latencySpanSubtype6 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype6.end(1000000002, TimeUnit.NANOSECONDS);
     // Boundary 7, >10s
-    Span latencySpanSubtype7 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype7.end(10000000002L);
+    Span latencySpanSubtype7 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype7.end(10000000002L, TimeUnit.NANOSECONDS);
     // Boundary 8, >100s
-    Span latencySpanSubtype8 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpanSubtype8.end(100000000002L);
+    Span latencySpanSubtype8 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpanSubtype8.end(100000000002L, TimeUnit.NANOSECONDS);
 
     TracezZPageHandler tracezZPageHandler = new TracezZPageHandler(dataAggregator);
     tracezZPageHandler.emitHtml(emptyQueryMap, output);
@@ -205,14 +216,18 @@ class TracezZPageHandlerTest {
   void summaryTable_linkForLatencyBasedSpans_MultipleForOneBoundary() {
     OutputStream output = new ByteArrayOutputStream();
     // 4 samples in boundary 5, >100ms
-    Span latencySpan100ms1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan100ms1.end(112931232L);
-    Span latencySpan100ms2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan100ms2.end(138694322L);
-    Span latencySpan100ms3 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan100ms3.end(154486482L);
-    Span latencySpan100ms4 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan100ms4.end(194892582L);
+    Span latencySpan100ms1 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan100ms1.end(112931232L, TimeUnit.NANOSECONDS);
+    Span latencySpan100ms2 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan100ms2.end(138694322L, TimeUnit.NANOSECONDS);
+    Span latencySpan100ms3 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan100ms3.end(154486482L, TimeUnit.NANOSECONDS);
+    Span latencySpan100ms4 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan100ms4.end(194892582L, TimeUnit.NANOSECONDS);
 
     TracezZPageHandler tracezZPageHandler = new TracezZPageHandler(dataAggregator);
     tracezZPageHandler.emitHtml(emptyQueryMap, output);
@@ -270,10 +285,12 @@ class TracezZPageHandlerTest {
   @Test
   void spanDetails_emitLatencySpanDetailsCorrectly() {
     OutputStream output = new ByteArrayOutputStream();
-    Span latencySpan1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan1.end(10002);
-    Span latencySpan2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
-    latencySpan2.end(10002);
+    Span latencySpan1 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan1.end(10002, TimeUnit.NANOSECONDS);
+    Span latencySpan2 =
+        tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
+    latencySpan2.end(10002, TimeUnit.NANOSECONDS);
     Map