fix: reload log level on SIGHUP#945
Conversation
Signed-off-by: Claude Code <claude-code@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fbe14e9e84
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| func reloadLogLevel(logger *rszerolog.Logger) error { | ||
| if err := config.Load(); err != nil { |
There was a problem hiding this comment.
Avoid writing config reload notices to stdout
When SIGHUP is used with a config file and the stdout target, this call goes through config.LoadFile, which prints using config file... with fmt.Printf to stdout; the stdout target writes NDJSON events to os.Stdout (pkg/wal/processor/stdout/wal_stdout_writer.go), so every reload injects a non-JSON line into the event stream. This is newly reachable at runtime because reload now calls config.Load() on each SIGHUP.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. I added a quiet config reload path and switched the SIGHUP handler to use that, so reloading a config file no longer emits the using config file... notice to stdout. I also added a regression test that captures stdout during reload and verifies it stays empty.
| return nil | ||
| } | ||
|
|
||
| *logger = logger.Level(level) |
There was a problem hiding this comment.
Guard logger mutation during SIGHUP reloads
When a SIGHUP arrives while run or snapshot is actively logging, the reload goroutine overwrites the shared *zerolog.Logger struct, while stdLogger and all WithFields loggers keep the same pointer and can read from it concurrently. That introduces a data race on the logger fields during normal logging; use a synchronized/atomic level mechanism or replace loggers without mutating the shared struct in place.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
That makes sense. I changed the reload path so it no longer overwrites the shared *zerolog.Logger. The logger now keeps a stable trace-level instance and reload updates zerolog’s global level filter instead, which avoids mutating the logger struct while other goroutines may be logging. I also ran go test -race ./cmd successfully.
Signed-off-by: Claude Code <claude-code@anthropic.com>
Summary
Reload the active log level when pgstream receives
SIGHUPinstead of treating it as a shutdown signal.Related Issue
Fixes #943
Changes
SIGHUPseparately from shutdown signals.SIGHUPreloads.Testing
docker run --rm -v "$PWD":/src -w /src golang:1.26.4 gofmt -w cmd/root_cmd.go cmd/run_cmd.go cmd/snapshot_cmd.go cmd/root_cmd_test.go cmd/config/config.go internal/log/zerolog/zerolog.go pkg/log/zerolog/logger.godocker run --rm -v "$PWD":/src -w /src golang:1.26.4 go test ./cmd/... ./internal/log/... ./pkg/log/...docker run --rm -v "$PWD":/src -w /src golang:1.26.4 go test -race ./cmdgit diff --checkNotes
Local host does not have Go installed, so validation was run in Docker with
golang:1.26.4, matching the repository's Go version.