Skip to content

feat(observability): add Prometheus metrics and Grafana dashboards#27

Merged
GRACENOBLE merged 2 commits into
mainfrom
observability.prometheus-and-grafana
Jun 15, 2026
Merged

feat(observability): add Prometheus metrics and Grafana dashboards#27
GRACENOBLE merged 2 commits into
mainfrom
observability.prometheus-and-grafana

Conversation

@GRACENOBLE

@GRACENOBLE GRACENOBLE commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Exposes /metrics endpoint via promhttp.Handler() (unauthenticated, excluded from rate limiting instrumentation)
  • Adds PrometheusMiddleware in transport/middleware recording http_requests_total and http_request_duration_seconds for every route except /metrics itself
  • Adds a dbStatsCollector in infrastructure/database/postgres that exports nine sql.DBStats fields to Prometheus on each scrape
  • Wires prometheus (port 9090) and grafana (port 3001) into docker-compose.yml alongside the existing Postgres service
  • Grafana auto-provisions a Prometheus data source and a starter dashboard with panels for request rate, p50/p95 latency, and DB pool connections
  • No new required env vars for local dev; GRAFANA_ADMIN_USER / GRAFANA_ADMIN_PASSWORD documented in .env.example with admin defaults
  • Fixed two stale docs: observability.md (wrong RegisterRoutes signature) and environment.md (missing REDIS_URL and BLUEPRINT_WS_ALLOWED_ORIGIN)

Closes #17

Test plan

  • go vet ./... — clean
  • go test ./internal/transport/... — passes (3 new middleware tests + metrics endpoint test)
  • docker compose up starts Postgres, Prometheus, and Grafana without errors
  • GET http://localhost:8080/metrics returns 200 with Prometheus exposition format
  • Grafana at http://localhost:3001 shows the backend dashboard with live data after a few requests

Summary by CodeRabbit

  • New Features

    • Added Prometheus metrics monitoring for HTTP requests and database connection pool statistics.
    • Added Grafana dashboard displaying request rates, latency metrics (p50/p95), and database connection metrics.
    • Exposed new /metrics endpoint for Prometheus data collection.
  • Documentation

    • Updated environment variable documentation with new monitoring and WebSocket configuration options.

Exposes a /metrics endpoint from the Go backend using promhttp, instruments
all HTTP routes with request count and latency histograms, and exports DB
connection pool stats via a custom prometheus.Collector. Prometheus and Grafana
are wired into Docker Compose (ports 9090 and 3001) with a provisioned data
source and starter dashboard covering request rate, p50/p95 latency, and DB
pool connections. Closes #17.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GRACENOBLE, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 45 minutes and 4 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54af9e92-d094-4e37-88c8-2ac0208b7bc6

📥 Commits

Reviewing files that changed from the base of the PR and between 038d94b and 7c081ac.

📒 Files selected for processing (5)
  • backend/cmd/api/main.go
  • backend/internal/server/server.go
  • backend/internal/transport/handlers/metrics_handler_test.go
  • backend/internal/transport/handlers/routes.go
  • backend/internal/transport/middleware/local_network.go
📝 Walkthrough

Walkthrough

Adds end-to-end Prometheus observability to the Go backend: a Gin middleware instruments HTTP request counts and latency, a new dbStatsCollector exposes PostgreSQL pool stats, and a /metrics endpoint serves scrape data. Docker Compose gains prometheus and grafana services with provisioned datasource and a "Backend Overview" dashboard.

Changes

Prometheus Metrics and Grafana Observability

