LogoPly
Middleware

Metrics

Expose Prometheus-compatible HTTP metrics without extra dependencies.

Metrics

middleware.Metrics records request counts and latency histograms and exposes them in Prometheus text format.

metrics := middleware.NewMetrics()

app.Use(metrics.Middleware())
app.Get("/metrics", metrics.Handler())

The metrics endpoint returns:

# HELP ply_http_requests_total Total number of HTTP requests.
# TYPE ply_http_requests_total counter
ply_http_requests_total{method="GET",status="200"} 42

# HELP ply_http_request_duration_seconds HTTP request duration in seconds.
# TYPE ply_http_request_duration_seconds histogram
ply_http_request_duration_seconds_bucket{method="GET",status="200",le="0.005"} 30
...
ply_http_request_duration_seconds_sum{method="GET",status="200"} 0.932
ply_http_request_duration_seconds_count{method="GET",status="200"} 42

Collected data

The middleware records:

  • total requests by method and status,
  • duration buckets by method and status,
  • duration sum and count by method and status.

Buckets are 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, and +Inf seconds.

Paths are not used as labels. This keeps cardinality bounded when routes include IDs, but it also means the metrics do not break down traffic by route.

Protect the endpoint

The handler does not authenticate. Expose /metrics only on a private port or protect it:

app.Get("/metrics", middleware.BasicAuth(
	[]middleware.BasicAuthUser{{Username: "metrics", Password: os.Getenv("METRICS_PASSWORD")}},
), metrics.Handler())

Prometheus can also scrape over a private network without a public route.

Scrape configuration

scrape_configs:
  - job_name: ply
    static_configs:
      - targets: ["127.0.0.1:8080"]

Process scope

Metrics live in memory in the current process. Each instance reports its own counters. Aggregate them in Prometheus or your metrics backend.

Counters reset when the process restarts.

Tracing

Tracing is not built in. Use middleware.RequestID for request correlation, and instrument database and outbound HTTP calls with OpenTelemetry or the tracing library your application already uses. A tracing exporter adds runtime dependencies to the application, not to Ply.

Ordering

Place metrics outside handlers that may short-circuit so rejected requests are counted:

app.Use(
	middleware.RequestID(),
	metrics.Middleware(),
	middleware.RateLimit(100, time.Minute),
)

Requests rejected by BodyLimit, RateLimit, or another short-circuiting middleware are counted only if metrics wraps that middleware.