services/rfq/guard/service/otel.go
package service
import (
"context"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/services/rfq/guard/guarddb"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
const meterName = "github.com/synapsecns/sanguine/services/rfq/guard/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
// disputeGauge is the gauge for the disputeCounts.
disputeGauge metric.Int64ObservableGauge
// db is a database connection for fetching disputes.
db guarddb.Service
// walletAddress is the wallet address for the signer.
walletAddress common.Address
// mux is a mutex used for making sure only one db query is run at a time.
mux sync.Mutex
}
func newOtelRecorder(meterHandler metrics.Handler, walletAddress common.Address, db guarddb.Service) (_ iOtelRecorder, err error) {
or := otelRecorder{
metrics: meterHandler,
meter: meterHandler.Meter(meterName),
db: db,
walletAddress: walletAddress,
}
or.disputeGauge, err = or.meter.Int64ObservableGauge("dispute_count")
if err != nil {
return nil, fmt.Errorf("could not create dispute count gauge: %w", err)
}
_, err = or.meter.RegisterCallback(or.recordDisputeCounts, or.disputeGauge)
if err != nil {
return nil, fmt.Errorf("could not register callback for dispute count gauge: %w", err)
}
return &or, nil
}
func (o *otelRecorder) recordDisputeCounts(ctx context.Context, observer metric.Observer) (err error) {
if o.metrics == nil || o.disputeGauge == nil || o.db == nil {
return nil
}
// make sure we aren't making simultaneous db queries
if o.mux.TryLock() {
defer o.mux.Unlock()
} else {
return nil
}
results, err := o.db.GetPendingProvensByStatus(ctx, guarddb.DisputePending, guarddb.Disputed)
if err != nil {
return fmt.Errorf("could not get disputes: %w", err)
}
opts := metric.WithAttributes(
attribute.String("wallet", o.walletAddress.Hex()),
)
observer.ObserveInt64(o.disputeGauge, int64(len(results)), opts)
return nil
}