Fiber: 馃 How to register a Prometheus handler

Created on 21 Feb 2020  路  8Comments  路  Source: gofiber/fiber

I'm trying to register the Prometheus handler to track some metrics from the application I'm writing but I cannot find a way. I managed to to the following:

package main

import (
  "github.com/gofiber/fiber"
  "github.com/prometheus/client_golang/prometheus/promhttp"
  "github.com/valyala/fasthttp/fasthttpadaptor"

func main() {
  app := fiber.New()

  p := fasthttpadaptor.NewFastHTTPHandler(promhttp.Handler())

  app.Get("/metrics", func(c *fiber.Ctx) {
    p(c.Fasthttp)
  })

  app.Get("/", func(c *fiber.Ctx){
    c.Send("Hello, World!")
  })

  app.Get("/404", func(c *fiber.Ctx){
    c.Status(4040).Send("Where did I put it?")
  })

  app.Listen(3000)
}

And it does respond to the requests on /metrics with some data that seems reasonable. Only problem it doesn't seem to count the responses correctly.

(the two endpoints / and /404 are there exactly to try it).

Most helpful comment

We have to think about that, but maybe its better to fix this in a wrapper for Prometheus.

PS: You have a bug in your code: 馃悵

@Fenny I have implemented the prometheus middleware with some basic defaults here.
https://github.com/ansrivas/fiberprometheus

All 8 comments

Thanks for opening your first issue here! 馃帀 Be sure to follow the issue template!

ps: Brilliant work, BTW 馃槃

@ganglio, make sure you are using the latest version:
go get -u github.com/gofiber/fiber

When I run your code I get the following output, could you tell me what variable is missing/incorrect?

package main

import (
    "github.com/gofiber/fiber"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "github.com/valyala/fasthttp/fasthttpadaptor"
)

func main() {
    app := fiber.New()

    p := fasthttpadaptor.NewFastHTTPHandler(promhttp.Handler())

    app.Get("/metrics", func(c *fiber.Ctx) {
        p(c.Fasthttp)
    })

    app.Get("/", func(c *fiber.Ctx) {
        c.Send("Hello, World!")
    })

    app.Get("/404", func(c *fiber.Ctx) {
        c.Status(404).Send("You just lost us an unpaid employee.")
    })

    app.Listen(3000)
}

http://localhost:3000/metrics

# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 9
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.13.5"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 1.166264e+06
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 1.166264e+06
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.443579e+06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 222
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
# TYPE go_memstats_gc_cpu_fraction gauge
go_memstats_gc_cpu_fraction 0
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 2.240512e+06
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
# TYPE go_memstats_heap_alloc_bytes gauge
go_memstats_heap_alloc_bytes 1.166264e+06
# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
# TYPE go_memstats_heap_idle_bytes gauge
go_memstats_heap_idle_bytes 6.4397312e+07
# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
# TYPE go_memstats_heap_inuse_bytes gauge
go_memstats_heap_inuse_bytes 2.2528e+06
# HELP go_memstats_heap_objects Number of allocated objects.
# TYPE go_memstats_heap_objects gauge
go_memstats_heap_objects 4380
# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
# TYPE go_memstats_heap_released_bytes gauge
go_memstats_heap_released_bytes 6.4397312e+07
# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
# TYPE go_memstats_heap_sys_bytes gauge
go_memstats_heap_sys_bytes 6.6650112e+07
# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
# TYPE go_memstats_last_gc_time_seconds gauge
go_memstats_last_gc_time_seconds 0
# HELP go_memstats_lookups_total Total number of pointer lookups.
# TYPE go_memstats_lookups_total counter
go_memstats_lookups_total 0
# HELP go_memstats_mallocs_total Total number of mallocs.
# TYPE go_memstats_mallocs_total counter
go_memstats_mallocs_total 4602
# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
# TYPE go_memstats_mcache_inuse_bytes gauge
go_memstats_mcache_inuse_bytes 13888
# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
# TYPE go_memstats_mcache_sys_bytes gauge
go_memstats_mcache_sys_bytes 16384
# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
# TYPE go_memstats_mspan_inuse_bytes gauge
go_memstats_mspan_inuse_bytes 25160
# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
# TYPE go_memstats_mspan_sys_bytes gauge
go_memstats_mspan_sys_bytes 32768
# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
# TYPE go_memstats_next_gc_bytes gauge
go_memstats_next_gc_bytes 4.473924e+06
# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
# TYPE go_memstats_other_sys_bytes gauge
go_memstats_other_sys_bytes 1.051133e+06
# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
# TYPE go_memstats_stack_inuse_bytes gauge
go_memstats_stack_inuse_bytes 458752
# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
# TYPE go_memstats_stack_sys_bytes gauge
go_memstats_stack_sys_bytes 458752
# HELP go_memstats_sys_bytes Number of bytes obtained from system.
# TYPE go_memstats_sys_bytes gauge
go_memstats_sys_bytes 7.189324e+07
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 8
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1024
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 8
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 1.0747904e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.58230736989e+09
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 5.82090752e+08
# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.
# TYPE process_virtual_memory_max_bytes gauge
process_virtual_memory_max_bytes -1
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 0
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0

@Fenny if you keep requesting /metrics you'll see the promhttp_metric_handler_requests_total{code="200"} increase.
It doesn't happen if you query / or /404 (do it a number of times as the metric increase by 1 every time you request the /metrics endpoint.

From what I understand, the promhttp is used as a wrapper around a router. In the case of Fiber it's only added as a single route thus, while it measures correctly the health of the application (memory, goroutines, etc) it doesn't count the requests correctly.

Ok, I managed to make it work 馃槃

package middlewares

import (
  "fmt"

  "github.com/gofiber/fiber"
  "github.com/prometheus/client_golang/prometheus"
  "github.com/prometheus/client_golang/prometheus/promhttp"
  "github.com/valyala/fasthttp/fasthttpadaptor"
)

var (
  p = fasthttpadaptor.NewFastHTTPHandler(
    promhttp.HandlerFor(
      prometheus.DefaultGatherer,
      promhttp.HandlerOpts{
        // Opt into OpenMetrics to support exemplars.
        EnableOpenMetrics: true,
      },
    ),
  )

  errCnt = prometheus.NewCounterVec(
    prometheus.CounterOpts{
      Name: "promhttp_metric_handler_errors_total",
      Help: "Total number of internal errors encountered by the promhttp metric handler.",
    },
    []string{"status", "method", "path"},
  )
)

func init() {
  prometheus.MustRegister(errCnt)
}

func PrometheusHandler(c *fiber.Ctx) {
  p(c.Fasthttp)
}

func Prometheus(c *fiber.Ctx) {
  sc := c.Fasthttp.Response.StatusCode()
  me := c.Fasthttp.Method()
  pa := c.Fasthttp.Path()
  errCnt.WithLabelValues(fmt.Sprintf("%d", sc), string(me), string(pa)).Add(1)
  c.Next()
}

Only caveat is that all the routes must have a defer c.Next() and the config looks like this:

...

app.Get("/metrics", middlewares.PrometheusHandler)
app.Get("/", func(c *fiber.Ctx) {
  defer c.Next()
  c.Send("馃悵")}
)
app.Use(middlewares.Prometheus)
app.Listen(3000)

...

It would be amazing to have support for "afterwares" that are always executed after everything.

We have to think about that, but maybe its better to fix this in a wrapper for Prometheus.

PS: You have a bug in your code: 馃悵

@Fenny any updates on this?

We have to think about that, but maybe its better to fix this in a wrapper for Prometheus.

PS: You have a bug in your code: 馃悵

@Fenny I have implemented the prometheus middleware with some basic defaults here.
https://github.com/ansrivas/fiberprometheus

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lucasmdomingues picture lucasmdomingues  路  3Comments

faultable picture faultable  路  3Comments

peterbourgon picture peterbourgon  路  4Comments

mhf-ir picture mhf-ir  路  3Comments

mayowa picture mayowa  路  3Comments