1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
package metric
import (
"context"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
otelmetric "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
)
const (
rateLimiterBlockDurationName = "limiter_block_duration"
rateLimiterLimitName = "limiter_limit"
allowedAttr attribute.Key = "allowed"
limiterNameAttr attribute.Key = "limiter_name"
limiterLimitUnitAttr attribute.Key = "unit"
)
type LimiterWrapper struct {
nameAttr attribute.KeyValue
tr trace.Tracer
hist otelmetric.Float64Histogram
}
func NewLimiterWrapper(limiterName string, limit float64, limitUnit string, m otelmetric.Meter, tr trace.Tracer) (*LimiterWrapper, error) {
nameAttr := limiterNameAttr.String(limiterName)
limitAttrs := otelmetric.WithAttributeSet(attribute.NewSet(
nameAttr,
limiterLimitUnitAttr.String(limitUnit),
)) // allocate once
_, err := m.Float64ObservableGauge(
rateLimiterLimitName,
otelmetric.WithDescription("Limit for the rate limiter"),
otelmetric.WithFloat64Callback(func(ctx context.Context, observer otelmetric.Float64Observer) error {
observer.Observe(limit, limitAttrs)
return nil
}),
)
if err != nil {
return nil, err
}
hist, err := m.Float64Histogram(
rateLimiterBlockDurationName,
otelmetric.WithUnit("s"),
otelmetric.WithDescription("Duration the rate limiter blocked for deciding to allow/block the call"),
otelmetric.WithExplicitBucketBoundaries(0.001, 0.004, 0.016, 0.064, 0.256, 1.024, 4.096, 16.384),
)
if err != nil {
return nil, err
}
return &LimiterWrapper{
nameAttr: nameAttr,
tr: tr,
hist: hist,
}, nil
}
func (w *LimiterWrapper) Start(ctx context.Context) (context.Context, func(allowed bool)) {
start := time.Now()
ctx, span := w.tr.Start(ctx, "limiter", trace.WithSpanKind(trace.SpanKindInternal)) //nolint:spancheck
return ctx, func(allowed bool) { //nolint:spancheck
duration := float64(time.Since(start)) / float64(time.Second)
// Pass background context because we always want to record the duration.
w.hist.Record(context.Background(), duration, otelmetric.WithAttributeSet(attribute.NewSet(
allowedAttr.Bool(allowed),
w.nameAttr,
)))
code := codes.Ok
if !allowed {
code = codes.Error
}
span.SetStatus(code, "")
span.End()
}
}
|