Layer / File(s) Summary
Go module dependency additions
backend/go.mod
Promotes sentry-go packages to direct deps and adds gorilla/websocket and prometheus/client_golang as new direct dependencies; extends the indirect set with Prometheus support packages (beorn7/perks, client_model, common, procfs, munnerz/goautoneg, kylelemons/godebug, go.yaml.in/yaml/v2).
Prometheus HTTP middleware and tests
backend/internal/transport/middleware/metrics.go, backend/internal/transport/middleware/metrics_test.go
Defines httpRequestsTotal counter and httpRequestDurationSeconds histogram; PrometheusMiddleware skips self-scrape on /metrics, derives route label from c.FullPath() (falling back to "unmatched"), and records status and latency. Three test cases cover normal requests, skipped /metrics requests, and unmatched routes.
DB pool metrics collector and server registration
backend/internal/infrastructure/database/postgres/db_metrics.go, backend/internal/server/server.go
dbStatsCollector wraps *sql.DB and publishes pool gauges and wait counters via Describe/Collect. NewServer registers the collector, suppressing prometheus.AlreadyRegisteredError and warning on other failures.
Route wiring, /metrics handler, and endpoint test
backend/internal/transport/handlers/routes.go, backend/internal/transport/handlers/metrics_handler.go, backend/internal/transport/handlers/metrics_handler_test.go
Registers PrometheusMiddleware globally, adds GET /metrics wrapping promhttp.Handler() via gin.WrapH, defines a stub MetricsHandler with Swagger annotations, and verifies the endpoint returns HTTP 200 with text/plain content type.
Swagger spec and observability docs
backend/docs/swagger/docs.go, backend/docs/swagger/swagger.json, backend/docs/swagger/swagger.yaml, backend/docs/observability.md
Adds GET /metrics to the generated Swagger specs with text/plain production and observability tag; updates observability.md to reflect the removed FirebaseTokenVerifier parameter from RegisterRoutes.
Docker Compose Prometheus + Grafana stack
backend/prometheus.yml, backend/docker-compose.yml, backend/grafana/provisioning/datasources/prometheus.yml, backend/grafana/provisioning/dashboards/provider.yml, backend/grafana/provisioning/dashboards/backend.json
Adds prometheus and grafana services with port mappings (9090, 3001:3000), volume mounts, and grafana_data named volume. Prometheus scrapes host.docker.internal:8080 every 15 s. Grafana provisions the Prometheus datasource and a "Backend Overview" dashboard with four PromQL panels: request rate, p50/p95 latency, and DB pool connections.
Env example and environment docs
backend/.env.example, backend/docs/environment.md
Adds BLUEPRINT_WS_ALLOWED_ORIGIN, GRAFANA_ADMIN_USER, and GRAFANA_ADMIN_PASSWORD to .env.example; documents REDIS_URL and BLUEPRINT_WS_ALLOWED_ORIGIN in the environment reference table.

Sequence Diagram(s)

sequenceDiagram
    participant Client as HTTP Client
    participant GinMiddleware as PrometheusMiddleware
    participant GinHandler as Route Handler
    participant PromRegistry as Prometheus Registry
    participant Prometheus as Prometheus Scraper
    participant Grafana as Grafana

    Client->>GinMiddleware: GET /api/...
    GinMiddleware->>GinHandler: c.Next()
    GinHandler-->>GinMiddleware: response status
    GinMiddleware->>PromRegistry: Inc(httpRequestsTotal{method,path,status})
    GinMiddleware->>PromRegistry: Observe(httpRequestDurationSeconds{method,path})

    Prometheus->>GinMiddleware: GET /metrics (skipped by middleware)
    GinMiddleware->>PromRegistry: promhttp.Handler() serves exposition
    PromRegistry-->>Prometheus: text/plain metrics

    Prometheus->>Grafana: PromQL query results
    Grafana-->>Grafana: render Backend Overview panels
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • GRACENOBLE/fullstack-template#25: Modifies the same Handler.RegisterRoutes signature and server.go middleware wiring that this PR also touches (adds Sentry middleware; this PR removes the FirebaseTokenVerifier parameter and adds Prometheus middleware).
  • GRACENOBLE/fullstack-template#11: Introduced the Swagger/OpenAPI integration and generated spec files that this PR extends with the new GET /metrics path entry.

Suggested labels

area: backend, type: chore

🐇 A rabbit hopped through the code one day,
And sprinkled metrics along the way—
Counters tick, histograms gleam,
Grafana dashboards glow and stream.
"Now I can see every latency!"
The bunny cheered with great glee. 📊✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(observability): add Prometheus metrics and Grafana dashboards' accurately summarizes the main changes in the PR, which adds Prometheus metrics collection, exposes a /metrics endpoint, and integrates Grafana visualization.
Linked Issues check ✅ Passed The PR successfully implements all backend objectives from issue #17: Prometheus metrics exposure via /metrics endpoint, HTTP request/latency instrumentation via middleware, and database pool statistics collection. Infrastructure objectives are met: Prometheus and Grafana services added to docker-compose.yml with proper configuration and provisioning. Observability baseline is addressed with documentation updates to environment.md and observability.md. All acceptance criteria are satisfied: services start via docker compose, dashboards display live metrics, no new env vars required for local dev, and docs are updated.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #17 objectives. Prometheus/Grafana additions, middleware instrumentation, database metrics, Docker Compose integration, documentation updates, and dependency additions all directly support the stated requirements. No extraneous modifications were detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch observability.prometheus-and-grafana

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added area: backend Go REST API type: chore Cleanup or maintenance tasks labels Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
backend/.env.example (1)

21-25: 💤 Low value

