Files
backend/internal/pkg/hook/metrics.go
lafay b640c9a249
All checks were successful
Build Backend / build (push) Successful in 3m51s
Build Backend / build-docker (push) Successful in 1m0s
refactor: upgrade to Go 1.26 and modernize code idioms
- Replace `interface{}` with `any` type alias across all packages
- Use built-in `min()`/`max()` for parameter clamping
- Use `slices.SortFunc`, `slices.Min`, `slices.Max` for cleaner code
- Use `strings.Cut()` for simpler string parsing in auth middleware
- Use `errors.Is()` for proper error comparison in handlers
- Update dependencies: golang.org/x/image 0.37.0 -> 0.38.0
- Add Wire code generation guidelines to ARCHITECTURE.md
- Disable Go cache in CI build workflow
2026-03-30 04:49:35 +08:00

123 lines
2.3 KiB
Go

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 := range len(key) {
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()
}