Skip to content

Replace manual logWriter WriteStream in ScriptRuntime with a simpler log-writing approach #97

Description

@heavyrubberslave

Context

ScriptRuntime currently manages automation-script console.* output to ${DATA_PATH}/automation-logs/automation.log via a hand-rolled fs.WriteStream (logWriter), with explicit lifecycle logic: openLogWriter() (create stream + await open + destroy-on-error), assign on load(), close on stop(), destroy on create-failure, and a log() writer. This is ~15 lines of stream-lifecycle ceremony.

Separately, script output now also goes to a dedicated pino child logger named AutomationScript (added for cleaner console output).

Idea

Investigate replacing the manual WriteStream lifecycle with something simpler now that a dedicated automation logger exists.

Findings from initial investigation (to save the next person time)

  • A pino child() logger CANNOT target its own file — pino v10's child() uses Object.create(this) and inherits the parent's stdout destination; there's no per-child destination option. So "make automationScriptLogger also write to the file" requires a separate top-level pino() instance with its own pino.destination(), not a child.
  • The GET /automation/log endpoint (getLogController) returns the file verbatim as text/plain (raw message\n lines) via read-last-lines. Any replacement must keep emitting plain-text lines, or the endpoint/frontend regresses. Default pino output is JSON, so a naive pino file destination would break this.
  • Today logWriter opens with flag 'w'truncates on every script run (log shows only current/last run). A pino destination appends by default — behavior change to decide on.
  • pino transports run on a worker thread (thread-stream), complicating the synchronous open/close/truncate guarantees load()/stop() rely on. Would also promote pino-pretty from dev to a runtime dependency if used for plain-message formatting.
  • onConsoleLog also feeds the WebSocket consoleLog event — independent of logWriter, must be preserved regardless of approach.

Candidate approaches

  • Option A — dedicated top-level pino file instance. Removes the manual stream, but adds pino-instance config + plain-message formatting (likely pino-pretty as a runtime dep), worker-thread transport, and an append-vs-truncate decision. Heavier; only justified if we want structured/rotated/multi-sink automation logs later.
  • Option B (recommended starting point) — drop the persistent stream, use direct fs writes. log()fs.appendFileSync(logFilePath, data + '\n'); truncate-per-run → one fs.writeFileSync(path, '') in load(). Removes openLogWriter(), the WriteStream field, close-on-stop, destroy-on-failure — no new dependency, no worker thread, format & endpoint unchanged. Sync write per log line is negligible at observed volumes.

Open decisions

  1. Truncate-per-run (today) vs. append-across-runs?
  2. Option A (pino) vs. Option B (simple fs)?
  3. Should script output continue to stdout via the AutomationScript pino logger, or live only in the file (not interleaved into the app's ops log)?

Acceptance criteria

  • Manual WriteStream lifecycle simplified/removed.
  • GET /automation/log still returns plain-text lines unchanged.
  • WebSocket consoleLog stream preserved.
  • Truncate/append behavior explicitly decided & documented.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions