diff --git a/clients/gateway/event_listener.go b/clients/gateway/event_listener.go new file mode 100644 index 0000000000..818d60fe11 --- /dev/null +++ b/clients/gateway/event_listener.go @@ -0,0 +1,17 @@ +package gateway + +import "time" + +type EventListener interface { + OnResponse(urlPath string, status int, took time.Duration) +} + +type SelectiveListener struct { + OnResponseCb func(urlPath string, status int, took time.Duration) +} + +func (l *SelectiveListener) OnResponse(urlPath string, status int, took time.Duration) { + if l.OnResponseCb != nil { + l.OnResponseCb(urlPath, status, took) + } +} diff --git a/clients/gateway/gateway.go b/clients/gateway/gateway.go index 937cd79231..0978f98357 100644 --- a/clients/gateway/gateway.go +++ b/clients/gateway/gateway.go @@ -38,6 +38,7 @@ var ( type Client struct { url string client *http.Client + listener EventListener log utils.SimpleLogger userAgent string apiKey string @@ -53,6 +54,11 @@ func (c *Client) WithAPIKey(key string) *Client { return c } +func (c *Client) WithListener(l EventListener) *Client { + c.listener = l + return c +} + // NewTestClient returns a client and a function to close a test server. func NewTestClient(t *testing.T) *Client { srv := newTestServer(t) @@ -100,7 +106,8 @@ func NewClient(gatewayURL string, log utils.SimpleLogger) *Client { client: &http.Client{ Timeout: time.Minute, }, - log: log, + listener: &SelectiveListener{}, + log: log, } } @@ -153,7 +160,13 @@ func (c *Client) doPost(ctx context.Context, url string, data any) (*http.Respon if c.apiKey != "" { req.Header.Set("X-Throttling-Bypass", c.apiKey) } - return c.client.Do(req) + reqTimer := time.Now() + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + c.listener.OnResponse(req.URL.Path, resp.StatusCode, time.Since(reqTimer)) + return resp, nil } type ErrorCode string diff --git a/node/metrics.go b/node/metrics.go index bbd99ca243..c2ad598971 100644 --- a/node/metrics.go +++ b/node/metrics.go @@ -7,6 +7,7 @@ import ( "github.com/NethermindEth/juno/blockchain" "github.com/NethermindEth/juno/clients/feeder" + "github.com/NethermindEth/juno/clients/gateway" "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/db" "github.com/NethermindEth/juno/jsonrpc" @@ -245,3 +246,18 @@ func makeFeederMetrics() feeder.EventListener { }, } } + +func makeGatewayMetrics() gateway.EventListener { + requestLatencies := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "gateway", + Subsystem: "client", + Name: "request_latency", + }, []string{"method", "status"}) + prometheus.MustRegister(requestLatencies) + return &gateway.SelectiveListener{ + OnResponseCb: func(urlPath string, status int, took time.Duration) { + statusString := strconv.FormatInt(int64(status), 10) + requestLatencies.WithLabelValues(urlPath, statusString).Observe(took.Seconds()) + }, + } +} diff --git a/node/node.go b/node/node.go index 2dda6aed76..f42468c297 100644 --- a/node/node.go +++ b/node/node.go @@ -181,6 +181,7 @@ func New(cfg *Config, version string) (*Node, error) { //nolint:gocyclo,funlen jsonrpcServerLegacy.WithListener(legacyRPCMetrics) synchronizer.WithListener(makeSyncMetrics(synchronizer, chain)) client.WithListener(makeFeederMetrics()) + gatewayClient.WithListener(makeGatewayMetrics()) services = append(services, makeMetrics(cfg.MetricsHost, cfg.MetricsPort)) } if cfg.GRPC {