|
| 1 | +package opentelemetry |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + |
| 6 | + "github.com/puzpuzpuz/xsync/v2" |
| 7 | + "go.opentelemetry.io/otel/metric" |
| 8 | +) |
| 9 | + |
| 10 | +// Int64Histogram is a wrapper around OpenTelemetry's Int64Histogram. It allows |
| 11 | +// recording values with predefined [metric.RecordOption]. |
| 12 | +type Int64Histogram struct { |
| 13 | + histogram metric.Int64Histogram |
| 14 | + opts []metric.RecordOption |
| 15 | +} |
| 16 | + |
| 17 | +// Record adds a value to the histogram using the predefined options. |
| 18 | +func (h *Int64Histogram) Record(ctx context.Context, incr int64) { |
| 19 | + h.histogram.Record(ctx, incr, h.opts...) |
| 20 | +} |
| 21 | + |
| 22 | +// Int64HistogramSet is a wrapper around OpenTelemetry's Int64Histogram. It |
| 23 | +// includes a cache for [metric.RecordOption] to optimize performance by |
| 24 | +// reducing redundant object creation for specific keys. |
| 25 | +type Int64HistogramSet[K comparable] struct { |
| 26 | + histogram metric.Int64Histogram |
| 27 | + optsCache *recordOptionCache[K] |
| 28 | +} |
| 29 | + |
| 30 | +// NewInt64HistogramSet initializes a new Int64HistogramSet with the specified |
| 31 | +// name and options. |
| 32 | +func NewInt64HistogramSet[K comparable](meter metric.Meter, name string, opts ...metric.Int64HistogramOption) (*Int64HistogramSet[K], error) { |
| 33 | + histogram, err := meter.Int64Histogram(name, opts...) |
| 34 | + if err != nil { |
| 35 | + return nil, err |
| 36 | + } |
| 37 | + |
| 38 | + ret := &Int64HistogramSet[K]{ |
| 39 | + histogram: histogram, |
| 40 | + optsCache: newRecordOptionCache[K](), |
| 41 | + } |
| 42 | + return ret, nil |
| 43 | +} |
| 44 | + |
| 45 | +// Record adds a value to the histogram for a specific key. It reuses cached |
| 46 | +// [metric.RecordOption] if available or generates new options using getOpts. |
| 47 | +func (hs *Int64HistogramSet[K]) Record(ctx context.Context, key K, incr int64, getOpts func() []metric.RecordOption) { |
| 48 | + record(ctx, hs.histogram, key, hs.optsCache, incr, getOpts) |
| 49 | +} |
| 50 | + |
| 51 | +// GetHistogram retrieves a [Int64Histogram] for the specified key, setting its |
| 52 | +// [metric.RecordOption] in the cache for quick access. |
| 53 | +func (hs *Int64HistogramSet[K]) GetHistogram(key K, opts ...metric.RecordOption) *Int64Histogram { |
| 54 | + hs.optsCache.mu.Lock() |
| 55 | + defer hs.optsCache.mu.Unlock() |
| 56 | + hs.optsCache.opts[key] = opts |
| 57 | + return &Int64Histogram{ |
| 58 | + histogram: hs.histogram, |
| 59 | + opts: opts, |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +// Float64Histogram is a wrapper around OpenTelemetry's Float64Histogram. It |
| 64 | +// allows recording values with predefined [metric.RecordOption]. |
| 65 | +type Float64Histogram struct { |
| 66 | + histogram metric.Float64Histogram |
| 67 | + opts []metric.RecordOption |
| 68 | +} |
| 69 | + |
| 70 | +// Record adds a value to the histogram using the predefined options. |
| 71 | +func (h *Float64Histogram) Record(ctx context.Context, incr float64) { |
| 72 | + h.histogram.Record(ctx, incr, h.opts...) |
| 73 | +} |
| 74 | + |
| 75 | +// Float64HistogramSet is a wrapper around OpenTelemetry's Float64Histogram. It |
| 76 | +// includes a cache for [metric.RecordOption] to optimize performance by |
| 77 | +// reducing redundant object creation for specific keys. |
| 78 | +type Float64HistogramSet[K comparable] struct { |
| 79 | + histogram metric.Float64Histogram |
| 80 | + optsCache *recordOptionCache[K] |
| 81 | +} |
| 82 | + |
| 83 | +// NewFloat64HistogramSet initializes a new Float64HistogramSet with the |
| 84 | +// specified name and options. |
| 85 | +func NewFloat64HistogramSet[K comparable](meter metric.Meter, name string, opts ...metric.Float64HistogramOption) (*Float64HistogramSet[K], error) { |
| 86 | + histogram, err := meter.Float64Histogram(name, opts...) |
| 87 | + if err != nil { |
| 88 | + return nil, err |
| 89 | + } |
| 90 | + |
| 91 | + ret := &Float64HistogramSet[K]{ |
| 92 | + histogram: histogram, |
| 93 | + optsCache: newRecordOptionCache[K](), |
| 94 | + } |
| 95 | + return ret, nil |
| 96 | +} |
| 97 | + |
| 98 | +// Record adds a value to the histogram for a specific key. It reuses cached |
| 99 | +// [metric.RecordOption] if available or generates new options using getOpts. |
| 100 | +func (hs *Float64HistogramSet[K]) Record(ctx context.Context, key K, incr float64, getOpts func() []metric.RecordOption) { |
| 101 | + record(ctx, hs.histogram, key, hs.optsCache, incr, getOpts) |
| 102 | +} |
| 103 | + |
| 104 | +// GetHistogram retrieves a [Float64Histogram] for the specified key, setting |
| 105 | +// its [metric.RecordOption] in the cache for quick access. |
| 106 | +func (hs *Float64HistogramSet[K]) GetHistogram(key K, opts ...metric.RecordOption) *Float64Histogram { |
| 107 | + hs.optsCache.mu.Lock() |
| 108 | + defer hs.optsCache.mu.Unlock() |
| 109 | + hs.optsCache.opts[key] = opts |
| 110 | + return &Float64Histogram{ |
| 111 | + histogram: hs.histogram, |
| 112 | + opts: opts, |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +type recordOptionCache[K comparable] struct { |
| 117 | + opts map[K][]metric.RecordOption |
| 118 | + mu *xsync.RBMutex |
| 119 | +} |
| 120 | + |
| 121 | +func newRecordOptionCache[K comparable]() *recordOptionCache[K] { |
| 122 | + return &recordOptionCache[K]{ |
| 123 | + opts: make(map[K][]metric.RecordOption), |
| 124 | + mu: xsync.NewRBMutex(), |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +type recorder[T int64 | float64] interface { |
| 129 | + Record(ctx context.Context, incr T, options ...metric.RecordOption) |
| 130 | +} |
| 131 | + |
| 132 | +func record[K comparable, V int64 | float64](ctx context.Context, recorder recorder[V], key K, cache *recordOptionCache[K], incr V, getOpts func() []metric.RecordOption) { |
| 133 | + rt := cache.mu.RLock() |
| 134 | + if opts, ok := cache.opts[key]; ok { |
| 135 | + cache.mu.RUnlock(rt) |
| 136 | + recorder.Record(ctx, incr, opts...) |
| 137 | + return |
| 138 | + } |
| 139 | + cache.mu.RUnlock(rt) |
| 140 | + |
| 141 | + cache.mu.Lock() |
| 142 | + defer cache.mu.Unlock() |
| 143 | + opts, ok := cache.opts[key] |
| 144 | + if !ok { |
| 145 | + opts = getOpts() |
| 146 | + cache.opts[key] = opts |
| 147 | + } |
| 148 | + recorder.Record(ctx, incr, opts...) |
| 149 | +} |
0 commit comments