Skip to content

Commit

Permalink
feat(metrics): measure req duration in httpwrapper by default
Browse files Browse the repository at this point in the history
  • Loading branch information
laouji authored and paul-nicolas committed Dec 16, 2024
1 parent 209d11f commit 7ad08e6
Show file tree
Hide file tree
Showing 51 changed files with 107 additions and 81 deletions.
30 changes: 25 additions & 5 deletions internal/connectors/httpwrapper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"net/http"
"time"

"github.com/formancehq/payments/internal/connectors/metrics"
"github.com/hashicorp/go-hclog"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"golang.org/x/oauth2"
)

Expand All @@ -33,11 +36,12 @@ var (
type Client interface {
// Do performs an HTTP request while handling errors and unmarshaling success and error responses into the provided interfaces
// expectedBody and errorBody should be pointers to structs
Do(req *http.Request, expectedBody, errorBody any) (statusCode int, err error)
Do(ctx context.Context, req *http.Request, expectedBody, errorBody any) (statusCode int, err error)
}

type client struct {
httpClient *http.Client
httpClient *http.Client
commonMetricsAttributes []attribute.KeyValue

httpErrorCheckerFn func(statusCode int) error
}
Expand Down Expand Up @@ -66,17 +70,33 @@ func NewClient(config *Config) (Client, error) {
config.HttpErrorCheckerFn = defaultHttpErrorCheckerFn
}

metricsAttributes := make([]attribute.KeyValue, 0)
for i := range config.CommonMetricsAttributes {
metricsAttributes = append(metricsAttributes, config.CommonMetricsAttributes[i])
}

return &client{
httpErrorCheckerFn: config.HttpErrorCheckerFn,
httpClient: httpClient,
httpErrorCheckerFn: config.HttpErrorCheckerFn,
httpClient: httpClient,
commonMetricsAttributes: metricsAttributes,
}, nil
}

