ansrivas / fiberprometheus

prometheus middleware for Fiber
MIT License
172 stars 37 forks source link

incorrect path when using groups #187

Closed FedeBev closed 7 months ago

FedeBev commented 11 months ago

Hi there, thanks for this work!

When using a.Group the path is not correct.

With the following app

app := fiber.New()

prometheus := fiberprometheus.NewWithLabels(labels, strings.ReplaceAll(Component, "-", "_"), "http")
prometheus.RegisterAt(app, "/metrics")
app.Use(prometheus.Middleware)

public := app.Group("/public")

public.Get("/somepath", func(c *fiber.Ctx) error {
   ...
})

The following metrics are created when calling GET /public/somepath

lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2e-09"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5e-09"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1e-08"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2e-08"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5e-08"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1e-07"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2e-07"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5e-07"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1e-06"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2e-06"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5e-06"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1e-05"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2e-05"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5e-05"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0001"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0002"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0005"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.001"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.002"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.005"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.01"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.02"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.05"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.1"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.2"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.5"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="10"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="15"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="20"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="30"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="+Inf"} 1
lessons_cdn_http_request_duration_seconds_sum{build="development",method="GET",path="/public",status_code="401",version="development"} 8.253e-05
lessons_cdn_http_request_duration_seconds_count{build="development",method="GET",path="/public",status_code="401",version="development"} 1

I'd expect to see /public/somepath in the path label.

Thanks!

ansrivas commented 11 months ago

Hey @FedeBev, thanks for reporting this. So, in your example I just moved the middleware mount a bit later, please try and see if this solves your issue.

    prometheus := fiberprometheus.New("my-service-name")

    public := app.Group("/public")  // Here declared the Group before calling RegisterAt and app.Use

    prometheus.RegisterAt(app, "/metrics")
    app.Use(prometheus.Middleware)

    public.Get("/somepath", func(c *fiber.Ctx) error {
        return c.SendString("Hello World from somepath")
    })

And corresponding to that, I can see the following:


http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="1e-09"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="2e-09"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="5e-09"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="1e-08"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="2e-08"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="5e-08"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="1e-07"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="2e-07"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="5e-07"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="1e-06"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="2e-06"} 0
http_request_duration_seconds_bucket{method="GET",path="/public/somepath",service="my-service-name",status_code="200",le="5e-06"} ```
FedeBev commented 11 months ago

Interesting, I tried your suggestion but I'm still seeing the same

app := fiber.New()

public := app.Group("/public")
admin := app.Group("/admin")

prometheus := fiberprometheus.NewWithLabels(labels, strings.ReplaceAll(Component, "-", "_"), "http")
prometheus.RegisterAt(app, "/metrics")

app.Use(prometheus.Middleware)

public.Get("/somepath", func(c *fiber.Ctx) error {
   ...
})
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0001"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0002"} 0
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.0005"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.001"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.002"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.005"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.01"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.02"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.05"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.1"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.2"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="0.5"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="1"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="2"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="5"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="10"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="15"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="20"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="30"} 1
lessons_cdn_http_request_duration_seconds_bucket{build="development",method="GET",path="/public",status_code="401",version="development",le="+Inf"} 1
ansrivas commented 11 months ago

This is certainly strange, here is the complete example, can you please try this:

package main

import (
    "github.com/ansrivas/fiberprometheus/v2"  // v2.6.1
    "github.com/gofiber/fiber/v2"    // v2.48.0
)

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

    public := app.Group("/public")
    admin := app.Group("/admin")

    prometheus := fiberprometheus.NewWithLabels(map[string]string{
        "component": "fiberprometheus",
    }, "somesystem", "http")

    prometheus.RegisterAt(app, "/metrics")
    app.Use(prometheus.Middleware)

    public.Get("/somepath", func(c *fiber.Ctx) error {
        return c.SendString("Hello World from somepath")
    })
    admin.Get("/adminpath", func(c *fiber.Ctx) error {
        return c.SendString("Hello World from adminpath")
    })

    app.Listen(":3000")
}
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="1"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="2"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="5"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="10"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="15"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="20"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="30"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200",le="+Inf"} 1
somesystem_http_request_duration_seconds_sum{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200"} 2.1273e-05
somesystem_http_request_duration_seconds_count{component="fiberprometheus",method="GET",path="/admin/adminpath",status_code="200"} 1
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="1e-09"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="2e-09"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="5e-09"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="1e-08"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="2e-08"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="5e-08"} 0
somesystem_http_request_duration_seconds_bucket{component="fiberprometheus",method="GET",path="/public/somepath",status_code="200",le="1e-07"} 0
FedeBev commented 11 months ago

I think I got what's going on

I think I was able to identify when the path is not correct (at least some cases)

public := app.Group("/public")

prometheus := fiberprometheus.NewWithLabels(labels, strings.ReplaceAll(Component, "-", "_"), "http")
prometheus.RegisterAt(app, "/metrics")
app.Use(prometheus.Middleware)

// if this returns, the path in the metric is /public instead of /lesson/:id/:page
public.Use(auth.Middleware(datasource)) 
public.Get("/lesson/:id/:page", func(c *fiber.Ctx) error {})
github-actions[bot] commented 9 months ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 8 months ago

This issue was closed because it has been stalled for 10 days with no activity.

Fesaa commented 8 months ago

Heya!

I've noticed this as well with one of my apps. The comment above was what I noticed as well, the / paths were all cache hits for me.

I've added a test here, that makes it super clear. https://github.com/Fesaa/fiberprometheus/commit/e748341c6afd9aada719e7aa50c318fc51a0aaa9

I'm looking around a bit, but am not seeing a solution without changes within fiber at the moment.

Am planning to look a bit longer as it annoys me a lot as it makes my grafana graphs wrong 😄

gaby commented 7 months ago

@FedeBev @Fesaa I submitted a PR that includes the new cache metrics from @Fesaa and also fixes the issues @FedeBev was running into with the path's. I also fixed a concurrency issues happening with the default global registry.

This middleware is going to be migrated to https://github.com/gofiber/contrib in the next week or so. Maintenance will be done by the GoFiber team (I'm one of the maintainers).

gaby commented 7 months ago

@FedeBev @Fesaa Migration has started and is being tracked here https://github.com/gofiber/contrib/pull/1032

I have a TODO list of tasks before making a first release. Basically trying to make the middleware easier to use and more feature complete.