Skip to content

fix: honor LOG_LEVEL in hub-worker - #115

Closed
xernobyl wants to merge 1 commit into
mainfrom
fix/hub-worker-log-level
Closed

fix: honor LOG_LEVEL in hub-worker#115
xernobyl wants to merge 1 commit into
mainfrom
fix/hub-worker-log-level

Conversation

@xernobyl

Copy link
Copy Markdown
Contributor

What does this PR do?

hub-worker never configured slog, so it ignored LOG_LEVEL entirely and ran on Go's default handler, while hub-api honored it. Setting LOG_LEVEL=error on the worker did nothing.

It's a bit worse than just the level being ignored: because the worker was on the default handler and the API on a configured TextHandler, the two processes emitted different formats to different streams. Same deployment, same image, two log shapes.

This came out of ENG-1916 (hub#114), where a missing enrichment credential takes down hub-worker. After that change the worker's logs become the only place the actionable error appears, so it matters that they're readable and that debug is reachable. Splitting it out from #114 since it's an independent concern and, as below, it changes output that people may be parsing.

The level parsing and handler setup move into internal/observability, which already owns the logging handler (TraceContextHandler), and both entrypoints call it. Living there also means the logic is actually covered by make test-unit, which runs ./cmd/api and ./internal/... but not ./cmd/worker.

Heads-up for operators — output changes, not just the level

hub-worker previously wrote to stderr as:

2026/07/28 10:37:10 INFO Worker running client_id=...

and now matches hub-api, writing to stdout as:

time=2026-07-28T10:37:10.962Z level=INFO msg="Worker running" client_id=...

Container log collection is unaffected (both streams are captured), but anything parsing hub-worker output, or relying on stderr/stdout separation, needs updating. Worth a line in the release notes.

hub-api behavior is unchanged, including that it configures logging before reporting a config-load failure so a broken config still produces output — the worker now does the same. Unrecognized levels still fall back to info rather than silencing logs, which felt like the safer default for a typo'd env var.

One small oddity preserved rather than fixed: only the exact string warn matches, so LOG_LEVEL=warning falls back to info. That's pre-existing; I pinned it in a test rather than changing it, but happy to make it accept warning if people think that's a trap.

How should this be tested?

make fmt && make lint && make build && make test-unit

The behavior is easiest to see as a before/after against a released image. With any valid DATABASE_URL and API_KEY, run the worker briefly with LOG_LEVEL=error:

docker run --rm -e API_KEY=... -e DATABASE_URL=... -e LOG_LEVEL=error \
  --entrypoint sh <image> -c '/app/hub-worker & sleep 3; kill %1'
  • ghcr.io/formbricks/hub:0.8.1 → still prints INFO lines (2026/07/28 ... INFO Worker running ...), i.e. LOG_LEVEL ignored, default-handler format.
  • this branch → prints nothing at INFO, and with LOG_LEVEL unset prints time=... level=INFO msg="Worker running" ..., matching hub-api.

ParseLogLevel itself is covered by a table test in internal/observability/logging_test.go, including the casing and unknown-value fallbacks.

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Repository Guidelines
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand bits
  • Ran make build
  • Ran make tests (integration tests in tests/) — not run for this change; it touches no DB or job path, and make test-unit covers the added logic
  • Ran make fmt and make lint; no new warnings
  • Removed debug prints / temporary logging
  • Merged the latest changes from main onto my branch with git pull origin main
  • If database schema changed: added migration in migrations/ — n/a, no schema change

Appreciated

  • If API changed: added or updated OpenAPI spec — n/a, no API change
  • If API behavior changed: added request/response examples — n/a
  • Updated docs in docs/ if changes were necessary — n/a, though the stream/format change above is worth a release note
  • Ran make tests-coverage — n/a for a change this size; the new logic has a direct unit test

hub-worker never configured slog, so it ran on Go's default handler at info
regardless of LOG_LEVEL, while hub-api honored it. That matters for ENG-1916
specifically: when a missing enrichment credential takes down the worker, the
worker's logs are the only place the actionable error appears, and its debug
lines were unreachable.

Move the level parsing and handler setup into internal/observability, which
already owns the logging handler, and call it from both entrypoints. Living
there also means the logic is covered by make test-unit, which runs ./cmd/api
and ./internal/... but not ./cmd/worker.

Note for operators: this changes hub-worker's log stream and format, not just
its level. It previously used Go's default handler, which writes to stderr as

    2026/07/28 10:37:10 INFO Worker running client_id=...

and now matches hub-api, writing to stdout as

    time=2026-07-28T10:37:10.962Z level=INFO msg="Worker running" client_id=...

Container log collection is unaffected, but anything parsing hub-worker output
or separating stderr from stdout needs updating. hub-api is unchanged,
including configuring logging before reporting a config-load failure so a
broken config still produces output. Unrecognized levels keep falling back to
info.
@xernobyl

Copy link
Copy Markdown
Contributor Author

Folded into #114 — the log-level commit is now the fifth commit there, so this is redundant. Closing.

Kept as its own commit in #114, and #114's description now carries the operator heads-up about the worker's stream and format change (stderr → stdout, default handler → TextHandler) so it isn't lost.

@xernobyl xernobyl closed this Jul 28, 2026
@xernobyl
xernobyl deleted the fix/hub-worker-log-level branch July 28, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant