Data-access layer for the run_logs table (ENH-008).
Replaces the O(n²) JSON mutation pattern on runs.logs:
- Every log line is stored as an independent row.
- Writers call
appendLog— a singleINSERT. - Readers call
getByRunIdto retrieve all lines in order.
Schema
run_logs(id AUTOINCREMENT, runId TEXT, seq INT, level TEXT, message TEXT, createdAt TEXT)
Typical flow
// In runLogger.js (called for every log() invocation):
appendLog(run.id, 'info', '[12:34:56] Starting crawl…');
// In SSE route (initial snapshot):
const logs = getByRunId(runId).map(r => r.message);
Exports
appendLog— insert one log rowgetByRunId— fetch all rows for a run, ordered by seqdeleteByRunId— hard-delete all logs for a run (purge path)countByRunId— row count for a run (used in tests)
Members
(inner, constant) _seqCache :Map.<string, number>
runId → next seq value
Type:
- Map.<string, number>
Methods
(static) appendLog(runId, level, message) → {void}
Append a single log line to the run_logs table.
This is the hot path — called on every log() invocation. It executes
a single INSERT and returns immediately.
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string | The run this log line belongs to |
level |
string | 'info' | 'warn' | 'error' |
message |
string | Pre-formatted log string (includes timestamp prefix) |
Returns:
- Type
- void
(static) countByRunId(runId) → {number}
Count log rows for a run. Primarily used in tests to verify write behaviour.
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
Returns:
- Type
- number
(static) deleteByRunId(runId) → {number}
Hard-delete all log rows for a run. Called when a run is permanently purged (recycle-bin purge path).
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
Returns:
Number of rows deleted.
- Type
- number
(static) deleteByRunIds(runIds) → {number}
Hard-delete all log rows for multiple runs (batch purge). Used when a project is purged and all its runs are hard-deleted.
Parameters:
| Name | Type | Description |
|---|---|---|
runIds |
Array.<string> |
Returns:
Total rows deleted.
- Type
- number
(static) evictCache(runId)
Evict a single run from the in-process seq cache. Called when a run reaches a terminal state (completed, failed, aborted) so the cache doesn't grow unboundedly on long-running servers.
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
(static) getByRunId(runId) → {Array.<RunLogRow>}
Fetch all log rows for a run, ordered by seq ascending.
Returns the full row objects so callers can access level for filtering
if needed. For plain string arrays (legacy API compat) use
getByRunId(id).map(r => r.message).
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
Returns:
- Type
- Array.<RunLogRow>
(static) getMessagesByRunId(runId) → {Array.<string>}
Fetch log messages (strings only) for a run, ordered by seq ascending.
Convenience wrapper over getByRunId for callers that only need
the message strings (e.g. the SSE snapshot and the run-detail REST endpoint).
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
Returns:
- Type
- Array.<string>
(inner) nextSeq(db, runId) → {number}
Return the next sequence number for runId and advance the cache.
Falls back to a DB MAX query on first access so restarts are safe.
Parameters:
| Name | Type | Description |
|---|---|---|
db |
Object | — better-sqlite3 Database instance |
runId |
string |
Returns:
- Type
- number
Type Definitions
RunLogRow
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
id |
number | Auto-increment primary key |
runId |
string | Foreign key → runs.id |
seq |
number | 1-based sequence number within the run |
level |
string | 'info' | 'warn' | 'error' |
message |
string | Formatted log line (e.g. "[12:34:56] msg") |
createdAt |
string | ISO 8601 timestamp |