diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt index 84b66584a..aca6c6495 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt @@ -56,7 +56,7 @@ internal class EmbraceSpanImpl( false } else { var successful: Boolean - val attemptedStartTimeMs = startTimeMs ?: openTelemetryClock.now().nanosToMillis() + val attemptedStartTimeMs = startTimeMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis() synchronized(startedSpan) { startedSpan.set(spanBuilder.startSpan(attemptedStartTimeMs)) successful = startedSpan.get() != null @@ -74,7 +74,7 @@ internal class EmbraceSpanImpl( false } else { var successful = false - val attemptedEndTimeMs = endTimeMs ?: openTelemetryClock.now().nanosToMillis() + val attemptedEndTimeMs = endTimeMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis() synchronized(startedSpan) { startedSpan.get()?.let { spanToStop -> diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracer.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracer.kt index 4a36fbcad..17a8fe053 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracer.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracer.kt @@ -22,7 +22,7 @@ internal class EmbraceTracer( spanService.startSpan( name = name, parent = parent, - startTimeMs = startTimeMs, + startTimeMs = startTimeMs?.normalizeTimestampAsMillis(), internal = false ) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/InternalTracer.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/InternalTracer.kt index 0612bf6d2..bfc09a2e6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/InternalTracer.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/InternalTracer.kt @@ -26,7 +26,10 @@ internal class InternalTracer( } override fun stopSpan(spanId: String, errorCode: ErrorCode?, endTimeMs: Long?): Boolean = - spanRepository.getSpan(spanId = spanId)?.stop(errorCode = errorCode, endTimeMs = endTimeMs) ?: false + spanRepository.getSpan(spanId = spanId)?.stop( + errorCode = errorCode, + endTimeMs = endTimeMs?.normalizeTimestampAsMillis() + ) ?: false override fun recordSpan( name: String, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt index c64609555..eb1c3b9be 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImplTest.kt @@ -120,6 +120,22 @@ internal class EmbraceSpanImplTest { } } + @Test + fun `starting and stopping span with nanosecond timestamps`() { + with(embraceSpan) { + val expectedStartTimeMs = fakeClock.now() + 99 + val expectedEndTimeMs = fakeClock.now() + 505 + assertTrue(start(startTimeMs = expectedStartTimeMs.millisToNanos())) + assertTrue(stop(errorCode = ErrorCode.FAILURE, endTimeMs = expectedEndTimeMs.millisToNanos())) + assertSnapshot( + expectedStartTimeMs = expectedStartTimeMs, + expectedEndTimeMs = expectedEndTimeMs, + expectedStatus = Span.Status.ERROR + ) + validateStoppedSpan() + } + } + @Test fun `check adding events`() { with(embraceSpan) { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index 59281a380..335f2b73f 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -81,8 +81,13 @@ internal class EmbraceTracerTest { val parent = checkNotNull(embraceTracer.startSpan(name = "test-span")) clock.tick(20L) val childStartTimeMs = clock.now() - 10L - val child = - checkNotNull(embraceTracer.startSpan(name = "child-span", parent = parent, startTimeMs = childStartTimeMs)) + val child = checkNotNull( + embraceTracer.startSpan( + name = "child-span", + parent = parent, + startTimeMs = childStartTimeMs, + ) + ) assertTrue(parent.stop()) assertTrue(child.stop()) val spans = spanSink.flushSpans() @@ -294,6 +299,26 @@ internal class EmbraceTracerTest { } } + @Test + fun `start and stop span with nanosecond timestamp`() { + spanSink.flushSpans() + val expectedStartTimeNanos = clock.nowInNanos() + val span = checkNotNull( + embraceTracer.startSpan( + name = "fallback-span", + parent = null, + startTimeMs = expectedStartTimeNanos + ) + ) + clock.tick(10L) + val expectedEndTimeNanos = clock.nowInNanos() + assertTrue(span.stop(endTimeMs = expectedEndTimeNanos)) + with(verifyPublicSpan("fallback-span")) { + assertEquals(expectedStartTimeNanos, startTimeNanos) + assertEquals(expectedEndTimeNanos, endTimeNanos) + } + } + @Test fun `recording completed spans fallback normalizes timestamps to millis when appropriate`() { val expectedName = "test-span" diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index 88914de39..1cf5f29b1 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -76,6 +76,20 @@ internal class InternalTracerTest { } } + @Test + fun `start and stop span with nanosecond timestamp`() { + spanSink.flushSpans() + val expectedStartTimeNanos = clock.nowInNanos() + val spanId = checkNotNull(internalTracer.startSpan(name = "my-span", startTimeMs = expectedStartTimeNanos)) + clock.tick(10L) + val expectedEndTimeNanos = clock.nowInNanos() + assertTrue(internalTracer.stopSpan(spanId = spanId, endTimeMs = expectedEndTimeNanos)) + with(verifyPublicSpan("my-span")) { + assertEquals(expectedStartTimeNanos, startTimeNanos) + assertEquals(expectedEndTimeNanos, endTimeNanos) + } + } + @Test fun `verify event timestamp fallback`() { spanSink.flushSpans()