package service import ( "context" "fmt" "github.com/synapsecns/sanguine/services/rfq/relayer/reldb" "github.com/cornelk/hashmap" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/ethergo/signer/signer" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" ) const meterName = "github.com/synapsecns/sanguine/services/rfq/relayer/service" // generate an interface for otelRecorder that exports the public method. // this allows us to avoid using recordX externally anad makes the package less confusing. // // ============================================================================= // ============================================================================= // IMPORTANT: DO NOT REMOVE THIS COMMENT. // NOTICE: PLEASE MAKE SURE YOU UPDATE BOTH THE DOCS AND THE GRAFANA DASHBOARD (IF NEEDED) AFTER UPDATING METRICS. // ============================================================================= // ============================================================================= // //go:generate go run github.com/vburenin/ifacemaker -f otel.go -s otelRecorder -i iOtelRecorder -p service -o otel_generated.go -c "autogenerated file" type otelRecorder struct { metrics metrics.Handler // meter is the metrics meter. meter metric.Meter // db is the relayer database. db reldb.Service // statusCountGauge is the gauge for the status. statusCountGauge metric.Int64ObservableGauge // rebalanceCountGauge is the gauge for the rebalances. rebalanceCountGauge metric.Int64ObservableGauge // statusCounts is used for metrics. // status -> count statusCounts *hashmap.Map[reldb.QuoteRequestStatus, int] // signer is the signer for signing transactions. signer signer.Signer } func newOtelRecorder(meterHandler metrics.Handler, db reldb.Service, signer signer.Signer) (_ iOtelRecorder, err error) { or := otelRecorder{ metrics: meterHandler, db: db, meter: meterHandler.Meter(meterName), statusCounts: hashmap.New[reldb.QuoteRequestStatus, int](), signer: signer, } or.statusCountGauge, err = or.meter.Int64ObservableGauge("status_count") if err != nil { return nil, fmt.Errorf("could not create status count gauge: %w", err) } or.rebalanceCountGauge, err = or.meter.Int64ObservableGauge("rebalance_count") if err != nil { return nil, fmt.Errorf("could not create rebalance count gauge: %w", err) } _, err = or.meter.RegisterCallback(or.recordStatusCounts, or.statusCountGauge) if err != nil { return nil, fmt.Errorf("could not register callback for status count gauge: %w", err) } _, err = or.meter.RegisterCallback(or.recordRebalanceCounts, or.rebalanceCountGauge) if err != nil { return nil, fmt.Errorf("could not register callback for rebalance count gauge: %w", err) } return &or, nil } func (o *otelRecorder) recordStatusCounts(_ context.Context, observer metric.Observer) (err error) { if o.metrics == nil || o.statusCountGauge == nil || o.statusCounts == nil { return nil } o.statusCounts.Range(func(status reldb.QuoteRequestStatus, count int) bool { opts := metric.WithAttributes( attribute.Int("status_int", int(status.Int())), attribute.String("status", status.String()), attribute.String("wallet", o.signer.Address().Hex()), ) observer.ObserveInt64(o.statusCountGauge, int64(count), opts) return true }) return nil } func (o *otelRecorder) recordRebalanceCounts(ctx context.Context, observer metric.Observer) (err error) { if o.metrics == nil || o.rebalanceCountGauge == nil || o.db == nil { return nil } rebalances, err := o.db.GetPendingRebalances(ctx) if err != nil { return fmt.Errorf("could not get pending rebalances: %w", err) } rebalanceIDs := []string{} for _, r := range rebalances { if r.RebalanceID != nil { rebalanceIDs = append(rebalanceIDs, *r.RebalanceID) } } opts := metric.WithAttributes( attribute.String("wallet", o.signer.Address().Hex()), attribute.StringSlice("rebalance_ids", rebalanceIDs), ) observer.ObserveInt64(o.rebalanceCountGauge, int64(len(rebalances)), opts) return nil } // RecordStatusCount records the request status count. func (o *otelRecorder) RecordStatusCount(status reldb.QuoteRequestStatus, count int) { o.statusCounts.Set(status, count) }