Skip to content

Commit e2859dd

Browse files
authored
Add metrics and additional logging for Data Sources (#5404)
* add: data sources metrics * remove: redundant log * fix: initMetrics * update: export initMetrics * update: remove counter; add ms unit
1 parent 9928200 commit e2859dd

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

internal/datasources/rest/handler.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ import (
1111
"io"
1212
"net/http"
1313
"strings"
14+
"sync"
1415
"time"
1516

1617
"github.com/cenkalti/backoff/v4"
18+
"github.com/rs/zerolog"
1719
"github.com/santhosh-tekuri/jsonschema/v6"
1820
uritemplate "github.com/std-uritemplate/std-uritemplate/go/v2"
21+
"go.opentelemetry.io/otel"
22+
"go.opentelemetry.io/otel/attribute"
23+
"go.opentelemetry.io/otel/metric"
1924
"google.golang.org/protobuf/types/known/structpb"
2025

2126
"github.com/mindersec/minder/internal/engine/eval/rego"
@@ -32,6 +37,12 @@ const (
3237
MaxBytesLimit int64 = 1 << 20
3338
)
3439

40+
var (
41+
metricsInit sync.Once
42+
43+
dataSourceLatencyHistogram metric.Int64Histogram
44+
)
45+
3546
type restHandler struct {
3647
rawInputSchema *structpb.Struct
3748
inputSchema *jsonschema.Schema
@@ -48,6 +59,21 @@ type restHandler struct {
4859
// TODO implement auth
4960
}
5061

62+
func initMetrics() {
63+
metricsInit.Do(func() {
64+
meter := otel.Meter("minder")
65+
var err error
66+
dataSourceLatencyHistogram, err = meter.Int64Histogram(
67+
"datasource.rest.latency",
68+
metric.WithDescription("Latency of data source requests in milliseconds"),
69+
metric.WithUnit("ms"),
70+
)
71+
if err != nil {
72+
zerolog.Ctx(context.Background()).Warn().Err(err).Msg("Creating histogram for data source requests failed")
73+
}
74+
})
75+
}
76+
5177
func newHandlerFromDef(def *minderv1.RestDataSource_Def) (*restHandler, error) {
5278
if def == nil {
5379
return nil, errors.New("rest data source handler definition is nil")
@@ -61,6 +87,8 @@ func newHandlerFromDef(def *minderv1.RestDataSource_Def) (*restHandler, error) {
6187

6288
bodyFromInput, body := parseRequestBodyConfig(def)
6389

90+
initMetrics()
91+
6492
return &restHandler{
6593
rawInputSchema: def.GetInputSchema(),
6694
inputSchema: schema,
@@ -141,14 +169,26 @@ func (h *restHandler) Call(ctx context.Context, _ *interfaces.Result, args any)
141169
return h.doRequest(cli, req)
142170
}
143171

172+
func recordMetrics(ctx context.Context, resp *http.Response, start time.Time) {
173+
attrs := []attribute.KeyValue{
174+
attribute.String("method", resp.Request.Method),
175+
attribute.String("endpoint", resp.Request.URL.String()),
176+
attribute.String("status_code", fmt.Sprintf("%d", resp.StatusCode)),
177+
}
178+
179+
dataSourceLatencyHistogram.Record(ctx, time.Since(start).Milliseconds(), metric.WithAttributes(attrs...))
180+
}
181+
144182
func (h *restHandler) doRequest(cli *http.Client, req *http.Request) (any, error) {
183+
start := time.Now()
145184
resp, err := retriableDo(cli, req)
146185
if err != nil {
147186
return nil, err
148187
}
149-
150188
defer resp.Body.Close()
151189

190+
recordMetrics(req.Context(), resp, start)
191+
152192
bout, err := h.parseResponseBody(resp.Body)
153193
if err != nil {
154194
return nil, err
@@ -257,21 +297,36 @@ func buildRestOutput(statusCode int, body any) any {
257297

258298
func retriableDo(cli *http.Client, req *http.Request) (*http.Response, error) {
259299
var resp *http.Response
300+
retryCount := 0
301+
260302
err := backoff.Retry(func() error {
261303
var err error
262304
resp, err = cli.Do(req)
263305
if err != nil {
306+
zerolog.Ctx(req.Context()).Debug().
307+
Err(err).
308+
Int("retry", retryCount).
309+
Msg("HTTP request failed, retrying")
310+
retryCount++
264311
return err
265312
}
266313

267314
if resp.StatusCode == http.StatusTooManyRequests {
315+
zerolog.Ctx(req.Context()).Debug().
316+
Int("retry", retryCount).
317+
Msg("rate limited, retrying")
318+
retryCount++
268319
return errors.New("rate limited")
269320
}
270321

271322
return nil
272323
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
273324

274325
if err != nil {
326+
zerolog.Ctx(req.Context()).Warn().
327+
Err(err).
328+
Int("retries", retryCount).
329+
Msg("HTTP request failed after retries")
275330
return nil, err
276331
}
277332

internal/datasources/rest/handler_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ func Test_restHandler_Call(t *testing.T) {
253253
parse: tt.fields.parse,
254254
testOnlyTransport: http.DefaultTransport,
255255
}
256+
initMetrics()
257+
256258
got, err := h.Call(context.Background(), nil, tt.args.args)
257259
if tt.wantErr {
258260
assert.Error(t, err)

0 commit comments

Comments
 (0)