Summary
Vigilante currently implements daemon logging through custom printf-style helpers such as state.AppendDaemonLog and environment.LoggingRunner.Logf instead of Go's standard log/slog package. Replace the custom daemon logging path with log/slog so new logging code uses the standard Go logging model while preserving the current local operator workflows and log outputs that Vigilante already depends on.
Problem
- The daemon log path is built around ad hoc string formatting and file append helpers rather than Go's standard structured logging API.
- Logging behavior is spread across
internal/app/app.go, internal/environment/environment.go, and internal/state/state.go, which makes logging semantics harder to evolve consistently.
- The current approach makes it harder to attach structured fields, centralize formatting decisions, and align new code with standard Go logging practices.
- This matters now because logging is an operational surface for Vigilante, and continuing to expand a custom logging abstraction will increase maintenance cost and inconsistency.
Context
- The current daemon logger is wired in
New() by passing store.AppendDaemonLog into environment.LoggingRunner.Logf.
- Daemon logs are written to
~/.vigilante/logs/vigilante.log.
- Vigilante also writes a structured subprocess access log to
~/.vigilante/logs/access.jsonl; that existing access-log workflow should remain usable after this change.
vigilante logs is already documented and used as the operator entry point for reading daemon, access, and per-issue logs.
- Existing tests assert against current logging behavior in
internal/environment/environment_test.go, internal/state/state_test.go, and multiple internal/app/app_test.go cases.
Desired Outcome
- Replace the custom daemon logging capability with a
log/slog-based implementation that becomes the standard logging path inside the codebase.
- Keep the current local log destinations and operator workflows working:
- daemon log remains available through
~/.vigilante/logs/vigilante.log
- access log remains available through
~/.vigilante/logs/access.jsonl
vigilante logs continues to surface the expected files
- Preserve the practical diagnostic value of current log events such as command start, command success, command failure, scan lifecycle, dispatch lifecycle, and recovery actions.
- Do not expand this issue into a broader telemetry redesign, analytics schema change, or a rewrite of unrelated session state persistence.
Implementation Notes
- Introduce a repository-level logging abstraction around
*slog.Logger instead of passing raw func(format string, args ...any) callbacks.
- Update
environment.LoggingRunner to emit log records through slog with structured attributes rather than preformatted strings.
- Replace
state.AppendDaemonLog as the primary daemon logging entry point with a slog-compatible handler/writer that still targets ~/.vigilante/logs/vigilante.log.
- Preserve human-readable daemon logs for operators. Using
slog does not require switching the daemon log file to JSON if a text handler is the better fit for local troubleshooting.
- Keep the access log behavior explicit. If access logging remains JSONL and separate from the daemon logger, document that boundary clearly in code and tests.
- Minimize churn in higher-level app logic by centralizing compatibility shims where necessary instead of scattering one-off formatting glue throughout the codebase.
- Required: standardize new daemon logging on
log/slog.
- Flexible: whether the final daemon log format uses
slog.TextHandler directly or a custom handler layered on slog, as long as the result is coherent and preserves operator usefulness.
Acceptance Criteria
Testing Expectations
- Update unit tests for
internal/environment/environment.go to validate slog-based command logging behavior.
- Update state/logging tests to cover the new daemon log writer or handler implementation.
- Run the relevant Go test suites at minimum for
./internal/environment, ./internal/state, and any impacted ./internal/app coverage.
- Cover regressions where logs silently stop being written, command failures lose useful context, or
vigilante logs no longer surfaces the expected daemon or access log files.
Operational / UX Considerations
- Preserve backward-compatible local troubleshooting workflows as much as possible; operators should not need a different command just because the implementation moved to
slog.
- Be deliberate about timestamp format, field naming, and readability so the new logger improves consistency without making daemon logs harder to scan.
- If the daemon log output format changes materially, note that clearly in the README and avoid mixing multiple incompatible formats without a transition plan.
Summary
Vigilante currently implements daemon logging through custom printf-style helpers such as
state.AppendDaemonLogandenvironment.LoggingRunner.Logfinstead of Go's standardlog/slogpackage. Replace the custom daemon logging path withlog/slogso new logging code uses the standard Go logging model while preserving the current local operator workflows and log outputs that Vigilante already depends on.Problem
internal/app/app.go,internal/environment/environment.go, andinternal/state/state.go, which makes logging semantics harder to evolve consistently.Context
New()by passingstore.AppendDaemonLogintoenvironment.LoggingRunner.Logf.~/.vigilante/logs/vigilante.log.~/.vigilante/logs/access.jsonl; that existing access-log workflow should remain usable after this change.vigilante logsis already documented and used as the operator entry point for reading daemon, access, and per-issue logs.internal/environment/environment_test.go,internal/state/state_test.go, and multipleinternal/app/app_test.gocases.Desired Outcome
log/slog-based implementation that becomes the standard logging path inside the codebase.~/.vigilante/logs/vigilante.log~/.vigilante/logs/access.jsonlvigilante logscontinues to surface the expected filesImplementation Notes
*slog.Loggerinstead of passing rawfunc(format string, args ...any)callbacks.environment.LoggingRunnerto emit log records throughslogwith structured attributes rather than preformatted strings.state.AppendDaemonLogas the primary daemon logging entry point with aslog-compatible handler/writer that still targets~/.vigilante/logs/vigilante.log.slogdoes not require switching the daemon log file to JSON if a text handler is the better fit for local troubleshooting.log/slog.slog.TextHandlerdirectly or a custom handler layered onslog, as long as the result is coherent and preserves operator usefulness.Acceptance Criteria
log/sloginstead of the current custom printf-style logging callback pattern.environment.LoggingRunneris emitted throughslogwith structured fields for at least command, directory, outcome, and error state where applicable.~/.vigilante/logs/vigilante.logis still produced and remains readable for operators usingvigilante logs.~/.vigilante/logs/access.jsonlcontinues to work, or any intentional format change is explicitly justified, implemented consistently, and reflected in docs and tests.slog-based behavior without losing coverage for failure and success cases.Testing Expectations
internal/environment/environment.goto validateslog-based command logging behavior../internal/environment,./internal/state, and any impacted./internal/appcoverage.vigilante logsno longer surfaces the expected daemon or access log files.Operational / UX Considerations
slog.