package hook import ( "sync" "sync/atomic" "time" ) type Metrics struct { totalCalls atomic.Int64 failedCalls atomic.Int64 totalTime atomic.Int64 hookMetrics map[string]*hookMetric mu sync.RWMutex } type hookMetric struct { calls atomic.Int64 failures atomic.Int64 totalTime atomic.Int64 lastCalled atomic.Int64 } func NewMetrics() *Metrics { return &Metrics{ hookMetrics: make(map[string]*hookMetric), } } func (m *Metrics) RecordHookExecution(name string, hookType HookType, duration time.Duration, success bool) { m.totalCalls.Add(1) m.totalTime.Add(int64(duration)) if !success { m.failedCalls.Add(1) } key := string(hookType) + ":" + name m.mu.Lock() metric, ok := m.hookMetrics[key] if !ok { metric = &hookMetric{} m.hookMetrics[key] = metric } m.mu.Unlock() metric.calls.Add(1) metric.totalTime.Add(int64(duration)) metric.lastCalled.Store(time.Now().Unix()) if !success { metric.failures.Add(1) } } type MetricSnapshot struct { Name string Type HookType Calls int64 Failures int64 TotalTime time.Duration AvgTime time.Duration LastCalled time.Time } func (m *Metrics) GetTotalCalls() int64 { return m.totalCalls.Load() } func (m *Metrics) GetFailedCalls() int64 { return m.failedCalls.Load() } func (m *Metrics) GetTotalTime() time.Duration { return time.Duration(m.totalTime.Load()) } func (m *Metrics) GetHookMetrics() []MetricSnapshot { m.mu.RLock() defer m.mu.RUnlock() var snapshots []MetricSnapshot for key, metric := range m.hookMetrics { calls := metric.calls.Load() var avgTime time.Duration if calls > 0 { avgTime = time.Duration(metric.totalTime.Load() / calls) } hookType, name := parseKey(key) snapshots = append(snapshots, MetricSnapshot{ Name: name, Type: hookType, Calls: calls, Failures: metric.failures.Load(), TotalTime: time.Duration(metric.totalTime.Load()), AvgTime: avgTime, LastCalled: time.Unix(metric.lastCalled.Load(), 0), }) } return snapshots } func parseKey(key string) (HookType, string) { for i := 0; i < len(key); i++ { if key[i] == ':' { return HookType(key[:i]), key[i+1:] } } return HookType(key), "" } func (m *Metrics) Reset() { m.totalCalls.Store(0) m.failedCalls.Store(0) m.totalTime.Store(0) m.mu.Lock() m.hookMetrics = make(map[string]*hookMetric) m.mu.Unlock() }