Skip to content

fix: logging stops silently after a regtest wipe #677

Description

@jvsena42

On regtest, a wallet wipe deletes the log directory, and nothing recreates it for the rest of the app process. Every Logger call after a wipe silently writes nothing, so the wipe-then-restore flow — the one most worth having logs for — produces no log file at all.

Cause

AppReset.wipeLogs removes the whole directory (Bitkit/Utilities/AppReset.swift:75-83):

private static func wipeLogs() throws {
    let path = Env.logDirectory
    if FileManager.default.fileExists(atPath: path) {
        Logger.warn("Wiping entire logs directory...")
        try FileManager.default.removeItem(at: URL(fileURLWithPath: path))
    }
}

Logger.sessionLogFile is a static let (Bitkit/Utilities/Logger.swift:10), so it resolves once per process, and creating the directory happens only inside that lazy initializer — which already ran at launch. After the wipe, writeToFile falls to the else branch and calls logMessage.write(toFile:atomically:), which fails because the parent directory no longer exists. The error is caught and only printed (Logger.swift:96-99), so nothing surfaces:

} catch {
    print("Failed to write to log file: \(error)")
}

Since a wipe and the restore that follows it happen in the same process, that entire flow goes unlogged. The directory only comes back on the next launch, when sessionLogFile is resolved again in a fresh process.

Impact

Debugging anything in the wipe/restore path currently requires attaching to the console, because the on-disk logs for that flow do not exist. This was hit while investigating a hardware wallet activity bug: the app group had no logs directory whatsoever, and the restore had to be reconstructed from the SQLite database instead. With the patch below applied, the very next run produced a complete session log and the root cause was visible immediately.

Regtest only, since wipeLogs() is gated on Env.network == .regtest — so it affects development and QA rather than production.

Suggested fix

Recreate the directory in writeToFile before writing. It runs on Logger.queue, and is a no-op on every normal write:

let logFilePath = sessionLogFile

// A regtest wipe deletes the whole log directory mid-process (`AppReset.wipeLogs`), but
// `sessionLogFile` is a `static let` that created the directory when it was first resolved.
// Without recreating it here, every write for the rest of the process fails silently — so
// a wipe followed by a restore, all in one process, is never logged at all.
let directory = URL(fileURLWithPath: logFilePath).deletingLastPathComponent()
if !FileManager.default.fileExists(atPath: directory.path) {
    try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
}

// Write to file
if FileManager.default.fileExists(atPath: logFilePath) {

This was verified locally: applied to a build, wiped, and a session log appeared at <app-group>/logs/bitkit_foreground_*.log covering the post-wipe flow. It was not committed anywhere, so it needs picking up separately.

An alternative worth considering is having wipeLogs() recreate the directory itself after removing it, which keeps the concern next to the deletion. The writeToFile guard is the more robust of the two, since it also covers the directory disappearing for any other reason.

Reproduction

  1. Run a regtest build and confirm <app-group>/logs/ contains a session log.
  2. Wipe the wallet from settings.
  3. Without relaunching, do anything that logs, such as restoring from backup.
  4. <app-group>/logs/ is gone and nothing from step 3 was recorded.

Related

Found while investigating synonymdev/bitkit-core#137, during work on #675.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions