diff --git a/group.go b/group.go index 404e39d..57c369a 100644 --- a/group.go +++ b/group.go @@ -622,5 +622,67 @@ func (g *group) registerInstruments(meter otelmetric.Meter) error { instruments.RemovedKeysCounter(), ) - return err + if err != nil { + return err + } + + // Register cache-level instruments for mainCache and hotCache + if err := g.registerCacheInstruments(meter); err != nil { + return err + } + + return nil +} + +func (g *group) registerCacheInstruments(meter otelmetric.Meter) error { + cacheInstr, err := newCacheInstruments(meter) + if err != nil { + return err + } + + type namedCache struct { + name string + cache func() Cache + } + + caches := []namedCache{ + {name: "main", cache: func() Cache { return g.mainCache }}, + {name: "hot", cache: func() Cache { return g.hotCache }}, + } + + for _, nc := range caches { + observeOptions := []otelmetric.ObserveOption{ + otelmetric.WithAttributes( + attribute.String("group.name", g.Name()), + attribute.String("cache.type", nc.name), + ), + } + + _, err := meter.RegisterCallback(func(ctx context.Context, o otelmetric.Observer) error { + c := nc.cache() + if c == nil { + return nil + } + stats := c.Stats() + o.ObserveInt64(cacheInstr.RejectedCounter(), stats.Rejected, observeOptions...) + o.ObserveInt64(cacheInstr.BytesGauge(), stats.Bytes, observeOptions...) + o.ObserveInt64(cacheInstr.ItemsGauge(), stats.Items, observeOptions...) + o.ObserveInt64(cacheInstr.GetsCounter(), stats.Gets, observeOptions...) + o.ObserveInt64(cacheInstr.HitsCounter(), stats.Hits, observeOptions...) + o.ObserveInt64(cacheInstr.EvictionsCounter(), stats.Evictions, observeOptions...) + return nil + }, + cacheInstr.RejectedCounter(), + cacheInstr.BytesGauge(), + cacheInstr.ItemsGauge(), + cacheInstr.GetsCounter(), + cacheInstr.HitsCounter(), + cacheInstr.EvictionsCounter(), + ) + if err != nil { + return err + } + } + + return nil } diff --git a/instance_test.go b/instance_test.go index cc851b9..8e6ac44 100644 --- a/instance_test.go +++ b/instance_test.go @@ -515,6 +515,7 @@ func TestNewGroupRegistersMetricsWithMeterProvider(t *testing.T) { require.NotNil(t, g) expectedCounters := []string{ + // group-level counters "groupcache.group.gets", "groupcache.group.cache_hits", "groupcache.group.peer.loads", @@ -525,11 +526,21 @@ func TestNewGroupRegistersMetricsWithMeterProvider(t *testing.T) { "groupcache.group.local.load_errors", "groupcache.group.remove_keys.requests", "groupcache.group.removed_keys", + // cache-level counters (shared by main and hot caches) + "groupcache.cache.rejected", + "groupcache.cache.gets", + "groupcache.cache.hits", + "groupcache.cache.evictions", } assert.Equal(t, expectedCounters, recMeter.counterNames) - assert.Equal(t, []string{"groupcache.group.peer.latency_max_ms"}, recMeter.updownNames) + assert.Equal(t, []string{ + "groupcache.group.peer.latency_max_ms", + "groupcache.cache.bytes", + "groupcache.cache.items", + }, recMeter.updownNames) assert.True(t, recMeter.callbackRegistered) - assert.Equal(t, 11, recMeter.instrumentCount) + // 11 group instruments + 6 cache instruments per cache (main + hot) = 11 + 6 + 6 = 23 + assert.Equal(t, 23, recMeter.instrumentCount) } func TestNewGroupFailsWhenMetricRegistrationFails(t *testing.T) { @@ -579,7 +590,7 @@ func (m *recordingMeter) Int64ObservableUpDownCounter(name string, _ ...metric.I func (m *recordingMeter) RegisterCallback(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { m.callbackRegistered = true - m.instrumentCount = len(instruments) + m.instrumentCount += len(instruments) // Invoke the callback once to ensure it tolerates being called with nil ctx/observer _ = f(context.Background(), noop.Observer{}) return noop.Registration{}, nil diff --git a/stats.go b/stats.go index 959eb46..363c7e6 100644 --- a/stats.go +++ b/stats.go @@ -280,23 +280,23 @@ func (gm *groupInstruments) RemovedKeysCounter() metric.Int64ObservableCounter { } type cacheInstruments struct { - rejectedCounter metric.Int64Counter - bytesGauge metric.Int64UpDownCounter - itemsGauge metric.Int64UpDownCounter - getsCounter metric.Int64Counter - hitsCounter metric.Int64Counter - evictionsCounter metric.Int64Counter + rejectedCounter metric.Int64ObservableCounter + bytesGauge metric.Int64ObservableUpDownCounter + itemsGauge metric.Int64ObservableUpDownCounter + getsCounter metric.Int64ObservableCounter + hitsCounter metric.Int64ObservableCounter + evictionsCounter metric.Int64ObservableCounter } func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { - rejectedCounter, err := meter.Int64Counter("groupcache.cache.rejected", + rejectedCounter, err := meter.Int64ObservableCounter("groupcache.cache.rejected", metric.WithDescription("Total number of items rejected from cache"), ) if err != nil { return nil, err } - bytesGauge, err := meter.Int64UpDownCounter( + bytesGauge, err := meter.Int64ObservableUpDownCounter( "groupcache.cache.bytes", metric.WithDescription("Number of bytes in cache"), ) @@ -304,7 +304,7 @@ func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { return nil, err } - itemsGauge, err := meter.Int64UpDownCounter( + itemsGauge, err := meter.Int64ObservableUpDownCounter( "groupcache.cache.items", metric.WithDescription("Number of items in cache"), ) @@ -312,7 +312,7 @@ func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { return nil, err } - getsCounter, err := meter.Int64Counter( + getsCounter, err := meter.Int64ObservableCounter( "groupcache.cache.gets", metric.WithDescription("Total get requests"), ) @@ -320,7 +320,7 @@ func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { return nil, err } - hitsCounter, err := meter.Int64Counter( + hitsCounter, err := meter.Int64ObservableCounter( "groupcache.cache.hits", metric.WithDescription("Total successful cache hits"), ) @@ -328,7 +328,7 @@ func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { return nil, err } - evictionsCounter, err := meter.Int64Counter( + evictionsCounter, err := meter.Int64ObservableCounter( "groupcache.cache.evictions", metric.WithDescription("Total number of evictions"), ) @@ -346,26 +346,26 @@ func newCacheInstruments(meter metric.Meter) (*cacheInstruments, error) { }, nil } -func (cm *cacheInstruments) RejectedCounter() metric.Int64Counter { +func (cm *cacheInstruments) RejectedCounter() metric.Int64ObservableCounter { return cm.rejectedCounter } -func (cm *cacheInstruments) BytesGauge() metric.Int64UpDownCounter { +func (cm *cacheInstruments) BytesGauge() metric.Int64ObservableUpDownCounter { return cm.bytesGauge } -func (cm *cacheInstruments) ItemsGauge() metric.Int64UpDownCounter { +func (cm *cacheInstruments) ItemsGauge() metric.Int64ObservableUpDownCounter { return cm.itemsGauge } -func (cm *cacheInstruments) GetsCounter() metric.Int64Counter { +func (cm *cacheInstruments) GetsCounter() metric.Int64ObservableCounter { return cm.getsCounter } -func (cm *cacheInstruments) HitsCounter() metric.Int64Counter { +func (cm *cacheInstruments) HitsCounter() metric.Int64ObservableCounter { return cm.hitsCounter } -func (cm *cacheInstruments) EvictionsCounter() metric.Int64Counter { +func (cm *cacheInstruments) EvictionsCounter() metric.Int64ObservableCounter { return cm.evictionsCounter } diff --git a/stats_test.go b/stats_test.go index 7eceb36..16cf8e9 100644 --- a/stats_test.go +++ b/stats_test.go @@ -112,7 +112,7 @@ func TestNewCacheInstrumentsErrorsOnCounterFailure(t *testing.T) { t.Parallel() expectedErr := errors.New("counter fail") - meter := &failingSyncMeter{counterErr: expectedErr} + meter := &failingObservableMeter{counterErr: expectedErr} inst, err := newCacheInstruments(meter) require.ErrorIs(t, err, expectedErr) @@ -123,7 +123,7 @@ func TestNewCacheInstrumentsErrorsOnUpDownCounterFailure(t *testing.T) { t.Parallel() expectedErr := errors.New("updown fail") - meter := &failingSyncMeter{upDownErr: expectedErr} + meter := &failingObservableMeter{upDownErr: expectedErr} inst, err := newCacheInstruments(meter) require.ErrorIs(t, err, expectedErr) @@ -150,7 +150,7 @@ func TestNewGroupPropagatesMetricRegistrationError(t *testing.T) { func TestNewCacheInstrumentsRegistersAllCounters(t *testing.T) { t.Parallel() - meter := &recordingSyncMeter{} + meter := &recordingMeter{} inst, err := newCacheInstruments(meter) require.NoError(t, err) @@ -229,27 +229,6 @@ func (m *failingObservableMeter) Int64ObservableUpDownCounter(string, ...metric. return noop.Int64ObservableUpDownCounter{}, nil } -type failingSyncMeter struct { - noop.Meter - - counterErr error - upDownErr error -} - -func (m *failingSyncMeter) Int64Counter(string, ...metric.Int64CounterOption) (metric.Int64Counter, error) { - if m.counterErr != nil { - return nil, m.counterErr - } - return noop.Int64Counter{}, nil -} - -func (m *failingSyncMeter) Int64UpDownCounter(string, ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) { - if m.upDownErr != nil { - return nil, m.upDownErr - } - return noop.Int64UpDownCounter{}, nil -} - type staticMeterProvider struct { noop.MeterProvider meter metric.Meter @@ -259,19 +238,3 @@ func (s *staticMeterProvider) Meter(string, ...metric.MeterOption) metric.Meter return s.meter } -type recordingSyncMeter struct { - noop.Meter - - counterNames []string - updownNames []string -} - -func (m *recordingSyncMeter) Int64Counter(name string, _ ...metric.Int64CounterOption) (metric.Int64Counter, error) { - m.counterNames = append(m.counterNames, name) - return noop.Int64Counter{}, nil -} - -func (m *recordingSyncMeter) Int64UpDownCounter(name string, _ ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) { - m.updownNames = append(m.updownNames, name) - return noop.Int64UpDownCounter{}, nil -}