Prometheus

Prometheus - 图1note

Echo community contribution

Prometheus middleware generates metrics for HTTP requests.

There are 2 versions of Prometheus middleware:

Migration guide from old to newer middleware can found here.

Usage

  • Add needed module go get -u github.com/labstack/echo-contrib
  • Add Prometheus middleware and metrics serving route

    1. e := echo.New()
    2. e.Use(echoprometheus.NewMiddleware("myapp")) // adds middleware to gather metrics
    3. e.GET("/metrics", echoprometheus.NewHandler()) // adds route to serve gathered metrics

Examples

Serve metric from the same server as where metrics is gathered

  1. package main
  2. import (
  3. "errors"
  4. "github.com/labstack/echo-contrib/echoprometheus"
  5. "github.com/labstack/echo/v4"
  6. "log"
  7. "net/http"
  8. )
  9. func main() {
  10. e := echo.New()
  11. e.Use(echoprometheus.NewMiddleware("myapp")) // adds middleware to gather metrics
  12. e.GET("/metrics", echoprometheus.NewHandler()) // adds route to serve gathered metrics
  13. e.GET("/hello", func(c echo.Context) error {
  14. return c.String(http.StatusOK, "hello")
  15. })
  16. if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  17. log.Fatal(err)
  18. }
  19. }

Serve metrics on a separate port

  1. func main() {
  2. app := echo.New() // this Echo instance will serve route on port 8080
  3. app.Use(echoprometheus.NewMiddleware("myapp")) // adds middleware to gather metrics
  4. go func() {
  5. metrics := echo.New() // this Echo will run on separate port 8081
  6. metrics.GET("/metrics", echoprometheus.NewHandler()) // adds route to serve gathered metrics
  7. if err := metrics.Start(":8081"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  8. log.Fatal(err)
  9. }
  10. }()
  11. app.GET("/hello", func(c echo.Context) error {
  12. return c.String(http.StatusOK, "hello")
  13. })
  14. if err := app.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  15. log.Fatal(err)
  16. }
  17. }

Sample output (for first example)

  1. curl http://localhost:8080/metrics
  2. # HELP echo_request_duration_seconds The HTTP request latencies in seconds.
  3. # TYPE echo_request_duration_seconds summary
  4. echo_request_duration_seconds_sum 0.41086482
  5. echo_request_duration_seconds_count 1
  6. # HELP echo_request_size_bytes The HTTP request sizes in bytes.
  7. # TYPE echo_request_size_bytes summary
  8. echo_request_size_bytes_sum 56
  9. echo_request_size_bytes_count 1
  10. # HELP echo_requests_total How many HTTP requests processed, partitioned by status code and HTTP method.
  11. # TYPE echo_requests_total counter
  12. echo_requests_total{code="200",host="localhost:8080",method="GET",url="/"} 1
  13. # HELP echo_response_size_bytes The HTTP response sizes in bytes.
  14. # TYPE echo_response_size_bytes summary
  15. echo_response_size_bytes_sum 61
  16. echo_response_size_bytes_count 1
  17. ...

Custom Configuration

Serving custom Prometheus Metrics

Usage

Using custom metrics with Prometheus default registry:

  1. package main
  2. import (
  3. "errors"
  4. "github.com/labstack/echo-contrib/echoprometheus"
  5. "github.com/labstack/echo/v4"
  6. "github.com/prometheus/client_golang/prometheus"
  7. "log"
  8. "net/http"
  9. )
  10. func main() {
  11. e := echo.New()
  12. customCounter := prometheus.NewCounter( // create new counter metric. This is replacement for `prometheus.Metric` struct
  13. prometheus.CounterOpts{
  14. Name: "custom_requests_total",
  15. Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
  16. },
  17. )
  18. if err := prometheus.Register(customCounter); err != nil { // register your new counter metric with default metrics registry
  19. log.Fatal(err)
  20. }
  21. e.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{
  22. AfterNext: func(c echo.Context, err error) {
  23. customCounter.Inc() // use our custom metric in middleware. after every request increment the counter
  24. },
  25. }))
  26. e.GET("/metrics", echoprometheus.NewHandler()) // register route for getting gathered metrics
  27. if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  28. log.Fatal(err)
  29. }
  30. }

