-
-
Notifications
You must be signed in to change notification settings - Fork 472
Expand file tree
/
Copy pathSQLiteSpanInstrumentation.kt
More file actions
123 lines (108 loc) · 4.55 KB
/
Copy pathSQLiteSpanInstrumentation.kt
File metadata and controls
123 lines (108 loc) · 4.55 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
119
120
121
122
123
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
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(0, 0L)
/** Span instrumentation for [SentrySQLiteDriver]. */
internal class SQLiteSpanInstrumentation(
private val scopes: IScopes,
private val dbMetadata: DbMetadata,
) {
private val stackTraceFactory = SentryStackTraceFactory(scopes.options)
/**
* Returns a timestamp in nanoseconds for use with [recordSpan]. 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 [recordSpan] 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 [recordSpan].
*/
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 recordSpan(
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 [SQLiteSpanInstrumentation] based on the [fileName] argument passed to
* [SQLiteDriver.open][androidx.sqlite.SQLiteDriver.open].
*/
fun fromFileName(
fileName: String,
scopes: IScopes = ScopesAdapter.getInstance(),
): SQLiteSpanInstrumentation =
SQLiteSpanInstrumentation(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
}