-
-
Notifications
You must be signed in to change notification settings - Fork 472
Expand file tree
/
Copy pathDriverSpans.kt
More file actions
118 lines (103 loc) · 4.48 KB
/
Copy pathDriverSpans.kt
File metadata and controls
118 lines (103 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package io.sentry.sqlite
import io.sentry.IScopes
import io.sentry.ISpan
import io.sentry.Instrumenter
import io.sentry.ScopesAdapter
import io.sentry.SentryDate
import io.sentry.SentryLongDate
import io.sentry.SentryNanotimeDate
import io.sentry.SentryStackTraceFactory
import io.sentry.SpanDataConvention
import io.sentry.SpanStatus
import java.util.Date
private const val SQLITE_TRACE_ORIGIN = "auto.db.sqlite"
/**
* Sentinel for extracting a [SentryNanotimeDate]'s underlying [System.nanoTime] value via
* [SentryDate.diff].
*/
private val EMPTY_NANO_TIME = SentryNanotimeDate(Date(0), 0L)
/** Span instrumentation for [SentrySQLiteDriver]. */
internal class DriverSpans(private val scopes: IScopes, private val dbMetadata: DbMetadata) {
private val stackTraceFactory = SentryStackTraceFactory(scopes.options)
/**
* Returns a timestamp in nanoseconds for use with [record]. Timestamp is ns-precise if the active
* parent span uses a [SentryNanotimeDate] (the ordinary case); otherwise it's ms-precise.
*
* Note: Internalizing the start time in [record] would shift spans to end-of-work on the trace
* timeline, which is less desirable; callers capture the start before doing database work and
* pass it back to [record].
*/
fun startTimestamp(): Long =
// Try to retain nanosecond precision + avoid SentryDate allocation...
scopes.span?.computeNanoStartTimestampForChild()
// ...otherwise fall back to millisecond precision + allocate.
?: scopes.options.dateProvider.now().nanoTimestamp()
/** Records a `db.sql.query` span. */
fun record(
sql: String,
startTimestampNanos: Long,
durationNanos: Long,
status: SpanStatus,
throwable: Throwable? = null,
) {
val parent = scopes.span ?: return
val startTimestamp = SentryLongDate(startTimestampNanos)
val endTimestamp = SentryLongDate(startTimestampNanos + durationNanos)
parent.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY).apply {
spanContext.origin = SQLITE_TRACE_ORIGIN
throwable?.let { this.throwable = it }
val isMainThread = scopes.options.threadChecker.isMainThread
setData(SpanDataConvention.BLOCKED_MAIN_THREAD_KEY, isMainThread)
if (isMainThread) {
setData(SpanDataConvention.CALL_STACK_KEY, stackTraceFactory.inAppCallStack)
}
dbMetadata.name?.let { setData(SpanDataConvention.DB_NAME_KEY, it) }
setData(SpanDataConvention.DB_SYSTEM_KEY, dbMetadata.system)
finish(status, endTimestamp)
}
}
companion object {
/**
* Returns [DriverSpans] based on the [fileName] argument passed to
* [SQLiteDriver.open][androidx.sqlite.SQLiteDriver.open].
*/
fun fromFileName(fileName: String, scopes: IScopes = ScopesAdapter.getInstance()): DriverSpans =
DriverSpans(scopes, dbMetadataFromFileName(fileName))
}
}
/**
* Computes a start timestamp with nanosecond precision for the child of the receiver span. Returns
* null if nanosecond precision isn't possible.
*
* Lets us improve the display of spans in the Sentry UI. If timestamps are only ms-precise, the
* Sentry UI will left-align and arbitrarily reorder spans that share the same wall clock ms:
* ```
* (Relative start times out of order)
* ↓
* Parent span ├█████████████┤
* END TRANSACTION ├███┤ 0.33 ms
* BEGIN IMMEDIATE TRANSACTION ├████┤ 0.02 ms
* INSERT INTO `my_db` … ├██┤ 0.30 ms
* ↑
* (All spans share the same ms baseline
* even though their execution was staggered)
* ```
*
* Nanosecond precision ensures proper ordering and lets the spans stagger:
* ```
* Parent span ├█████████████┤
* BEGIN IMMEDIATE TRANSACTION ├████┤ 0.02 ms
* INSERT INTO `my_db` … ├██┤ 0.30 ms
* END TRANSACTION ├███┤ 0.33 ms
* ```
*/
internal fun ISpan.computeNanoStartTimestampForChild(): Long? {
if (startDate !is SentryNanotimeDate) {
return null
}
val parentWallClockNanos = startDate.nanoTimestamp()
val parentMonotonicNanos = startDate.diff(EMPTY_NANO_TIME)
val elapsedSinceParentStart = System.nanoTime() - parentMonotonicNanos
// Return the child's absolute start time.
return parentWallClockNanos + elapsedSinceParentStart
}