diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dbcde58f10..0de5fc72452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ ### Improvements - Reduce boxing to improve performance ([#5523](https://github.com/getsentry/sentry-java/pull/5523), [#5527](https://github.com/getsentry/sentry-java/pull/5527), [#5551](https://github.com/getsentry/sentry-java/pull/5551)) +- Replace `Date` with a unix timestamp in `SentryNanotimeDate` to improve performance ([#5550](https://github.com/getsentry/sentry-java/pull/5550)) + - `SentryNanotimeDate` is now marked `@ApiStatus.Internal`. A new `(long unixDateMillis, long nanos)` constructor was added, where `unixDateMillis` is milliseconds since the epoch. The existing `(Date, long)` constructor is retained but deprecated. ### Dependencies diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 19cee7fcce5..8a891926341 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Collections; -import java.util.Date; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.Future; @@ -94,7 +93,7 @@ public final class ActivityLifecycleIntegration private final @NotNull WeakHashMap ttfdSpanMap = new WeakHashMap<>(); private final @NotNull WeakHashMap activitySpanHelpers = new WeakHashMap<>(); - private @NotNull SentryDate lastPausedTime = new SentryNanotimeDate(new Date(0), 0); + private @NotNull SentryDate lastPausedTime = new SentryNanotimeDate(0, 0); private @Nullable Future ttfdAutoCloseFuture = null; // WeakHashMap isn't thread safe but ActivityLifecycleCallbacks is only called from the @@ -729,7 +728,7 @@ public void onActivityDestroyed(final @NotNull Activity activity) { private void clear() { firstActivityCreated = false; - lastPausedTime = new SentryNanotimeDate(new Date(0), 0); + lastPausedTime = new SentryNanotimeDate(0, 0); activitySpanHelpers.clear(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java index a83454d29b7..074a4a6ea51 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java @@ -13,7 +13,6 @@ import io.sentry.android.core.internal.util.SentryFrameMetricsCollector; import io.sentry.protocol.MeasurementValue; import io.sentry.util.AutoClosableReentrantLock; -import java.util.Date; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; @@ -33,7 +32,7 @@ public class SpanFrameMetricsCollector // grow indefinitely in case of a long running span private static final int MAX_FRAMES_COUNT = 3600; private static final long ONE_SECOND_NANOS = TimeUnit.SECONDS.toNanos(1); - private static final SentryNanotimeDate EMPTY_NANO_TIME = new SentryNanotimeDate(new Date(0), 0); + private static final SentryNanotimeDate EMPTY_NANO_TIME = new SentryNanotimeDate(0, 0); private final boolean enabled; protected final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index f2ffb4b4b96..19f43432bef 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -41,7 +41,6 @@ import io.sentry.protocol.SentryId import io.sentry.protocol.TransactionNameSource import io.sentry.test.DeferredExecutorService import io.sentry.test.getProperty -import java.util.Date import java.util.concurrent.Future import java.util.concurrent.TimeUnit import kotlin.test.AfterTest @@ -936,7 +935,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) sut.setFirstActivityCreated(false) - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime(date) fixture.options.dateProvider = SentryDateProvider { date } @@ -961,7 +960,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) sut.setFirstActivityCreated(false) - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime(date) val activity = mock() @@ -984,8 +983,8 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) sut.setFirstActivityCreated(false) - val date = SentryNanotimeDate(Date(1), 0) - val date2 = SentryNanotimeDate(Date(2), 2) + val date = SentryNanotimeDate(1, 0) + val date2 = SentryNanotimeDate(2, 2) setAppStartTime(date) val activity = mock() @@ -1011,7 +1010,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut { it.tracesSampleRate = 1.0 } sut.register(fixture.scopes, fixture.options) sut.setFirstActivityCreated(true) - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime(date) val activity = mock() @@ -1030,8 +1029,8 @@ class ActivityLifecycleIntegrationTest { sut.setFirstActivityCreated(false) // usually set by SentryPerformanceProvider - val date = SentryNanotimeDate(Date(1), 0) - val date2 = SentryNanotimeDate(Date(2), 2) + val date = SentryNanotimeDate(1, 0) + val date2 = SentryNanotimeDate(2, 2) val activity = mock() // Activity onCreate date will be used @@ -1056,7 +1055,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) // usually set by SentryPerformanceProvider - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime(date) val activity = mock() @@ -1080,7 +1079,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) // usually set by SentryPerformanceProvider - val startDate = SentryNanotimeDate(Date(1), 0) + val startDate = SentryNanotimeDate(1, 0) setAppStartTime(startDate) val appStartMetrics = AppStartMetrics.getInstance() appStartMetrics.appStartType = AppStartType.WARM @@ -1113,9 +1112,9 @@ class ActivityLifecycleIntegrationTest { it.isEnableStandaloneAppStartTracing = true } sut.register(fixture.scopes, fixture.options) - val firstFrameDate = SentryNanotimeDate(Date(1499), 0) + val firstFrameDate = SentryNanotimeDate(1499, 0) fixture.options.dateProvider = SentryDateProvider { firstFrameDate } - setAppStartTime(SentryNanotimeDate(Date(1), 0)) + setAppStartTime(SentryNanotimeDate(1, 0)) val activity = mock() sut.onActivityPreCreated(activity, fixture.bundle) @@ -1168,8 +1167,8 @@ class ActivityLifecycleIntegrationTest { it.isEnableStandaloneAppStartTracing = true } sut.register(fixture.scopes, fixture.options) - val appStartEndDate = SentryNanotimeDate(Date(499), 0) - setAppStartTime(SentryNanotimeDate(Date(1), 0), appStartEndDate) + val appStartEndDate = SentryNanotimeDate(499, 0) + setAppStartTime(SentryNanotimeDate(1, 0), appStartEndDate) val activity = mock() sut.onActivityPreCreated(activity, fixture.bundle) @@ -1230,9 +1229,9 @@ class ActivityLifecycleIntegrationTest { AppStartMetrics.getInstance().appStartSentryTraceHeader = SentryTraceHeader(storedTraceId, SpanId(), true).value // headless start ended right before the activity opens - AppStartMetrics.getInstance().appStartEndTime = SentryNanotimeDate(Date(0), 0) + AppStartMetrics.getInstance().appStartEndTime = SentryNanotimeDate(0, 0) sut.register(fixture.scopes, fixture.options) - setAppStartTime(date = SentryNanotimeDate(Date(1), 0)) + setAppStartTime(date = SentryNanotimeDate(1, 0)) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1254,9 +1253,9 @@ class ActivityLifecycleIntegrationTest { AppStartMetrics.getInstance().appStartSentryTraceHeader = SentryTraceHeader(storedTraceId, SpanId(), true).value // headless start ended at epoch, but the activity opens more than a minute later - AppStartMetrics.getInstance().appStartEndTime = SentryNanotimeDate(Date(0), 0) + AppStartMetrics.getInstance().appStartEndTime = SentryNanotimeDate(0, 0) sut.register(fixture.scopes, fixture.options) - setAppStartTime(date = SentryNanotimeDate(Date(TimeUnit.MINUTES.toMillis(2)), 0)) + setAppStartTime(date = SentryNanotimeDate(TimeUnit.MINUTES.toMillis(2), 0)) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1389,7 +1388,7 @@ class ActivityLifecycleIntegrationTest { // usually done by SentryPerformanceProvider, if disabled it's done by // SentryAndroid.init - val startDate = SentryNanotimeDate(Date(1), 0) + val startDate = SentryNanotimeDate(1, 0) setAppStartTime(startDate) AppStartMetrics.getInstance().appStartType = AppStartType.WARM @@ -1415,7 +1414,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider - val startDate = SentryNanotimeDate(Date(1), 0) + val startDate = SentryNanotimeDate(1, 0) setAppStartTime(startDate) AppStartMetrics.getInstance().appStartType = AppStartType.WARM AppStartMetrics.getInstance().sdkInitTimeSpan.setStoppedAt(1234) @@ -1439,7 +1438,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider - val startDate = SentryNanotimeDate(Date(1), 0) + val startDate = SentryNanotimeDate(1, 0) setAppStartTime(startDate) AppStartMetrics.getInstance().appStartType = AppStartType.WARM @@ -1474,7 +1473,7 @@ class ActivityLifecycleIntegrationTest { sut.register(fixture.scopes, fixture.options) sut.setFirstActivityCreated(true) - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime() val activity = mock() @@ -1988,14 +1987,14 @@ class ActivityLifecycleIntegrationTest { @Test fun `When sentry is initialized mid activity lifecycle, last paused time should be used in favor of app start time`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) - val now = SentryNanotimeDate(Date(1234), 456) + val now = SentryNanotimeDate(1234, 456) fixture.options.tracesSampleRate = 1.0 fixture.options.dateProvider = SentryDateProvider { now } sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider - val startDate = SentryNanotimeDate(Date(5678), 910) + val startDate = SentryNanotimeDate(5678, 910) setAppStartTime(startDate) AppStartMetrics.getInstance().appStartType = AppStartType.COLD @@ -2019,7 +2018,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 sut.register(fixture.scopes, fixture.options) - val date = SentryNanotimeDate(Date(1), 0) + val date = SentryNanotimeDate(1, 0) setAppStartTime(date) assertTrue(sut.activitySpanHelpers.isEmpty()) @@ -2036,8 +2035,8 @@ class ActivityLifecycleIntegrationTest { fun `Creates activity lifecycle spans`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - val appStartDate = SentryNanotimeDate(Date(1), 0) - val startDate = SentryNanotimeDate(Date(2), 0) + val appStartDate = SentryNanotimeDate(1, 0) + val startDate = SentryNanotimeDate(2, 0) val appStartMetrics = AppStartMetrics.getInstance() val activity = mock() fixture.options.dateProvider = SentryDateProvider { startDate } @@ -2076,7 +2075,7 @@ class ActivityLifecycleIntegrationTest { fun `Creates activity lifecycle spans even when no app start span is available`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - val startDate = SentryNanotimeDate(Date(2), 0) + val startDate = SentryNanotimeDate(2, 0) val appStartMetrics = AppStartMetrics.getInstance() val activity = mock() fixture.options.dateProvider = SentryDateProvider { startDate } @@ -2134,8 +2133,8 @@ class ActivityLifecycleIntegrationTest { fun `Creates activity lifecycle spans on API lower than 29`() { val sut = fixture.getSut(apiVersion = Build.VERSION_CODES.P) fixture.options.tracesSampleRate = 1.0 - val appStartDate = SentryNanotimeDate(Date(1), 0) - val startDate = SentryNanotimeDate(Date(2), 0) + val appStartDate = SentryNanotimeDate(1, 0) + val startDate = SentryNanotimeDate(2, 0) val appStartMetrics = AppStartMetrics.getInstance() val activity = mock() fixture.options.dateProvider = SentryDateProvider { startDate } @@ -2187,8 +2186,8 @@ class ActivityLifecycleIntegrationTest { fun `Does not add activity lifecycle spans when firstActivityCreated is true`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - val appStartDate = SentryNanotimeDate(Date(1), 0) - val startDate = SentryNanotimeDate(Date(2), 0) + val appStartDate = SentryNanotimeDate(1, 0) + val startDate = SentryNanotimeDate(2, 0) val appStartMetrics = AppStartMetrics.getInstance() val activity = mock() fixture.options.dateProvider = SentryDateProvider { startDate } @@ -2209,7 +2208,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is false and app start span has stopped, restart app start to current date`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - val appStartDate = SentryNanotimeDate(Date(1), 0) + val appStartDate = SentryNanotimeDate(1, 0) val appStartMetrics = AppStartMetrics.getInstance() val activity = mock() setAppStartTime(appStartDate) @@ -2290,7 +2289,7 @@ class ActivityLifecycleIntegrationTest { } private fun setAppStartTime( - date: SentryDate = SentryNanotimeDate(Date(1), 0), + date: SentryDate = SentryNanotimeDate(1, 0), stopDate: SentryDate? = null, ) { // set by SentryPerformanceProvider so forcing it here diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt index 4f6ba7fc5f0..711f5f7fe0b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt @@ -5,7 +5,6 @@ import android.net.Network import android.net.NetworkCapabilities import android.os.Build import io.sentry.Breadcrumb -import io.sentry.DateUtils import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SentryDateProvider @@ -54,8 +53,9 @@ class NetworkBreadcrumbsIntegrationTest { executorService = executor isEnableNetworkEventBreadcrumbs = enableNetworkEventBreadcrumbs dateProvider = SentryDateProvider { - val nowNanos = TimeUnit.MILLISECONDS.toNanos(nowMs ?: System.currentTimeMillis()) - SentryNanotimeDate(DateUtils.nanosToDate(nowNanos), nowNanos) + val nowMillis = nowMs ?: System.currentTimeMillis() + val nowNanos = TimeUnit.MILLISECONDS.toNanos(nowMillis) + SentryNanotimeDate(nowMillis, nowNanos) } } return NetworkBreadcrumbsIntegration(context, buildInfo) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt index e5d7349d37c..2b6f19a8d31 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt @@ -8,7 +8,6 @@ import io.sentry.SentryNanotimeDate import io.sentry.SpanContext import io.sentry.android.core.internal.util.SentryFrameMetricsCollector import io.sentry.protocol.MeasurementValue -import java.util.Date import java.util.UUID import java.util.concurrent.TimeUnit import kotlin.test.Test @@ -50,11 +49,12 @@ class SpanFrameMetricsCollectorTest { val span = mock() val spanContext = SpanContext("op.fake") whenever(span.spanContext).thenReturn(spanContext) - whenever(span.startDate).thenReturn(SentryNanotimeDate(Date(), startTimeStampNanos)) + whenever(span.startDate) + .thenReturn(SentryNanotimeDate(System.currentTimeMillis(), startTimeStampNanos)) whenever(span.finishDate) .thenReturn( if (endTimeStampNanos != null) { - SentryNanotimeDate(Date(), endTimeStampNanos) + SentryNanotimeDate(System.currentTimeMillis(), endTimeStampNanos) } else { null } @@ -69,11 +69,12 @@ class SpanFrameMetricsCollectorTest { val span = mock() val spanContext = SpanContext("op.fake") whenever(span.spanContext).thenReturn(spanContext) - whenever(span.startDate).thenReturn(SentryNanotimeDate(Date(), startTimeStampNanos)) + whenever(span.startDate) + .thenReturn(SentryNanotimeDate(System.currentTimeMillis(), startTimeStampNanos)) whenever(span.finishDate) .thenReturn( if (endTimeStampNanos != null) { - SentryNanotimeDate(Date(), endTimeStampNanos) + SentryNanotimeDate(System.currentTimeMillis(), endTimeStampNanos) } else { null } @@ -438,8 +439,8 @@ class SpanFrameMetricsCollectorTest { @Test fun `SentryNanoDate diff does nano precision`() { // having this in here, as SpanFrameMetricsCollector relies on this behavior - val a = SentryNanotimeDate(Date(1234), 567) - val b = SentryNanotimeDate(Date(1234), 0) + val a = SentryNanotimeDate(1234, 567) + val b = SentryNanotimeDate(1234, 0) assertEquals(567, a.diff(b)) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelperTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelperTest.kt index 710fc835acd..ef048978795 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelperTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelperTest.kt @@ -12,7 +12,6 @@ import io.sentry.SpanDataConvention import io.sentry.SpanOptions import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext -import java.util.Date import java.util.concurrent.TimeUnit import kotlin.test.BeforeTest import kotlin.test.Test @@ -31,8 +30,8 @@ class ActivityLifecycleSpanHelperTest { val appStartSpan: ISpan val scopes = mock() val options = SentryOptions() - val date = SentryNanotimeDate(Date(1), 1000000) - val endDate = SentryNanotimeDate(Date(3), 3000000) + val date = SentryNanotimeDate(1, 1000000) + val endDate = SentryNanotimeDate(3, 3000000) init { whenever(scopes.options).thenReturn(options) @@ -59,7 +58,7 @@ class ActivityLifecycleSpanHelperTest { @Test fun `createAndStopOnCreateSpan creates and finishes onCreate span`() { val helper = fixture.getSut() - val date = SentryNanotimeDate(Date(1), 1) + val date = SentryNanotimeDate(1, 1) helper.setOnCreateStartTimestamp(date) helper.createAndStopOnCreateSpan(fixture.appStartSpan) @@ -99,7 +98,7 @@ class ActivityLifecycleSpanHelperTest { @Test fun `createAndStopOnStartSpan creates and finishes onStart span`() { val helper = fixture.getSut() - val date = SentryNanotimeDate(Date(1), 1) + val date = SentryNanotimeDate(1, 1) helper.setOnStartStartTimestamp(date) helper.createAndStopOnStartSpan(fixture.appStartSpan) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt index ab0013a8c75..2737785349f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt @@ -18,7 +18,6 @@ import io.sentry.android.core.CurrentActivityHolder import io.sentry.android.core.SentryAndroidOptions import io.sentry.android.core.SentryShadowProcess import io.sentry.protocol.SentryId -import java.util.Date import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger import kotlin.test.Test @@ -639,7 +638,7 @@ class AppStartMetricsTest { @Test fun `createProcessInitSpan creates a span`() { val appStartMetrics = AppStartMetrics.getInstance() - val startDate = SentryNanotimeDate(Date(1), 1000000) + val startDate = SentryNanotimeDate(1, 1000000) appStartMetrics.classLoadedUptimeMs = 10 val startMillis = DateUtils.nanosToMillis(startDate.nanoTimestamp().toDouble()).toLong() appStartMetrics.appStartTimeSpan.setStartedAt(1) diff --git a/sentry-apache-http-client-5/src/test/kotlin/io/sentry/transport/apache/ApacheHttpClientTransportTest.kt b/sentry-apache-http-client-5/src/test/kotlin/io/sentry/transport/apache/ApacheHttpClientTransportTest.kt index 639dd4e0513..9f5c9b910ad 100644 --- a/sentry-apache-http-client-5/src/test/kotlin/io/sentry/transport/apache/ApacheHttpClientTransportTest.kt +++ b/sentry-apache-http-client-5/src/test/kotlin/io/sentry/transport/apache/ApacheHttpClientTransportTest.kt @@ -213,7 +213,7 @@ class ApacheHttpClientTransportTest { val now = Date(9001) val sut = fixture.getSut() fixture.options.dateProvider = mock() - whenever(fixture.options.dateProvider.now()).thenReturn(SentryNanotimeDate(now, 0)) + whenever(fixture.options.dateProvider.now()).thenReturn(SentryNanotimeDate(now.time, 0)) val envelope = SentryEnvelope.from(fixture.options.serializer, SentryEvent(), null) sut.send(envelope) @@ -226,7 +226,7 @@ class ApacheHttpClientTransportTest { val now = Date(9001) val sut = fixture.getSut() fixture.options.dateProvider = mock() - whenever(fixture.options.dateProvider.now()).thenReturn(SentryNanotimeDate(now, 0)) + whenever(fixture.options.dateProvider.now()).thenReturn(SentryNanotimeDate(now.time, 0)) val envelope = SentryEnvelope.from(fixture.options.serializer, SentryEvent(), null) sut.send(envelope, Hint()) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 22f9366f738..e9083350349 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -382,7 +382,6 @@ public final class io/sentry/DataCategory : java/lang/Enum { } public final class io/sentry/DateUtils { - public static fun dateToNanos (Ljava/util/Date;)J public static fun dateToSeconds (Ljava/util/Date;)D public static fun doubleToBigDecimal (D)Ljava/math/BigDecimal; public static fun getCurrentDateTime ()Ljava/util/Date; @@ -3558,6 +3557,7 @@ public final class io/sentry/SentryMetricsEvents$JsonKeys { public final class io/sentry/SentryNanotimeDate : io/sentry/SentryDate { public fun ()V + public fun (JJ)V public fun (Ljava/util/Date;J)V public fun compareTo (Lio/sentry/SentryDate;)I public synthetic fun compareTo (Ljava/lang/Object;)I diff --git a/sentry/src/main/java/io/sentry/DateUtils.java b/sentry/src/main/java/io/sentry/DateUtils.java index b86bddeaad8..e407391c394 100644 --- a/sentry/src/main/java/io/sentry/DateUtils.java +++ b/sentry/src/main/java/io/sentry/DateUtils.java @@ -151,17 +151,6 @@ public static double dateToSeconds(final @NotNull Date date) { return millisToSeconds(date.getTime()); } - /** - * Convert {@link Date} to nanoseconds represented as {@link Long}. - * - * @param date - date - * @return nanoseconds - */ - @SuppressWarnings("JavaUtilDate") - public static long dateToNanos(final @NotNull Date date) { - return millisToNanos(date.getTime()); - } - public static long secondsToNanos(final @NotNull long seconds) { return seconds * (1000L * 1000L * 1000L); } diff --git a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java index 98c46ad5325..f3abf3518f7 100644 --- a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java +++ b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java @@ -1,32 +1,43 @@ package io.sentry; import java.util.Date; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * Uses {@link Date} in combination with System.nanoTime(). + * Uses a unix timestamp (milliseconds since the epoch) in combination with System.nanoTime(). * - *

