From e9263e2a8203422412526b929eae7a127aa734f9 Mon Sep 17 00:00:00 2001 From: Joao Paulo Vieira Date: Fri, 30 Jan 2026 15:40:33 -0300 Subject: [PATCH 1/3] Add support to send additional attributes on any counter metric --- request.go | 38 +++++++++++++++++++++++--------------- request_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/request.go b/request.go index 9c2cb13..7290f77 100644 --- a/request.go +++ b/request.go @@ -13,12 +13,13 @@ import ( ) type Request struct { - alias string - chainCallback Callback - hostURL *url.URL - metrics Metrics - restyRequest *resty.Request - startTime time.Time + alias string + chainCallback Callback + hostURL *url.URL + metrics Metrics + additionalAttrs map[string]string + restyRequest *resty.Request + startTime time.Time } // NewRequest creates a request for the specified HTTP method. @@ -36,6 +37,11 @@ func (r *Request) HostURL() *url.URL { return r.hostURL } +func (r *Request) SetMetricsAttrs(attrs map[string]string) *Request { + r.additionalAttrs = attrs + return r +} + // SetHostURL sets the host url for the request. func (r *Request) SetHostURL(url *url.URL) *Request { r.hostURL = url @@ -131,6 +137,11 @@ func (r *Request) Execute(method string, url string) (*Response, error) { metricsAlias = strings.Replace(metricsAlias, ".", "-", -1) + attrs := r.additionalAttrs + if len(r.additionalAttrs) == 0 { + attrs = map[string]string{} + } + return registerMetrics(metricsAlias, r.metrics, func() (*Response, error) { execute := func() (*Response, error) { r.startTime = time.Now() @@ -142,24 +153,21 @@ func (r *Request) Execute(method string, url string) (*Response, error) { } return r.chainCallback(execute) - }) + }, attrs) } -func registerMetrics(key string, metrics Metrics, f func() (*Response, error)) (*Response, error) { +func registerMetrics(key string, metrics Metrics, f func() (*Response, error), additionalAttrs map[string]string) (*Response, error) { resp, err := f() if metrics != nil { go func(resp *Response, err error) { - var attrs map[string]string if resp != nil { - attrs = map[string]string{ - "host": resp.Request().HostURL().Host, - "path": resp.Request().HostURL().Path, - } + additionalAttrs["host"] = resp.Request().HostURL().Host + additionalAttrs["path"] = resp.Request().HostURL().Path metrics.PushToSeries(fmt.Sprintf("%s.%s", key, "response_time"), resp.ResponseTime().Seconds()) if resp.statusCode != 0 { metrics.IncrCounter(fmt.Sprintf("%s.status.%d", key, resp.StatusCode())) - attrs["status"] = fmt.Sprintf("%d", resp.StatusCode()) + additionalAttrs["status"] = fmt.Sprintf("%d", resp.StatusCode()) } } if err != nil { @@ -169,7 +177,7 @@ func registerMetrics(key string, metrics Metrics, f func() (*Response, error)) ( metrics.IncrCounter(fmt.Sprintf("%s.%s", key, "errors")) } } - metrics.IncrCounterWithAttrs(fmt.Sprintf("%s.%s", key, "total"), attrs) + metrics.IncrCounterWithAttrs(fmt.Sprintf("%s.%s", key, "total"), additionalAttrs) }(resp, err) } diff --git a/request_test.go b/request_test.go index c2db8b1..e341f8e 100644 --- a/request_test.go +++ b/request_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "net/url" "testing" + "time" "github.com/globocom/httpclient" @@ -160,3 +161,31 @@ func testSetHostURL(target *httpclient.Request) func(*testing.T) { assert.Nil(t, target.HostURL()) } } + +func TestSetMetricsAttrs_PropagatesToMetrics(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(handleFunc)) + defer server.Close() + + metrics := &mockMetrics{} + client := httpclient.NewHTTPClient( + &httpclient.LoggerAdapter{Writer: io.Discard}, + httpclient.WithHostURL(server.URL), + httpclient.WithMetrics(metrics), + ) + request := client.NewRequest() + + attrs := map[string]string{"foo": "bar", "baz": "qux"} + request.SetMetricsAttrs(attrs) + _, _ = request.Get("/") + + time.Sleep(100 * time.Millisecond) + + found := false + for _, calledAttrs := range metrics.incrCounterWithAttrsCalls { + if calledAttrs.attrs["foo"] == "bar" && calledAttrs.attrs["baz"] == "qux" { + found = true + break + } + } + assert.True(t, found, "Attributes passed to SetMetricsAttrs must be propagated to IncrCounterWithAttrs") +} From 4babe4b1a81c18f43b28a168e08274c595166bd3 Mon Sep 17 00:00:00 2001 From: Joao Paulo Vieira Date: Sun, 15 Mar 2026 22:31:16 -0300 Subject: [PATCH 2/3] Add zero value for additional attributes --- request.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/request.go b/request.go index 7290f77..ace6c20 100644 --- a/request.go +++ b/request.go @@ -25,10 +25,11 @@ type Request struct { // NewRequest creates a request for the specified HTTP method. func (c *HTTPClient) NewRequest() *Request { return &Request{ - restyRequest: c.resty.NewRequest(), - chainCallback: c.callbackChain, - metrics: c.metrics, - hostURL: c.hostURL, + restyRequest: c.resty.NewRequest(), + chainCallback: c.callbackChain, + metrics: c.metrics, + hostURL: c.hostURL, + additionalAttrs: map[string]string{}, } } @@ -137,11 +138,6 @@ func (r *Request) Execute(method string, url string) (*Response, error) { metricsAlias = strings.Replace(metricsAlias, ".", "-", -1) - attrs := r.additionalAttrs - if len(r.additionalAttrs) == 0 { - attrs = map[string]string{} - } - return registerMetrics(metricsAlias, r.metrics, func() (*Response, error) { execute := func() (*Response, error) { r.startTime = time.Now() @@ -153,7 +149,7 @@ func (r *Request) Execute(method string, url string) (*Response, error) { } return r.chainCallback(execute) - }, attrs) + }, r.additionalAttrs) } func registerMetrics(key string, metrics Metrics, f func() (*Response, error), additionalAttrs map[string]string) (*Response, error) { From a46d907b413141ab25f577b6d9267448b3bc2e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20da=20Silva=20Vieira?= Date: Sat, 21 Mar 2026 19:55:35 -0300 Subject: [PATCH 3/3] Send error value on additionalAttrs --- request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request.go b/request.go index 7816a8e..929903f 100644 --- a/request.go +++ b/request.go @@ -171,7 +171,7 @@ func registerMetrics(key string, metrics Metrics, f func() (*Response, error), a metrics.IncrCounter(fmt.Sprintf("%s.%s", key, "circuit_open")) } else { metrics.IncrCounter(fmt.Sprintf("%s.%s", key, "errors")) - attrs["error"] = err.Error() + additionalAttrs["error"] = err.Error() } } metrics.IncrCounterWithAttrs(fmt.Sprintf("%s.%s", key, "total"), additionalAttrs)