mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
logging: make the writer total and harden export
Never let a log-write or export failure reach the uncaught handler: wrap the async writer and crash path, synchronize export with rotation, and tolerate vanished files. Redact the Logcat stack-trace copy, stop leaking rows=0 on writes, shut down the previous writer on re-init, and drop unused API. Strengthen tests for the batched write path, rotation count and throwable redaction.
This commit is contained in:
@@ -2,11 +2,13 @@ package com.subtracks.data.db
|
||||
|
||||
import androidx.room3.migration.Migration
|
||||
import androidx.sqlite.SQLiteConnection
|
||||
import androidx.sqlite.SQLiteDriver
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.sqlite.execSQL
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -21,8 +23,24 @@ class DbLoggingTest {
|
||||
|
||||
@Test
|
||||
fun forwardsConnectionPoolFlag() {
|
||||
val delegate = BundledSQLiteDriver()
|
||||
assertEquals(delegate.hasConnectionPool, LoggingSQLiteDriver(delegate) {}.hasConnectionPool)
|
||||
assertTrue(LoggingSQLiteDriver(FakeDriver(hasConnectionPool = true)) {}.hasConnectionPool)
|
||||
assertFalse(LoggingSQLiteDriver(FakeDriver(hasConnectionPool = false)) {}.hasConnectionPool)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recordsOneExecutionPerBatchedWriteRow() {
|
||||
driver().open(":memory:").use { connection ->
|
||||
connection.prepare("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)").use { it.step() }
|
||||
connection.prepare("INSERT INTO t (name) VALUES (?)").use { statement ->
|
||||
repeat(3) { index ->
|
||||
statement.bindText(1, "row$index")
|
||||
statement.step()
|
||||
statement.reset()
|
||||
statement.clearBindings()
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(3, executions.count { it.sql.startsWith("INSERT") })
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,3 +123,9 @@ class DbLoggingTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeDriver(
|
||||
override val hasConnectionPool: Boolean,
|
||||
) : SQLiteDriver {
|
||||
override fun open(fileName: String): SQLiteConnection = error("open should not be called")
|
||||
}
|
||||
|
||||
@@ -59,6 +59,22 @@ class LogFileStoreTest {
|
||||
assertTrue(text, text.contains("IllegalStateException: kaboom"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun redactsCredentialsInThrowable() {
|
||||
val store = LogFileStore(root, LogLevel.VERBOSE)
|
||||
store.write(
|
||||
LogLevel.ERROR,
|
||||
"http",
|
||||
"failed",
|
||||
IllegalStateException("GET http://alice:secret@host/x?u=bob&p=hunter2 failed"),
|
||||
)
|
||||
store.flush()
|
||||
val text = store.files().single().readText()
|
||||
assertTrue(text, text.contains("REDACTED@host"))
|
||||
assertFalse(text, text.contains("secret"))
|
||||
assertFalse(text, text.contains("hunter2"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dropsBelowMinimumLevel() {
|
||||
val store = LogFileStore(root, LogLevel.WARN)
|
||||
@@ -75,7 +91,7 @@ class LogFileStoreTest {
|
||||
val store = LogFileStore(root, LogLevel.VERBOSE, maxFileBytes = 1)
|
||||
repeat(4) { store.write(LogLevel.INFO, "x", "line $it", null) }
|
||||
store.flush()
|
||||
assertTrue(store.files().size >= 4)
|
||||
assertEquals(4, store.files().size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,6 +143,13 @@ class LogFileStoreTest {
|
||||
assertFalse(isDataStatement("END TRANSACTION"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun classifiesReadStatements() {
|
||||
assertTrue(isReadStatement("select * from songs"))
|
||||
assertTrue(isReadStatement("WITH x AS (SELECT 1) SELECT * FROM x"))
|
||||
assertFalse(isReadStatement("INSERT INTO t VALUES (1)"))
|
||||
}
|
||||
|
||||
private fun execution(
|
||||
milliseconds: Double,
|
||||
sql: String,
|
||||
|
||||
Reference in New Issue
Block a user