Skip to content

Commit

Permalink
fixed/metrics/prometheus: correctly handle prefix (#178)
Browse files Browse the repository at this point in the history
This patches fixes a bug where the sanitization function of the
prometheus metric manager was not correctly handling the given path if
it was containing a '_prefix'. This patch fixes this issue and adds
tests.

The parameter 'url' of the interface MetricManager.MeasureRequest has
also been renamed to 'path' for better usage clarity.
  • Loading branch information
primalmotion authored Mar 30, 2023
1 parent ba5e229 commit a0e0fb6
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 14 deletions.
2 changes: 1 addition & 1 deletion gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type fakeMetricManager struct {
unregisterTCPConnectionCalled int64
}

func (m *fakeMetricManager) MeasureRequest(method string, url string) bahamut.FinishMeasurementFunc {
func (m *fakeMetricManager) MeasureRequest(method string, path string) bahamut.FinishMeasurementFunc {
return func(code int, span opentracing.Span) time.Duration { return 0 }
}

Expand Down
2 changes: 1 addition & 1 deletion health_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func freePort() (port int) {
// A MetricsManager handles Prometheus Metrics Management
type testMetricsManager struct{}

func (m *testMetricsManager) MeasureRequest(method string, url string) FinishMeasurementFunc {
func (m *testMetricsManager) MeasureRequest(method string, path string) FinishMeasurementFunc {
return nil
}
func (m *testMetricsManager) RegisterWSConnection() {}
Expand Down
2 changes: 1 addition & 1 deletion metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type FinishMeasurementFunc func(code int, span opentracing.Span) time.Duration

// A MetricsManager handles Prometheus Metrics Management
type MetricsManager interface {
MeasureRequest(method string, url string) FinishMeasurementFunc
MeasureRequest(method string, path string) FinishMeasurementFunc
RegisterWSConnection()
UnregisterWSConnection()
RegisterTCPConnection()
Expand Down
27 changes: 19 additions & 8 deletions metrics_prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,31 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
)

var vregexp = regexp.MustCompile(`^/v/\d+`)
var (
pregexp = regexp.MustCompile(`^/_[a-zA-Z0-9-_+]+`)
vregexp = regexp.MustCompile(`^/v/\d+`)
)

func sanitizeURL(url string) string {
func sanitizePath(url string) string {

prefix := "/"
matches := pregexp.FindAllString(url, 1)
if len(matches) == 1 {
prefix = matches[0] + "/"
url = strings.TrimPrefix(url, matches[0])
}
url = vregexp.ReplaceAllString(url, "")
url = strings.TrimPrefix(url, "/")

parts := strings.Split(url, "/")
if len(parts) <= 2 {
return url

if len(parts) <= 1 {
return prefix + url
}

parts[2] = ":id"
parts[1] = ":id"

return strings.Join(parts, "/")
return prefix + strings.Join(parts, "/")
}

type prometheusMetricsManager struct {
Expand Down Expand Up @@ -119,9 +130,9 @@ func newPrometheusMetricsManager(registerer prometheus.Registerer) MetricsManage
return mc
}

func (c *prometheusMetricsManager) MeasureRequest(method string, url string) FinishMeasurementFunc {
func (c *prometheusMetricsManager) MeasureRequest(method string, path string) FinishMeasurementFunc {

surl := sanitizeURL(url)
surl := sanitizePath(path)

timer := prometheus.NewTimer(
prometheus.ObserverFunc(
Expand Down
49 changes: 46 additions & 3 deletions metrics_prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,53 @@ func Test_sanitizeURL(t *testing.T) {
},
"/toto/:id/titi",
},

{
"test /_prefix/toto",
args{
"/_prefix/toto",
},
"/_prefix/toto",
},
{
"test /_prefix/v/1/toto",
args{
"/_prefix/v/1/toto",
},
"/_prefix/toto",
},
{
"test /_prefix/toto/xxxxxxx",
args{
"/_prefix/toto/xxxxxxx",
},
"/_prefix/toto/:id",
},
{
"test /_prefix/v/1/toto/xxxxxxx",
args{
"/_prefix/v/1/toto/xxxxxxx",
},
"/_prefix/toto/:id",
},
{
"test /_prefix/toto/xxxxxxx/titi",
args{
"/_prefix/toto/xxxxxxx/titi",
},
"/_prefix/toto/:id/titi",
},
{
"test /_prefix/v/1/toto/xxxxxxx/titi",
args{
"/_prefix/v/1/toto/xxxxxxx/titi",
},
"/_prefix/toto/:id/titi",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := sanitizeURL(tt.args.url); got != tt.want {
if got := sanitizePath(tt.args.url); got != tt.want {
t.Errorf("sanitizeURL() = %v, want %v", got, tt.want)
}
})
Expand Down Expand Up @@ -104,7 +147,7 @@ func TestMeasureRequest(t *testing.T) {

Convey("When I call measure a 502 request", func() {

f := pmm.MeasureRequest("GET", "http://toto.com/id/toto")
f := pmm.MeasureRequest("GET", "/toto/id")
f(502, nil)

data, _ := r.Gather()
Expand All @@ -114,7 +157,7 @@ func TestMeasureRequest(t *testing.T) {
So(data[0].GetMetric()[0].Label[0].String(), ShouldEqual, `name:"code" value:"502" `)
So(data[0].GetMetric()[0].Label[1].String(), ShouldEqual, `name:"method" value:"GET" `)
So(data[0].GetMetric()[0].Label[2].String(), ShouldEqual, `name:"trace" value:"unknown" `)
So(data[0].GetMetric()[0].Label[3].String(), ShouldEqual, `name:"url" value:"http://:id/id/toto" `)
So(data[0].GetMetric()[0].Label[3].String(), ShouldEqual, `name:"url" value:"/toto/:id" `)
})
})
})
Expand Down

0 comments on commit a0e0fb6

Please sign in to comment.