Skip to content

R7: structured logging with zap - #231

Merged
can3p merged 11 commits into
masterfrom
refactor/r7-logging
Oct 11, 2026
Merged

can3p merged 11 commits into
masterfrom
refactor/r7-logging

Conversation

@can3p

@can3p can3p commented Oct 11, 2026

Copy link
Copy Markdown
Owner

Replaces the standard log package, log/slog and log-like fmt calls with one *zap.Logger, passed explicitly (no global logger).

What changed

  • pkg/logging:
    • FromContext / WithLogger / With work on a context, a *gin.Context and its c.Request.Context(), and return a no-op logger when none is set.
    • New builds the logger from --log-level and the new --log-format: json is the default; compose and .env.example set console.
    • Middleware gives each request a logger with a request_id, adds user_id once auth knows the user, and logs the request.
    • Recovery logs a panic with its stack.
  • Router: gin.New() plus that middleware, instead of gin.Default().
  • gogo v0.2.0: forms.DefaultHandler now takes the request's logger.
  • Workers: the mail sender, the feed poller and the login pruner take a WithLogger option, wired in serve.go.
  • Request code: services, forms, auth, the media server and the export action log through logging.FromContext.
  • Markdown: the transformers carry their own logger.
  • Subcommands: those other than serve log in the console format on stderr.
  • Lint: depguard forbids importing log and log/slog outside pkg/logging, tests included.
  • Tests: pkg/testutil/testlog records log entries so tests can assert on them.
  • Dead code: util.LocalizeTime had no callers and is deleted.

Behavior is unchanged apart from the log output itself. A page that panics with REPORT_PANICS on still answers 200, as it did before; that is filed as #230.

Docs

  • docs/architecture.md gains a "Logging" section; docs/testing.md lists testlog.
  • The README describes the console log format for local development.
  • The wave-run skill now says a signature shared by two parallel tasks is fixed before dispatch, and .claude/worktrees/ is gitignored.
  • The wave's history is in docs/archive/history/r7.md.

Checks

  • make check-q and make cover-check pass.
  • In the sandbox, make test-ui fails only the three browser tests docs/testing.md already lists as failing only there.

🤖 Generated with Claude Code

https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL


Generated by Claude Code

claude added 11 commits October 10, 2026 21:01
gogo v0.2.0 logs through zap, so L0 bumps it with the DefaultHandler
call sites. Logs go to stdout: JSON by default, console in local
development. Workers outside pkg/feedops, files that logged but no task
owned, and the serve.go wiring are assigned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WyCPayWrFEFoujXXMsY1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WyCPayWrFEFoujXXMsY1s
Add pkg/logging: FromContext/WithLogger/With carry a *zap.Logger through a
context or a *gin.Context (no-op when none is set), New builds it from
--log-level and the new --log-format (json by default, console in local
development), Middleware gives each request a logger with its request ID
and logs the request, Recovery logs a panic with its stack.

The router replaces gin.Default with gin.New plus these, adds the user ID
once auth knows it, and logs through zap instead of the log package; serve
builds the logger and the pprof server's fatal error goes through it. Bump
gogo to v0.2.0, whose forms.DefaultHandler takes the request's logger.
pkg/testutil/testlog records entries for tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
…til.LocalizeTime

The feeds poller (L2) calls the feed cleaner (L1), so the signature between
them is fixed before both run. util.LocalizeTime had no callers;
pkg/util/date has the one in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
dbsender takes WithLogger and its poller carries it in the context; the
media server logs through the request's logger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
feeds and accounts take WithLogger for the feed poller and the login
pruner, which carry it in their context; request code logs with
logging.FromContext; the markdown transformers carry their own logger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
…the logger

serve passes the logger to the mail sender, the feed poller and the login
pruner. logging.WithLogger on a gin context also sets it on the request's
context, so services called with c.Request.Context() log with the
request's ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
openDB logs a failure to close through the logger it is given: serve's,
or for the other subcommands a console logger on stderr, apart from what
they print on stdout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
…gent worktrees

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012htHSQKmZ4rKeWkSPsJdgL
@can3p
can3p merged commit f1e3a1d into master Oct 11, 2026
8 checks passed
@can3p
can3p deleted the refactor/r7-logging branch October 11, 2026 23:42
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.

2 participants