Optional: Address static analysis hints.

The dotenv-linter flags alphabetical ordering and a missing trailing blank line. The current logical grouping (by feature/purpose) is more maintainable than strict alphabetical order, but you may choose to add a blank line at the end for consistency with linter expectations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/.env.example` around lines 21 - 25, The dotenv-linter has flagged a
missing trailing blank line in the backend/.env.example file. Add a blank line
at the end of the file after the GRAFANA_ADMIN_PASSWORD=admin line to satisfy
linter expectations and maintain consistency with file formatting conventions.
The current logical grouping by feature is acceptable and does not need to be
changed to strict alphabetical order.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/internal/server/server.go`:
- Around line 40-45: In the prometheus.Register error handling block for the
dbCollector in the server initialization function, the
non-AlreadyRegisteredError case is only logging a warning instead of failing
startup. Remove the warning log for non-AlreadyRegisteredError cases and instead
return the error from the function (such as NewServer) so that real registration
failures cause startup to fail fast. Keep the AlreadyRegisteredError check to
allow idempotent registration, but propagate any other error type up the call
stack rather than silently continuing execution.

In `@backend/internal/transport/handlers/metrics_handler_test.go`:
- Around line 11-13: The test in metrics_handler_test.go is constructing a
NewHandler with all nil parameters, which bypasses testing the handler's
dependency contracts. Replace the three nil arguments in the NewHandler(nil,
nil, nil) call with appropriate mock interfaces or stubs that satisfy the
handler's use-case dependencies. This ensures the unit test properly exercises
the handler with mocked dependencies as per the coding guideline that handler
unit tests should mock Use case interfaces rather than relying on nil values.

In `@backend/internal/transport/handlers/routes.go`:
- Line 45: The `/metrics` endpoint registration at line 45 in the routes file
currently exposes the metrics handler without any access control in all
environments. Add environment-based conditional logic around the
r.GET("/metrics", gin.WrapH(promhttp.Handler())) route registration to either
skip it entirely in production or wrap it with authentication and/or
trusted-network middleware that restricts access to internal callers only. This
prevents information disclosure through metrics exposure in production
deployments.

---

Nitpick comments:
In `@backend/.env.example`:
- Around line 21-25: The dotenv-linter has flagged a missing trailing blank line
in the backend/.env.example file. Add a blank line at the end of the file after
the GRAFANA_ADMIN_PASSWORD=admin line to satisfy linter expectations and
maintain consistency with file formatting conventions. The current logical
grouping by feature is acceptable and does not need to be changed to strict
alphabetical order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6638f93-bed9-46f3-9a84-d3ce63b07826

📥 Commits

Reviewing files that changed from the base of the PR and between ce46f00 and 038d94b.

⛔ Files ignored due to path filters (1)
  • backend/go.sum is excluded by !**/*.sum
📒 Files selected for processing (19)
  • backend/.env.example
  • backend/docker-compose.yml
  • backend/docs/environment.md
  • backend/docs/observability.md
  • backend/docs/swagger/docs.go
  • backend/docs/swagger/swagger.json
  • backend/docs/swagger/swagger.yaml
  • backend/go.mod
  • backend/grafana/provisioning/dashboards/backend.json
  • backend/grafana/provisioning/dashboards/provider.yml
  • backend/grafana/provisioning/datasources/prometheus.yml
  • backend/internal/infrastructure/database/postgres/db_metrics.go
  • backend/internal/server/server.go
  • backend/internal/transport/handlers/metrics_handler.go
  • backend/internal/transport/handlers/metrics_handler_test.go
  • backend/internal/transport/handlers/routes.go
  • backend/internal/transport/middleware/metrics.go
  • backend/internal/transport/middleware/metrics_test.go
  • backend/prometheus.yml

Comment thread backend/internal/server/server.go
Comment thread backend/internal/transport/handlers/metrics_handler_test.go Outdated
Comment thread backend/internal/transport/handlers/routes.go Outdated
- NewServer now returns error so collector registration failures bubble
  up to main rather than being silently swallowed
- /metrics is restricted to loopback/RFC-1918 IPs in staging/production
  via a new LocalNetworkOnly middleware; unrestricted in debug mode
- metrics handler test uses &Handler{} zero value instead of nil deps,
  consistent with the existing hello_handler_test.go pattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@GRACENOBLE GRACENOBLE merged commit 99c064f into main Jun 15, 2026
3 checks passed
@GRACENOBLE GRACENOBLE deleted the observability.prometheus-and-grafana branch June 15, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Go REST API type: chore Cleanup or maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add Prometheus metrics and Grafana dashboards

1 participant