2026-03-20 12:23:28 +08:00
|
|
|
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) {
|
2026-03-30 04:49:35 +08:00
|
|
|
for i := range len(key) {
|
2026-03-20 12:23:28 +08:00
|
|
|
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()
|
|
|
|
|
}
|