or create your own registry and register custom metrics with that:

  1. package main
  2. import (
  3. "errors"
  4. "github.com/labstack/echo-contrib/echoprometheus"
  5. "github.com/labstack/echo/v4"
  6. "github.com/prometheus/client_golang/prometheus"
  7. "log"
  8. "net/http"
  9. )
  10. func main() {
  11. e := echo.New()
  12. customRegistry := prometheus.NewRegistry() // create custom registry for your custom metrics
  13. customCounter := prometheus.NewCounter( // create new counter metric. This is replacement for `prometheus.Metric` struct
  14. prometheus.CounterOpts{
  15. Name: "custom_requests_total",
  16. Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
  17. },
  18. )
  19. if err := customRegistry.Register(customCounter); err != nil { // register your new counter metric with metrics registry
  20. log.Fatal(err)
  21. }
  22. e.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{
  23. AfterNext: func(c echo.Context, err error) {
  24. customCounter.Inc() // use our custom metric in middleware. after every request increment the counter
  25. },
  26. Registerer: customRegistry, // use our custom registry instead of default Prometheus registry
  27. }))
  28. e.GET("/metrics", echoprometheus.NewHandlerWithConfig(echoprometheus.HandlerConfig{Gatherer: customRegistry})) // register route for getting gathered metrics data from our custom Registry
  29. if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  30. log.Fatal(err)
  31. }
  32. }

Skipping URL(s)

Usage

A middleware skipper can be passed to avoid generating metrics to certain URL(s)

  1. package main
  2. import (
  3. "errors"
  4. "github.com/labstack/echo-contrib/echoprometheus"
  5. "github.com/labstack/echo/v4"
  6. "log"
  7. "net/http"
  8. "strings"
  9. )
  10. func main() {
  11. e := echo.New()
  12. mwConfig := echoprometheus.MiddlewareConfig{
  13. Skipper: func(c echo.Context) bool {
  14. return strings.HasPrefix(c.Path(), "/testurl")
  15. }, // does not gather metrics metrics on routes starting with `/testurl`
  16. }
  17. e.Use(echoprometheus.NewMiddlewareWithConfig(mwConfig)) // adds middleware to gather metrics
  18. e.GET("/metrics", echoprometheus.NewHandler()) // adds route to serve gathered metrics
  19. e.GET("/", func(c echo.Context) error {
  20. return c.String(http.StatusOK, "Hello, World!")
  21. })
  22. if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  23. log.Fatal(err)
  24. }
  25. }

Complex Scenarios

Example: modify default echoprometheus metrics definitions

  1. package main
  2. import (
  3. "errors"
  4. "github.com/labstack/echo-contrib/echoprometheus"
  5. "github.com/labstack/echo/v4"
  6. "github.com/prometheus/client_golang/prometheus"
  7. "log"
  8. "net/http"
  9. )
  10. func main() {
  11. e := echo.New()
  12. e.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{
  13. // labels of default metrics can be modified or added with `LabelFuncs` function
  14. LabelFuncs: map[string]echoprometheus.LabelValueFunc{
  15. "scheme": func(c echo.Context, err error) string { // additional custom label
  16. return c.Scheme()
  17. },
  18. "host": func(c echo.Context, err error) string { // overrides default 'host' label value
  19. return "y_" + c.Request().Host
  20. },
  21. },
  22. // The `echoprometheus` middleware registers the following metrics by default:
  23. // - Histogram: request_duration_seconds
  24. // - Histogram: response_size_bytes
  25. // - Histogram: request_size_bytes
  26. // - Counter: requests_total
  27. // which can be modified with `HistogramOptsFunc` and `CounterOptsFunc` functions
  28. HistogramOptsFunc: func(opts prometheus.HistogramOpts) prometheus.HistogramOpts {
  29. if opts.Name == "request_duration_seconds" {
  30. opts.Buckets = []float64{1000.0, 10_000.0, 100_000.0, 1_000_000.0} // 1KB ,10KB, 100KB, 1MB
  31. }
  32. return opts
  33. },
  34. CounterOptsFunc: func(opts prometheus.CounterOpts) prometheus.CounterOpts {
  35. if opts.Name == "requests_total" {
  36. opts.ConstLabels = prometheus.Labels{"my_const": "123"}
  37. }
  38. return opts
  39. },
  40. })) // adds middleware to gather metrics
  41. e.GET("/metrics", echoprometheus.NewHandler()) // adds route to serve gathered metrics
  42. e.GET("/hello", func(c echo.Context) error {
  43. return c.String(http.StatusOK, "hello")
  44. })
  45. if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
  46. log.Fatal(err)
  47. }
  48. }