ansrivas / fiberprometheus

prometheus middleware for Fiber

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Path in metrics is always `/`

ryan-yalo opened this issue · comments

I thought this was a bug in fiber but it turns out that the documented behavior for middleware is that ctx.Route().Path is built up incrementally in the middleware chain. The path label should likely be removed from the requests in flight gauge entirely. The only path data that can be accessed early in the middleware chain is ctx.Path() which would lead to non-paramaterized paths in the metrics endpoint which would be a less than ideal outcome, every variant of /id/N would appear vs /id/:id with the correct number of hits. For the other collectors, the full path should be available as a label but it should be collected after ctx.Next() has been called as the official documentation suggests.

Hi @ryan-yalo thanks for reporting this and you are absolutely correct. It seems that the error started occurring between the two releases. v2.2.0 => v2.1.2

Could you please try this in your project - go get github.com/ansrivas/fiberprometheus/v2@v2.1.2

Hi @ansrivas, I took a look at the difference between those versions and it appears that v2.1.2 collects the path variable differently, using string(ctx.Context().Path()) vs. ctx.Route().Path. Unfortunately, that leads to an issue with paths that have parameters, like in my id example above. Each unique ID will be its own separate line in the metrics endpoint's output and that can grow really large. You can imagine on a larger service with millions of IDs that hitting the metrics endpoint at all could start to take a really long time. The example code I have below confirms this is the case. I think the correct thing to do is to do what you're doing in v2.2.0 and get it from the route path. But, for that to succeed, you need to have Next get called and the only way I can see to have defer ps.requestInFlight.WithLabelValues(method, path).Dec() work would be to deep copy the path string with something to the effect of path := string([]byte(ctx.Route().Path)) and then after Next() happens, get the path again and use unsafe code to mutate the underlying data in the path string. Which is why I said initially, I think it might be best to just drop the label entirely. Unsafely mutating strings is kind of a no-no. That being said however, fiber is based on fasthttp and their zero allocations guarantee is based on unsafely recycling string pointers so possibly not off the table entirely, I would just feel reluctant to go that route.

package main

import (
	"fmt"
	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New(fiber.Config{DisableStartupMessage: true})
	app.Use(
		func(ctx *fiber.Ctx) error {
			fmt.Println(string(ctx.Context().Path()))
			err := ctx.Next()
			fmt.Println(string(ctx.Context().Path()))
			return err
		},
	)
	app.Get("/path/:path", func(ctx *fiber.Ctx) error {
		return nil
	})
	err := app.Listen(":3000")
	if err != nil {
		panic(err)
	}
}