ginprom

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 20, 2026 License: MIT Imports: 8 Imported by: 0

README

gin-prometheus

A Gin middleware that automatically records HTTP request metrics and exposes them to Prometheus.

Go Reference Go Report Card


Features

Capability Details
Request counter http_requests_total labelled by status_code, method, path
Latency histogram http_request_duration_seconds
Request-size histogram http_request_size_bytes
Response-size histogram http_response_size_bytes
Metrics endpoint Drop-in http.Handler compatible with any Gin route
Basic Auth Optional username/password protection on the /metrics endpoint
Route filtering Exclude specific routes (e.g. /healthz) from metrics
Status-code aggregation Collapse codes into 2xx / 4xx / 5xx classes
Custom buckets Override default histogram buckets
Custom metric prefix Namespace metrics per service
Custom registry Isolated prometheus.Registry for testing or multi-tenant use
Unmatched route handling Group or filter 404/unknown paths to avoid cardinality explosion

Installation

go get github.com/logocomune/gin-prometheus

Requires Go 1.23+ and Gin v1.10+.


Quick Start

package main

import (
    "github.com/gin-gonic/gin"
    ginprom "github.com/logocomune/gin-prometheus"
)

func main() {
    r := gin.Default()

    // Register the metrics middleware (records all routes by default).
    r.Use(ginprom.Middleware())

    // Expose the /metrics endpoint for Prometheus to scrape.
    r.GET("/metrics", gin.WrapH(ginprom.GetMetricHandler()))

    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello World")
    })

    r.Run(":8080")
}

Metrics Reference

All metrics carry three labels:

Label Description Example
status_code HTTP response status code (or class) 200, 404, 2xx
method HTTP method GET, POST
path Gin route pattern /users/:id
Metric names
Name Type Description
http_requests_total Counter Total number of handled requests
http_request_duration_seconds Histogram Time elapsed from first byte received to last byte sent
http_request_size_bytes Histogram Inbound request size (headers + body)
http_response_size_bytes Histogram Outbound response body size

Default histogram buckets:

  • Duration – exponential, 15 buckets from 1 ms to ~16 s (factor 2).
  • Size – exponential, 10 buckets from 100 B to ~51 KB (factor 2).

Configuration Options

Middleware options (Option)

Pass these to Middleware(...) or MiddlewareWithMetrics(mc, ...).

