Skip to content

Commit 2184b7e

Browse files
committed
feat(stats): add OpenTelemetry histogram wrappers with caching
This change introduces `Int64Histogram` and `Float64Histogram` wrappers for OpenTelemetry histograms, enabling value recording with predefined options. It also adds `Int64HistogramSet` and `Float64HistogramSet` to support caching of `metric.RecordOption` for optimized performance. This enhances histogram usage by reducing redundant object creation and improving performance.
1 parent 7b8fa26 commit 2184b7e

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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

Comments
 (0)