A single date only offers millisecond precision but diff can be calculated with up to + *

The unix timestamp only offers millisecond precision but diff can be calculated with up to * nanosecond precision. This increased precision can also be used to calculate a new end date for a * transaction where start date is sent with ms precision and end date is added to it with ns * precision leading to an end timestamp with ns precision that can be used to gain ns precision * transaction durations. * *

This is a workaround for older versions of Java (before 9) and Android API (lower than 26) - * that allows for higher precision than {@link Date} alone would. + * that allows for higher precision than a millisecond timestamp alone would. */ +@ApiStatus.Internal public final class SentryNanotimeDate extends SentryDate { - private final @NotNull Date date; + private final long unixDateMillis; private final long nanos; public SentryNanotimeDate() { - this(DateUtils.getCurrentDateTime(), System.nanoTime()); + this(System.currentTimeMillis(), System.nanoTime()); } + /** + * @deprecated use {@link SentryNanotimeDate#SentryNanotimeDate(long, long)} instead. + */ + @Deprecated + @SuppressWarnings({"InlineMeSuggester", "JavaUtilDate"}) public SentryNanotimeDate(final @NotNull Date date, final long nanos) { - this.date = date; + this(date.getTime(), nanos); + } + + public SentryNanotimeDate(final long unixDateMillis, final long nanos) { + this.unixDateMillis = unixDateMillis; this.nanos = nanos; } @@ -41,7 +52,7 @@ public long diff(final @NotNull SentryDate otherDate) { @Override public long nanoTimestamp() { - return DateUtils.dateToNanos(date); + return DateUtils.millisToNanos(unixDateMillis); } @Override @@ -63,8 +74,8 @@ public long laterDateNanosTimestampByDiff(final @Nullable SentryDate otherDate) public int compareTo(@NotNull SentryDate otherDate) { if (otherDate instanceof SentryNanotimeDate) { final @NotNull SentryNanotimeDate otherNanoDate = (SentryNanotimeDate) otherDate; - final long thisDateMillis = date.getTime(); - final long otherDateMillis = otherNanoDate.date.getTime(); + final long thisDateMillis = unixDateMillis; + final long otherDateMillis = otherNanoDate.unixDateMillis; if (thisDateMillis == otherDateMillis) { return Long.compare(nanos, otherNanoDate.nanos); } else { diff --git a/sentry/src/test/java/io/sentry/DefaultCompositePerformanceCollectorTest.kt b/sentry/src/test/java/io/sentry/DefaultCompositePerformanceCollectorTest.kt index ceec3571ebd..f8e3a8f9f98 100644 --- a/sentry/src/test/java/io/sentry/DefaultCompositePerformanceCollectorTest.kt +++ b/sentry/src/test/java/io/sentry/DefaultCompositePerformanceCollectorTest.kt @@ -4,7 +4,6 @@ import io.sentry.test.getCtor import io.sentry.test.getProperty import io.sentry.test.injectForField import io.sentry.util.thread.ThreadChecker -import java.util.Date import java.util.Timer import java.util.concurrent.TimeUnit import kotlin.test.Test @@ -188,14 +187,8 @@ class DefaultCompositePerformanceCollectorTest { val mockCollector = mock() val dates = listOf( - SentryNanotimeDate( - Date().apply { time = TimeUnit.SECONDS.toMillis(100) }, - TimeUnit.SECONDS.toNanos(100), - ), - SentryNanotimeDate( - Date().apply { time = TimeUnit.SECONDS.toMillis(131) }, - TimeUnit.SECONDS.toNanos(131), - ), + SentryNanotimeDate(TimeUnit.SECONDS.toMillis(100), TimeUnit.SECONDS.toNanos(100)), + SentryNanotimeDate(TimeUnit.SECONDS.toMillis(131), TimeUnit.SECONDS.toNanos(131)), ) whenever(mockDateProvider.now()).thenReturn(dates[0], dates[0], dates[0], dates[1]) val collector = @@ -226,14 +219,8 @@ class DefaultCompositePerformanceCollectorTest { val mockDateProvider = mock() val dates = listOf( - SentryNanotimeDate( - Date().apply { time = TimeUnit.SECONDS.toMillis(100) }, - TimeUnit.SECONDS.toNanos(100), - ), - SentryNanotimeDate( - Date().apply { time = TimeUnit.SECONDS.toMillis(130) }, - TimeUnit.SECONDS.toNanos(130), - ), + SentryNanotimeDate(TimeUnit.SECONDS.toMillis(100), TimeUnit.SECONDS.toNanos(100)), + SentryNanotimeDate(TimeUnit.SECONDS.toMillis(130), TimeUnit.SECONDS.toNanos(130)), ) whenever(mockDateProvider.now()).thenReturn(dates[0], dates[0], dates[0], dates[1]) val collector = fixture.getSut { it.dateProvider = mockDateProvider } diff --git a/sentry/src/test/java/io/sentry/SentryNanotimeDateTest.kt b/sentry/src/test/java/io/sentry/SentryNanotimeDateTest.kt index 86464bcedba..3f7a5dca8b6 100644 --- a/sentry/src/test/java/io/sentry/SentryNanotimeDateTest.kt +++ b/sentry/src/test/java/io/sentry/SentryNanotimeDateTest.kt @@ -1,20 +1,19 @@ package io.sentry -import java.util.Date import kotlin.test.Test import kotlin.test.assertEquals class SentryNanotimeDateTest { @Test fun `doubleValue only offers ms precision`() { - val date = SentryNanotimeDate(Date(1672742031123), 123456789) + val date = SentryNanotimeDate(1672742031123, 123456789) assertEquals(1672742031123000000L, date.nanoTimestamp()) } @Test fun `laterDateNanosByDiff offers ns precision`() { - val startDate = SentryNanotimeDate(Date(1672742031123), 456788) - val finishDate = SentryNanotimeDate(Date(1672742031123), 456789) + val startDate = SentryNanotimeDate(1672742031123, 456788) + val finishDate = SentryNanotimeDate(1672742031123, 456789) val dateInSeconds = startDate.laterDateNanosTimestampByDiff(finishDate) assertEquals(1672742031123000001L, dateInSeconds) } @@ -26,7 +25,7 @@ class SentryNanotimeDateTest { */ @Test fun `laterDateNanosByDiff with SentryLongDate gives ms precision`() { - val startDate = SentryNanotimeDate(Date(1672742031123), 456789) + val startDate = SentryNanotimeDate(1672742031123, 456789) val finishDate = SentryLongDate(61633553039) val dateInSeconds = startDate.laterDateNanosTimestampByDiff(finishDate) assertEquals(1672742031123000000L, dateInSeconds) @@ -36,36 +35,36 @@ class SentryNanotimeDateTest { @Test fun `compareTo() with equal dates returns 0`() { - val date1 = SentryNanotimeDate(Date(1672742031123), 456789) - val date2 = SentryNanotimeDate(Date(1672742031123), 456789) + val date1 = SentryNanotimeDate(1672742031123, 456789) + val date2 = SentryNanotimeDate(1672742031123, 456789) assertEquals(0, date1.compareTo(date2)) } @Test fun `compareTo() returns -1 for earlier ns`() { - val date1 = SentryNanotimeDate(Date(1672742031123), 456788) - val date2 = SentryNanotimeDate(Date(1672742031123), 456789) + val date1 = SentryNanotimeDate(1672742031123, 456788) + val date2 = SentryNanotimeDate(1672742031123, 456789) assertEquals(-1, date1.compareTo(date2)) } @Test fun `compareTo() returns 1 for later ns`() { - val date1 = SentryNanotimeDate(Date(1672742031123), 456789) - val date2 = SentryNanotimeDate(Date(1672742031123), 456788) + val date1 = SentryNanotimeDate(1672742031123, 456789) + val date2 = SentryNanotimeDate(1672742031123, 456788) assertEquals(1, date1.compareTo(date2)) } @Test fun `compareTo() returns -1 for earlier date`() { - val date1 = SentryNanotimeDate(Date(1672742030123), 456789) - val date2 = SentryNanotimeDate(Date(1672742031123), 456789) + val date1 = SentryNanotimeDate(1672742030123, 456789) + val date2 = SentryNanotimeDate(1672742031123, 456789) assertEquals(-1, date1.compareTo(date2)) } @Test fun `compareTo() returns 1 for later date`() { - val date1 = SentryNanotimeDate(Date(1672742031123), 456789) - val date2 = SentryNanotimeDate(Date(1672742030123), 456789) + val date1 = SentryNanotimeDate(1672742031123, 456789) + val date2 = SentryNanotimeDate(1672742030123, 456789) assertEquals(1, date1.compareTo(date2)) } } diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 1ccbcf2f318..3b808dd2220 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -8,7 +8,6 @@ import io.sentry.test.getProperty import io.sentry.util.thread.IThreadChecker import java.time.LocalDateTime import java.time.ZoneOffset -import java.util.Date import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -184,7 +183,7 @@ class SentryTracerTest { val tracer = fixture.getSut() val date = SentryNanotimeDate( - Date.from(LocalDateTime.of(2022, 12, 24, 23, 59, 58, 0).toInstant(ZoneOffset.UTC)), + LocalDateTime.of(2022, 12, 24, 23, 59, 58, 0).toInstant(ZoneOffset.UTC).toEpochMilli(), 0, ) tracer.finish(SpanStatus.ABORTED, date) @@ -643,7 +642,7 @@ class SentryTracerTest { @Test fun `when startTimestamp is given, use it as startTimestamp`() { - val date = SentryNanotimeDate(Date(0), 0) + val date = SentryNanotimeDate(0, 0) val transaction = fixture.getSut(startTimestamp = date) assertSame(date, transaction.startDate) diff --git a/sentry/src/test/java/io/sentry/transport/AsyncHttpTransportTest.kt b/sentry/src/test/java/io/sentry/transport/AsyncHttpTransportTest.kt index 70092ffa7ba..6f711cfedbf 100644 --- a/sentry/src/test/java/io/sentry/transport/AsyncHttpTransportTest.kt +++ b/sentry/src/test/java/io/sentry/transport/AsyncHttpTransportTest.kt @@ -367,7 +367,7 @@ class AsyncHttpTransportTest { // given val now = Date(9001) fixture.sentryOptions.dateProvider = mock() - whenever(fixture.sentryOptions.dateProvider.now()).thenReturn(SentryNanotimeDate(now, 0)) + whenever(fixture.sentryOptions.dateProvider.now()).thenReturn(SentryNanotimeDate(now.time, 0)) val envelope = SentryEnvelope.from(fixture.sentryOptions.serializer, createSession(), null) whenever(fixture.transportGate.isConnected).thenReturn(true) @@ -387,7 +387,7 @@ class AsyncHttpTransportTest { // given val now = Date(9001) fixture.sentryOptions.dateProvider = mock() - whenever(fixture.sentryOptions.dateProvider.now()).thenReturn(SentryNanotimeDate(now, 0)) + whenever(fixture.sentryOptions.dateProvider.now()).thenReturn(SentryNanotimeDate(now.time, 0)) val envelope = SentryEnvelope.from(fixture.sentryOptions.serializer, createSession(), null) whenever(fixture.transportGate.isConnected).thenReturn(true)