Option Default Description
WithRecordRequestSize(bool) true Enable/disable request-size histogram
WithRecordResponseSize(bool) true Enable/disable response-size histogram
WithRecordDuration(bool) true Enable/disable latency histogram
WithAggregateStatusCode(bool) false Group codes into 1xx5xx classes
WithFilterRoutes([]string) Skip listed route patterns entirely
WithFilterPath(func) Custom per-request filter function
WithPathAggregator(func) Custom path-label mapping function
WithUnmatchedRouteHandling(bool) true Count or ignore 404/unmatched routes
WithUnmatchedRouteGrouping(bool) true Collapse all unmatched under /unmatched/*
Metrics handler options (HandlerOption)

Pass these to GetMetricHandler(...).

Option Description
WithBasicAuth(username, password string) Require HTTP Basic Auth to access /metrics
Metrics collection options (MetricsOption)

Pass these to NewMetricsCollection(...) when you need a custom setup.

Option Description
WithCustomRegistry(*prometheus.Registry) Use an isolated registry instead of the global one
WithMetricPrefix(string) Prefix all metric names (e.g. "myapp"myapp_http_requests_total)
WithCustomBuckets(duration, size []float64) Override all histogram buckets at once
WithCustomRequestCounter(*prometheus.CounterVec) Bring your own request counter
WithCustomRequestSizeHistogram(*prometheus.HistogramVec) Bring your own request-size histogram
WithCustomResponseSizeHistogram(*prometheus.HistogramVec) Bring your own response-size histogram
WithCustomDurationHistogram(*prometheus.HistogramVec) Bring your own duration histogram

Usage Examples

Exclude health-check routes from metrics
r.Use(ginprom.Middleware(
    ginprom.WithFilterRoutes([]string{"/healthz", "/readyz", "/metrics"}),
))
Aggregate status codes into classes

Reduces label cardinality: 200, 201, 204 all become 2xx.

r.Use(ginprom.Middleware(
    ginprom.WithAggregateStatusCode(true),
))
Custom path filter
r.Use(ginprom.Middleware(
    ginprom.WithFilterPath(func(route, path string) bool {
        // Skip any route starting with /internal
        return strings.HasPrefix(path, "/internal")
    }),
))
Custom path aggregator

Useful when you have path segments that are not Gin parameters but still produce high cardinality (e.g. UUIDs embedded in the path).

r.Use(ginprom.Middleware(
    ginprom.WithPathAggregator(func(route, path string, statusCode int) string {
        if route != "" {
            return route
        }
        // Bucket unmatched paths by status class only
        if statusCode >= 500 {
            return "error_5xx"
        }
        return "other"
    }),
))
Protect the metrics endpoint with Basic Auth
r.GET("/metrics", gin.WrapH(
    ginprom.GetMetricHandler(
        ginprom.WithBasicAuth("prometheus", "s3cr3t"),
    ),
))
Custom metric prefix
mc := ginprom.NewMetricsCollection(
    ginprom.WithMetricPrefix("myservice"),
)
r.Use(ginprom.MiddlewareWithMetrics(mc))
// Metrics: myservice_http_requests_total, myservice_http_request_duration_seconds, …
Custom histogram buckets
mc := ginprom.NewMetricsCollection(
    ginprom.WithCustomBuckets(
        []float64{0.005, 0.01, 0.05, 0.1, 0.5, 1, 2, 5}, // duration (seconds)
        []float64{512, 1024, 4096, 16384, 65536},          // sizes (bytes)
    ),
)
r.Use(ginprom.MiddlewareWithMetrics(mc))
Isolated registry (useful in tests)
reg := prometheus.NewRegistry()
mc := ginprom.NewMetricsCollection(
    ginprom.WithCustomRegistry(reg),
)
r.Use(ginprom.MiddlewareWithMetrics(mc))
Disable specific measurements
r.Use(ginprom.Middleware(
    ginprom.WithRecordRequestSize(false),  // don't track request body sizes
    ginprom.WithRecordResponseSize(false), // don't track response sizes
))
Unmatched route handling

By default, requests to routes that are not registered in Gin (which would return 404) are still counted under the label /unmatched/* to keep cardinality bounded. You can turn this off:

// Do not record metrics for unmatched routes at all
r.Use(ginprom.Middleware(
    ginprom.WithUnmatchedRouteHandling(false),
))

// Record each unmatched path individually (may explode cardinality!)
r.Use(ginprom.Middleware(
    ginprom.WithUnmatchedRouteHandling(true),
    ginprom.WithUnmatchedRouteGrouping(false),
))

Prometheus Scrape Configuration

Add a scrape job to your prometheus.yml:

scrape_configs:
  - job_name: my-gin-service
    static_configs:
      - targets: ["localhost:8080"]
    metrics_path: /metrics
    # If you enabled Basic Auth:
    # basic_auth:
    #   username: prometheus
    #   password: s3cr3t

Example Grafana Queries

# Request rate per route (last 5 min)
sum(rate(http_requests_total[5m])) by (path, method)

# 99th-percentile latency per route
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, path))

# Error rate (5xx)
sum(rate(http_requests_total{status_code=~"5.."}[5m])) by (path)

# Average request size in KB
sum(rate(http_request_size_bytes_sum[5m])) by (path)
/ sum(rate(http_request_size_bytes_count[5m])) by (path) / 1024

Contributing

Contributions, bug reports and feature requests are welcome. Please open an issue or submit a pull request.

License

MIT

Documentation

Overview

Package ginprom provides a Gin middleware that automatically collects and exposes HTTP request metrics to Prometheus.

It records request counts, latency, request size, and response size, all labelled by HTTP method, status code, and route pattern. Metrics can be exposed through the helper GetMetricHandler and optionally protected with HTTP Basic Authentication via WithBasicAuth.

Basic usage:

r := gin.Default()
r.Use(ginprom.Middleware())
r.GET("/metrics", gin.WrapH(ginprom.GetMetricHandler()))

Index

Constants

This section is empty.

Variables

View Source
var (
	DefaultDurationBuckets = prometheus.ExponentialBuckets(0.001, 2, 15)
	DefaultSizeBuckets     = prometheus.ExponentialBuckets(100, 2, 10)
)

Default histogram bucket sets used when no custom buckets are provided.

DefaultDurationBuckets covers request latency from 1 ms up to ~16 s in 15 exponential steps (base 2, start 0.001 s).

DefaultSizeBuckets covers payload sizes from 100 bytes up to ~51 KB in 10 exponential steps (base 2, start 100 bytes).

Functions

func GetMetricHandler

func GetMetricHandler(opt ...HandlerOption) http.Handler

GetMetricHandler returns an http.Handler that serves the default Prometheus metrics page (equivalent to promhttp.Handler). Pass WithBasicAuth to require authentication before metrics are exposed.

func Middleware

func Middleware(options ...Option) gin.HandlerFunc

Middleware returns a Gin handler that records Prometheus metrics for every request using the package-level default metric collectors registered with the global Prometheus registry.

Accept zero or more Option values to tune what is measured:

r.Use(ginprom.Middleware(
    ginprom.WithFilterRoutes([]string{"/healthz", "/readyz"}),
    ginprom.WithAggregateStatusCode(true),
))

func MiddlewareWithMetrics added in v1.3.1

func MiddlewareWithMetrics(metrics *MetricsCollection, options ...Option) gin.HandlerFunc

MiddlewareWithMetrics is like Middleware but records metrics into the provided MetricsCollection instead of the package-level default. Use this when you need multiple independent metric namespaces, custom registries, or fine-grained control over the collectors.

Types

type HandlerOption added in v1.2.0

type HandlerOption func(*handlerConfig)

HandlerOption is a functional option that configures the metrics HTTP handler returned by GetMetricHandler.

func WithBasicAuth added in v1.2.0

func WithBasicAuth(username, password string) HandlerOption

WithBasicAuth protects the metrics endpoint with HTTP Basic Authentication. Requests that do not supply matching credentials receive a 401 Unauthorized response with a WWW-Authenticate challenge header.

Example:

r.GET("/metrics", gin.WrapH(ginprom.GetMetricHandler(
    ginprom.WithBasicAuth("prometheus", "s3cr3t"),
)))

type MetricsCollection added in v1.3.1

type MetricsCollection struct {
	TotalRequests *prometheus.CounterVec
	ResponseSize  *prometheus.HistogramVec
	RequestSize   *prometheus.HistogramVec
	Duration      *prometheus.HistogramVec
	Registry      *prometheus.Registry // Optional custom registry
}

MetricsCollection groups the four Prometheus collectors used by the middleware: a counter for total requests and three histograms for duration, request size, and response size. An optional custom registry may be set so that metrics are not registered with the default global Prometheus registry.

func NewMetricsCollection added in v1.3.1

func NewMetricsCollection(opts ...MetricsOption) *MetricsCollection

NewMetricsCollection creates a MetricsCollection and registers all four collectors with Prometheus. Pass MetricsOption functions to customise metric names, buckets, or the target registry.

Example – use a custom registry and a metric name prefix:

reg := prometheus.NewRegistry()
mc := ginprom.NewMetricsCollection(
    ginprom.WithCustomRegistry(reg),
    ginprom.WithMetricPrefix("myservice"),
)

type MetricsOption added in v1.3.1

type MetricsOption func(*MetricsCollection)

MetricsOption is a functional option that configures a MetricsCollection. Options are applied in order by NewMetricsCollection.

func WithCustomBuckets added in v1.3.1

func WithCustomBuckets(durationBuckets, sizeBuckets []float64) MetricsOption

WithCustomBuckets replaces the bucket definitions for all three histogram collectors. durationBuckets configures the request-duration histogram; sizeBuckets configures both the request-size and response-size histograms.

func WithCustomDurationHistogram added in v1.3.1

func WithCustomDurationHistogram(histogram *prometheus.HistogramVec) MetricsOption

WithCustomDurationHistogram replaces the default request-duration histogram with the provided one. The histogram must carry the same labels as the middleware (status_code, method, path).

func WithCustomRegistry added in v1.3.1

func WithCustomRegistry(registry *prometheus.Registry) MetricsOption

WithCustomRegistry configures the MetricsCollection to register all collectors with the provided registry instead of the default global one. This is especially useful in tests or when running multiple independent metric namespaces inside the same process.

func WithCustomRequestCounter added in v1.3.1

func WithCustomRequestCounter(counter *prometheus.CounterVec) MetricsOption

WithCustomRequestCounter replaces the default request-count counter with the provided one. The counter must use the same label set as the middleware (status_code, method, path).

func WithCustomRequestSizeHistogram added in v1.3.1

func WithCustomRequestSizeHistogram(histogram *prometheus.HistogramVec) MetricsOption

WithCustomRequestSizeHistogram replaces the default request-size histogram with the provided one. The histogram must carry the same labels as the middleware (status_code, method, path).

func WithCustomResponseSizeHistogram added in v1.3.1

func WithCustomResponseSizeHistogram(histogram *prometheus.HistogramVec) MetricsOption

WithCustomResponseSizeHistogram replaces the default response-size histogram with the provided one. The histogram must carry the same labels as the middleware (status_code, method, path).

func WithMetricPrefix added in v1.3.1

func WithMetricPrefix(prefix string) MetricsOption

WithMetricPrefix prepends prefix to all four default metric names. For example, passing "myapp" will produce metrics named "myapp_http_requests_total", "myapp_http_request_duration_seconds", etc. This option recreates the four collectors, so it must appear before any individual collector option that should apply to the prefixed names.

type Option

type Option func(*config)

Option is a functional option that configures the Middleware or MiddlewareWithMetrics behaviour. Options are evaluated in order; later options override earlier ones when they affect the same field.

func WithAggregateStatusCode

func WithAggregateStatusCode(aggregate bool) Option

WithAggregateStatusCode controls status-code label granularity. When enabled, individual codes are bucketed into class labels such as "2xx", "4xx", "5xx", reducing metric cardinality at the cost of less specific alerting. Disabled by default.

func WithFilterPath

func WithFilterPath(filter func(string, string) bool) Option

WithFilterPath installs a custom filter function that decides, for each request, whether metrics should be skipped. The function receives the Gin route pattern (first argument) and the raw URL path (second argument) and returns true when the request must be excluded from metrics collection.

Example – skip any path that starts with "/internal":

ginprom.WithFilterPath(func(route, path string) bool {
    return strings.HasPrefix(path, "/internal")
})

func WithFilterRoutes added in v1.3.0

func WithFilterRoutes(routes []string) Option

WithFilterRoutes registers a list of exact Gin route patterns that should be excluded from metrics collection. The match is performed against the registered pattern (e.g. "/health"), not the raw request URL.

Example:

ginprom.WithFilterRoutes([]string{"/healthz", "/readyz", "/metrics"})

func WithPathAggregator

func WithPathAggregator(aggregator func(string, string, int) string) Option

WithPathAggregator installs a custom function that maps a (route, path, statusCode) triple to the label value used in all four metrics. The default implementation returns route when it is non-empty, "path_4xx" for 4xx unmatched requests, "path_5xx" for 5xx unmatched ones, and "missing_route" otherwise.

Use this option to normalise dynamic segments that Gin does not capture as named parameters, or to further reduce metric cardinality.

func WithRecordDuration

func WithRecordDuration(record bool) Option

WithRecordDuration enables or disables recording of request durations. When enabled (the default), every request is observed by the http_request_duration_seconds histogram.

func WithRecordRequestSize

func WithRecordRequestSize(record bool) Option

WithRecordRequestSize enables or disables recording of HTTP request body sizes. When enabled (the default), every request is observed by the http_request_size_bytes histogram.

func WithRecordResponseSize

func WithRecordResponseSize(record bool) Option

WithRecordResponseSize enables or disables recording of HTTP response sizes. When enabled (the default), every response is observed by the http_response_size_bytes histogram.

func WithUnmatchedRouteGrouping added in v1.3.1

func WithUnmatchedRouteGrouping(enabled bool) Option

WithUnmatchedRouteGrouping controls whether all unmatched routes are collapsed into a single "/unmatched/*" label value (enabled = true) or recorded individually under "/unmatched<original-path>" (enabled = false). Grouping is the safer default because it prevents metric cardinality explosion caused by random or attacker-supplied URL paths.

func WithUnmatchedRouteHandling added in v1.3.1

func WithUnmatchedRouteHandling(enabled bool) Option

WithUnmatchedRouteHandling controls whether requests that do not match any registered Gin route are still counted in metrics. When enabled (the default), such requests are grouped under an "/unmatched/*" label (see also WithUnmatchedRouteGrouping). Disable this to silently drop all unmatched requests from metrics.

func WithUnmatchedRouteMarking added in v1.3.1

func WithUnmatchedRouteMarking(enabled bool) Option

WithUnmatchedRouteMarking enables or disables the special "/unmatched" prefix added to route patterns that the Gin router did not match. Deprecated in favour of WithUnmatchedRouteHandling, kept for backwards compatibility.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL