feat(observability): add structured logging and metrics #516

Merged
fuzzy merged 1 commit from feat/observability into main 2026-08-04 01:59:33 +00:00
Owner

What

Add application observability:

  • Structured logging (#97) — a gin middleware logs method, path, status, client IP, and latency via zap for every request. Webhook events log the ref and after SHA, and webhook/site failures log the error. The zap logger is threaded through httpapi.Deps.
  • Metrics (#98) — a lightweight in-house metrics.Registry serves GET /metrics with:
    • thwap_pagesd_sites_total and thwap_pagesd_deployments_total computed on scrape from the backing repositories
    • thwap_pagesd_errors_total — a running counter incremented on server-side failures in the site and webhook handlers
    • Prometheus text format
  • main.go wires the logger and metrics registry into the router.

Why

Phase 7 task #100 — completes the Rollback and Operational Features phase; operators can observe request traffic, site/deployment counts, and errors.

Testing

  • go build ./...
  • go vet ./...
  • go test -race ./... — metrics registry (counts + error counter + handler), /metrics endpoint, registration-error increments the counter; full suite passes
  • gofmt -l ./cmd ./internal ./pkg clean
  • golangci-lint run — 0 issues
  • pre-commit hooks all pass

Breaking Changes

None.

Notes

  • OpenTelemetry tracing (#99) is deferred — the roadmap marks it optional; a follow-up can add it without touching this task's surface.
  • Issues #97, #98, #100 were closed via the Forgejo API as part of this task per the workflow.

Closes #97
Closes #98
Closes #100

## What Add application observability: - **Structured logging (#97)** — a gin middleware logs method, path, status, client IP, and latency via zap for every request. Webhook events log the ref and after SHA, and webhook/site failures log the error. The zap logger is threaded through `httpapi.Deps`. - **Metrics (#98)** — a lightweight in-house `metrics.Registry` serves `GET /metrics` with: - `thwap_pagesd_sites_total` and `thwap_pagesd_deployments_total` computed on scrape from the backing repositories - `thwap_pagesd_errors_total` — a running counter incremented on server-side failures in the site and webhook handlers - Prometheus text format - **main.go** wires the logger and metrics registry into the router. ## Why Phase 7 task #100 — completes the Rollback and Operational Features phase; operators can observe request traffic, site/deployment counts, and errors. ## Testing - [x] `go build ./...` - [x] `go vet ./...` - [x] `go test -race ./...` — metrics registry (counts + error counter + handler), `/metrics` endpoint, registration-error increments the counter; full suite passes - [x] `gofmt -l ./cmd ./internal ./pkg` clean - [x] `golangci-lint run` — 0 issues - [x] pre-commit hooks all pass ## Breaking Changes None. ## Notes - OpenTelemetry tracing (#99) is deferred — the roadmap marks it optional; a follow-up can add it without touching this task's surface. - Issues #97, #98, #100 were closed via the Forgejo API as part of this task per the workflow. Closes #97 Closes #98 Closes #100
feat(observability): add structured logging and metrics
All checks were successful
Test and Release / lint (pull_request) Successful in 4m26s
Test and Release / test (pull_request) Successful in 23m18s
e9a276ba24
Add application observability:

- Structured request logging: a gin middleware logs method, path,
  status, client IP, and latency via zap for every request; webhook
  events log ref and after SHA, and failures log the error
- Metrics: a lightweight internal registry serves /metrics with site
  and deployment counts computed on scrape from the backing
  repositories plus a running error counter, in Prometheus text format
- Error counter increments on server-side failures in the site and
  webhook handlers
- Wire the zap logger and metrics registry into the http router in
  main

OpenTelemetry tracing (#99) is deferred as optional. Add tests for the
metrics registry, the /metrics endpoint, and error-count increments.
Mark tasks done in ROADMAP.md.

closes #97
closes #98
closes #100
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-08-04 01:39:21 +00:00
the.auditor left a comment

Review Summary

Verified locally on the PR head (e9a276b): go build, go vet, gofmt, golangci-lint run all clean; all metrics + httpapi tests pass with -race. Smoke-tested the running daemon (fake Forgejo + kubeconfig): /metrics serves correct Prometheus text (sites_total/deployments_total/errors_total), /healthz//readyz return 200, request logging flows through zap ("http request","method":"GET","path":"/metrics","status":200,"client":...), and graceful shutdown works.

The requestLogger middleware replaces gin's stdout Logger() with zap structured logs — closing the recurring mixed-logging note from earlier reviews. The metrics registry is clean and thread-safe (atomic.Uint64 for errors, on-scrape computation from the repositories), and only server-side failures (500s / webhook 401s/400s) increment the error counter — client 4xx on the API correctly don't.

No blocking issues.

Non-blocking observations:

  1. Pre-existing (from #141, not this PR): the daemon fails to start without a reachable Forgejo — forgejo.NewClient performs an eager /api/v1/version check — plus a kubeconfig. /healthz//metrics can't run without full cluster+Forgejo config; worth documenting startup requirements.
  2. gin.Recovery() still writes panic stacks to stdout rather than zap — minor.
  3. Metrics lack # HELP/# TYPE declarations; Prometheus treats them as untyped counters, which works but is slightly non-idiomatic.
  4. Registry.Handler captures context.Background() at router-build time rather than the request context — fine for a scrape endpoint.

Approving.

## Review Summary Verified locally on the PR head (`e9a276b`): `go build`, `go vet`, `gofmt`, `golangci-lint run` all clean; all metrics + httpapi tests pass with `-race`. Smoke-tested the running daemon (fake Forgejo + kubeconfig): `/metrics` serves correct Prometheus text (`sites_total`/`deployments_total`/`errors_total`), `/healthz`/`/readyz` return 200, request logging flows through zap (`"http request","method":"GET","path":"/metrics","status":200,"client":...`), and graceful shutdown works. The `requestLogger` middleware replaces gin's stdout `Logger()` with zap structured logs — closing the recurring mixed-logging note from earlier reviews. The metrics registry is clean and thread-safe (`atomic.Uint64` for errors, on-scrape computation from the repositories), and only server-side failures (500s / webhook 401s/400s) increment the error counter — client 4xx on the API correctly don't. **No blocking issues.** Non-blocking observations: 1. Pre-existing (from #141, not this PR): the daemon fails to start without a *reachable* Forgejo — `forgejo.NewClient` performs an eager `/api/v1/version` check — plus a kubeconfig. `/healthz`/`/metrics` can't run without full cluster+Forgejo config; worth documenting startup requirements. 2. `gin.Recovery()` still writes panic stacks to stdout rather than zap — minor. 3. Metrics lack `# HELP`/`# TYPE` declarations; Prometheus treats them as untyped counters, which works but is slightly non-idiomatic. 4. `Registry.Handler` captures `context.Background()` at router-build time rather than the request context — fine for a scrape endpoint. Approving.
fuzzy merged commit e9a276ba24 into main 2026-08-04 01:59:33 +00:00
fuzzy deleted branch feat/observability 2026-08-04 01:59:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/thwap-pagesd!516
No description provided.