func (c *client) Do(req *http.Request, expectedBody, errorBody any) (int, error) {
func (c *client) Do(ctx context.Context, req *http.Request, expectedBody, errorBody any) (int, error) {
start := time.Now()
attrs := c.commonMetricsAttributes
attrs = append(attrs, attribute.String("endpoint", req.URL.Path))
defer func() {
f := metrics.GetMetricsRegistry().ConnectorPSPCallLatencies().Record
opts := metric.WithAttributes(attrs...)
f(ctx, time.Since(start).Milliseconds(), opts)
}()

resp, err := c.httpClient.Do(req)
if err != nil {
return 0, fmt.Errorf("failed to make request: %w", err)
}
attrs = append(attrs, attribute.Int("status", resp.StatusCode))

reqErr := c.httpErrorCheckerFn(resp.StatusCode)
// the caller doesn't care about the response body so we return early
Expand Down
9 changes: 5 additions & 4 deletions internal/connectors/httpwrapper/client_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package httpwrapper_test

import (
"context"
"net/http"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -64,7 +65,7 @@ var _ = Describe("ClientWrapper", func() {
Expect(err).To(BeNil())

res := &successRes{}
code, doErr := client.Do(req, res, nil)
code, doErr := client.Do(context.Background(), req, res, nil)
Expect(code).To(Equal(http.StatusOK))
Expect(doErr).To(BeNil())
Expect(res.ID).To(Equal("someid"))
Expand All @@ -74,7 +75,7 @@ var _ = Describe("ClientWrapper", func() {
Expect(err).To(BeNil())

res := &errorRes{}
code, doErr := client.Do(req, &successRes{}, res)
code, doErr := client.Do(context.Background(), req, &successRes{}, res)
Expect(code).To(Equal(http.StatusInternalServerError))
Expect(doErr).To(MatchError(httpwrapper.ErrStatusCodeServerError))
Expect(res.Code).To(Equal("err123"))
Expand All @@ -84,7 +85,7 @@ var _ = Describe("ClientWrapper", func() {
Expect(err).To(BeNil())

res := &errorRes{}
code, doErr := client.Do(req, &successRes{}, res)
code, doErr := client.Do(context.Background(), req, &successRes{}, res)
Expect(code).To(Equal(http.StatusBadRequest))
Expect(doErr).To(MatchError(httpwrapper.ErrStatusCodeClientError))
Expect(res.Code).To(Equal("err123"))
Expand All @@ -94,7 +95,7 @@ var _ = Describe("ClientWrapper", func() {
Expect(err).To(BeNil())

res := &errorRes{}
code, doErr := client.Do(req, &successRes{}, res)
code, doErr := client.Do(context.Background(), req, &successRes{}, res)
Expect(code).To(Equal(0))
Expect(doErr).To(MatchError(ContainSubstring("failed to make request")))
})
Expand Down
4 changes: 3 additions & 1 deletion internal/connectors/httpwrapper/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"net/http"
"time"

"go.opentelemetry.io/otel/attribute"
"golang.org/x/oauth2/clientcredentials"
)

type Config struct {
HttpErrorCheckerFn func(code int) error
HttpErrorCheckerFn func(code int) error
CommonMetricsAttributes []attribute.KeyValue

Timeout time.Duration
Transport http.RoundTripper
Expand Down
3 changes: 3 additions & 0 deletions internal/connectors/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package metrics

import (
"github.com/hashicorp/go-hclog"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/noop"
)

var registry MetricsRegistry

func GetMetricsRegistry() MetricsRegistry {
hclog.Default().Info("get metrics registry", "name", "payments")
if registry == nil {
registry = NewNoOpMetricsRegistry()
}
Expand All @@ -27,6 +29,7 @@ type metricsRegistry struct {

func RegisterMetricsRegistry(meterProvider metric.MeterProvider) (MetricsRegistry, error) {
meter := meterProvider.Meter("payments")
hclog.Default().Info("REGISTERING METER", "name", "payments")

connectorPSPCalls, err := meter.Int64Counter(
"payments_connectors_psp_calls",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (c *Client) GetAccounts(ctx context.Context, page int, pageSize int, fromOp
}

res := response{Result: make([]Account, 0)}
statusCode, err := c.httpClient.Do(req, &res, nil)
statusCode, err := c.httpClient.Do(ctx, req, &res, nil)
if err != nil {
return nil, fmt.Errorf("failed to get accounts, status code %d: %w", statusCode, err)
}
Expand All @@ -90,7 +90,7 @@ func (c *Client) GetAccount(ctx context.Context, accountID string) (*Account, er
req.Header.Set("Authorization", "Bearer "+c.accessToken)

var account Account
statusCode, err := c.httpClient.Do(req, &account, nil)
statusCode, err := c.httpClient.Do(ctx, req, &account, nil)
if err != nil {
return nil, fmt.Errorf("failed to get account, status code %d: %w", statusCode, err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (c *Client) login(ctx context.Context) error {

var res response
var errors []responseError
statusCode, err := c.httpClient.Do(req, &res, &errors)
statusCode, err := c.httpClient.Do(ctx, req, &res, &errors)
if err != nil {
if len(errors) > 0 {
log.Printf("bankingcircle auth failed with code %s: %s", errors[0].ErrorCode, errors[0].ErrorText)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (c *Client) GetPayments(ctx context.Context, page int, pageSize int) ([]Pay
}

res := response{Result: make([]Payment, 0)}
statusCode, err := c.httpClient.Do(req, &res, nil)
statusCode, err := c.httpClient.Do(ctx, req, &res, nil)
if err != nil {
return nil, fmt.Errorf("failed to get payments, status code %d: %w", statusCode, err)
}
Expand All @@ -133,7 +133,7 @@ func (c *Client) GetPayment(ctx context.Context, paymentID string) (*Payment, er
req.Header.Set("Authorization", "Bearer "+c.accessToken)

var res Payment
statusCode, err := c.httpClient.Do(req, &res, nil)
statusCode, err := c.httpClient.Do(ctx, req, &res, nil)
if err != nil {
return nil, fmt.Errorf("failed to get payment, status code %d: %w", statusCode, err)
}
Expand Down Expand Up @@ -161,7 +161,7 @@ func (c *Client) GetPaymentStatus(ctx context.Context, paymentID string) (*Statu
req.Header.Set("Authorization", "Bearer "+c.accessToken)

var res StatusResponse
statusCode, err := c.httpClient.Do(req, &res, nil)
statusCode, err := c.httpClient.Do(ctx, req, &res, nil)
if err != nil {
return nil, fmt.Errorf("failed to get payment status, status code %d: %w", statusCode, err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (c *Client) InitiateTransferOrPayouts(ctx context.Context, transferRequest
req.Header.Set("Authorization", "Bearer "+c.accessToken)

var res PaymentResponse
statusCode, err := c.httpClient.Do(req, &res, nil)
statusCode, err := c.httpClient.Do(ctx, req, &res, nil)
if err != nil {
return nil, fmt.Errorf("failed to make payout, status code %d: %w", statusCode, err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (c *client) GetAccounts(ctx context.Context, page int, pageSize int) ([]*Ac

res := response{Accounts: make([]*Account, 0)}
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, 0, fmt.Errorf("failed to get accounts: %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (c *client) authenticate(ctx context.Context) error {

var res response
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return fmt.Errorf("failed to get authenticate: %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *client) GetBalances(ctx context.Context, page int, pageSize int) ([]*Ba

res := response{Balances: make([]*Balance, 0)}
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, 0, fmt.Errorf("failed to get balances %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *client) GetBeneficiaries(ctx context.Context, page int, pageSize int) (

res := response{Beneficiaries: make([]*Beneficiary, 0)}
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, 0, fmt.Errorf("failed to get beneficiaries %w, %w", err, errRes.Error())
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (c *client) GetContactID(ctx context.Context, accountID string) (*Contact,

res := Contacts{Contacts: make([]*Contact, 0)}
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, fmt.Errorf("failed to get contacts %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (c *client) InitiatePayout(ctx context.Context, payoutRequest *PayoutReques

var payoutResponse PayoutResponse
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &payoutResponse, &errRes)
_, err = c.httpClient.Do(ctx, req, &payoutResponse, &errRes)
if err != nil {
return nil, fmt.Errorf("failed to create payout: %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (c *client) GetTransactions(ctx context.Context, page int, pageSize int, up

res := response{Transactions: make([]Transaction, 0)}
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, 0, fmt.Errorf("failed to get transactions: %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (c *client) InitiateTransfer(ctx context.Context, transferRequest *Transfer

var res TransferResponse
var errRes currencyCloudError
_, err = c.httpClient.Do(req, &res, &errRes)
_, err = c.httpClient.Do(ctx, req, &res, &errRes)
if err != nil {
return nil, fmt.Errorf("failed to create transfer: %w, %w", err, errRes.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (c *client) createBankAccount(ctx context.Context, endpoint string, req any
httpReq.Header.Set("Content-Type", "application/json")

var bankAccount BankAccount
statusCode, err := c.httpClient.Do(httpReq, &bankAccount, nil)
statusCode, err := c.httpClient.Do(ctx, httpReq, &bankAccount, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to create bank account: %w", err), statusCode)
}
Expand Down Expand Up @@ -162,7 +162,7 @@ func (c *client) GetBankAccounts(ctx context.Context, userID string, page, pageS
req.URL.RawQuery = q.Encode()

var bankAccounts []BankAccount
statusCode, err := c.httpClient.Do(req, &bankAccounts, nil)
statusCode, err := c.httpClient.Do(ctx, req, &bankAccounts, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get bank accounts: %w", err), statusCode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (c *client) GetPayin(ctx context.Context, payinID string) (*PayinResponse,
}

var payinResponse PayinResponse
statusCode, err := c.httpClient.Do(req, &payinResponse, nil)
statusCode, err := c.httpClient.Do(ctx, req, &payinResponse, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get payin: %w", err), statusCode)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/connectors/plugins/public/mangopay/client/payout.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (c *client) InitiatePayout(ctx context.Context, payoutRequest *PayoutReques
req.Header.Set("Idempotency-Key", payoutRequest.Reference)

var payoutResponse PayoutResponse
statusCode, err := c.httpClient.Do(req, &payoutResponse, nil)
statusCode, err := c.httpClient.Do(ctx, req, &payoutResponse, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to initiate payout: %w", err), statusCode)
}
Expand All @@ -85,7 +85,7 @@ func (c *client) GetPayout(ctx context.Context, payoutID string) (*PayoutRespons
}

var payoutResponse PayoutResponse
statusCode, err := c.httpClient.Do(req, &payoutResponse, nil)
statusCode, err := c.httpClient.Do(ctx, req, &payoutResponse, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get payout: %w", err), statusCode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (c *client) GetRefund(ctx context.Context, refundID string) (*Refund, error
}

var refund Refund
statusCode, err := c.httpClient.Do(req, &refund, nil)
statusCode, err := c.httpClient.Do(ctx, req, &refund, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get refund: %w", err), statusCode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *client) GetTransactions(ctx context.Context, walletsID string, page, pa
req.URL.RawQuery = q.Encode()

var payments []Payment
statusCode, err := c.httpClient.Do(req, &payments, nil)
statusCode, err := c.httpClient.Do(ctx, req, &payments, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get transactions: %w", err), statusCode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (c *client) InitiateWalletTransfer(ctx context.Context, transferRequest *Tr

var transferResponse TransferResponse
var errRes mangopayError
statusCode, err := c.httpClient.Do(req, &transferResponse, &errRes)
statusCode, err := c.httpClient.Do(ctx, req, &transferResponse, &errRes)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to initiate transfer: %w %w", err, errRes.Error()), statusCode)
}
Expand All @@ -86,7 +86,7 @@ func (c *client) GetWalletTransfer(ctx context.Context, transferID string) (Tran
}

var transfer TransferResponse
statusCode, err := c.httpClient.Do(req, &transfer, nil)
statusCode, err := c.httpClient.Do(ctx, req, &transfer, nil)
if err != nil {
return transfer, errorsutils.NewErrorWithExitCode(
fmt.Errorf("failed to get transfer response: %w", err),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (c *client) GetUsers(ctx context.Context, page int, pageSize int) ([]User,
req.URL.RawQuery = q.Encode()

var users []User
statusCode, err := c.httpClient.Do(req, &users, nil)
statusCode, err := c.httpClient.Do(ctx, req, &users, nil)
if err != nil {
return nil, errorsutils.NewErrorWithExitCode(fmt.Errorf("failed to get user response: %w", err), statusCode)
}
Expand Down
Loading

0 comments on commit 7ad08e6

Please sign in to comment.