84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package observability
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type Snapshot struct {
|
|
GeneratedAtUTC string `json:"generated_at_utc"`
|
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
|
TotalRequests uint64 `json:"total_requests"`
|
|
ClientErrors uint64 `json:"client_errors"`
|
|
ServerErrors uint64 `json:"server_errors"`
|
|
ClientErrorRatePct float64 `json:"client_error_rate_pct"`
|
|
ServerErrorRatePct float64 `json:"server_error_rate_pct"`
|
|
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
|
MaxLatencyMs float64 `json:"max_latency_ms"`
|
|
}
|
|
|
|
type Collector struct {
|
|
startedAt time.Time
|
|
totalRequests uint64
|
|
clientErrors uint64
|
|
serverErrors uint64
|
|
totalLatencyNs uint64
|
|
maxLatencyNs uint64
|
|
}
|
|
|
|
func NewCollector() *Collector {
|
|
return &Collector{startedAt: time.Now()}
|
|
}
|
|
|
|
func (c *Collector) Observe(status int, latency time.Duration) {
|
|
atomic.AddUint64(&c.totalRequests, 1)
|
|
atomic.AddUint64(&c.totalLatencyNs, uint64(latency.Nanoseconds()))
|
|
|
|
if status >= 500 {
|
|
atomic.AddUint64(&c.serverErrors, 1)
|
|
} else if status >= 400 {
|
|
atomic.AddUint64(&c.clientErrors, 1)
|
|
}
|
|
|
|
latNs := uint64(latency.Nanoseconds())
|
|
for {
|
|
old := atomic.LoadUint64(&c.maxLatencyNs)
|
|
if latNs <= old {
|
|
break
|
|
}
|
|
if atomic.CompareAndSwapUint64(&c.maxLatencyNs, old, latNs) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Collector) Snapshot() Snapshot {
|
|
now := time.Now()
|
|
total := atomic.LoadUint64(&c.totalRequests)
|
|
clientErr := atomic.LoadUint64(&c.clientErrors)
|
|
serverErr := atomic.LoadUint64(&c.serverErrors)
|
|
totalLatencyNs := atomic.LoadUint64(&c.totalLatencyNs)
|
|
maxLatencyNs := atomic.LoadUint64(&c.maxLatencyNs)
|
|
|
|
var clientRate float64
|
|
var serverRate float64
|
|
var avgLatencyMs float64
|
|
if total > 0 {
|
|
clientRate = float64(clientErr) * 100 / float64(total)
|
|
serverRate = float64(serverErr) * 100 / float64(total)
|
|
avgLatencyMs = float64(totalLatencyNs) / float64(total) / 1e6
|
|
}
|
|
|
|
return Snapshot{
|
|
GeneratedAtUTC: now.UTC().Format(time.RFC3339),
|
|
UptimeSeconds: int64(now.Sub(c.startedAt).Seconds()),
|
|
TotalRequests: total,
|
|
ClientErrors: clientErr,
|
|
ServerErrors: serverErr,
|
|
ClientErrorRatePct: clientRate,
|
|
ServerErrorRatePct: serverRate,
|
|
AvgLatencyMs: avgLatencyMs,
|
|
MaxLatencyMs: float64(maxLatencyNs) / 1e6,
|
|
}
|
|
}
|