package proxy import ( "sort" "sync" "sync/atomic" "time" ) type serverStats struct { active atomic.Int64 total atomic.Int64 failures atomic.Int64 bytesIn atomic.Int64 // client -> backend bytesOut atomic.Int64 // backend -> client } type Stats struct { Started time.Time active atomic.Int64 total atomic.Int64 rejected atomic.Int64 unknown atomic.Int64 mu sync.Mutex servers map[string]*serverStats } func newStats() *Stats { return &Stats{Started: time.Now(), servers: map[string]*serverStats{}} } func (s *Stats) server(name string) *serverStats { s.mu.Lock() defer s.mu.Unlock() ss := s.servers[name] if ss == nil { ss = &serverStats{} s.servers[name] = ss } return ss } type ServerSnapshot struct { Name string `json:"name"` Active int64 `json:"active"` Total int64 `json:"total"` Failures int64 `json:"failures"` BytesIn int64 `json:"bytes_in"` BytesOut int64 `json:"bytes_out"` } type Snapshot struct { UptimeSecs int64 `json:"uptime_secs"` Active int64 `json:"active"` Total int64 `json:"total"` Rejected int64 `json:"rejected"` Unknown int64 `json:"unknown_host"` Servers []ServerSnapshot `json:"servers"` } func (s *Stats) Snapshot() Snapshot { out := Snapshot{ UptimeSecs: int64(time.Since(s.Started).Seconds()), Active: s.active.Load(), Total: s.total.Load(), Rejected: s.rejected.Load(), Unknown: s.unknown.Load(), } s.mu.Lock() for n, ss := range s.servers { out.Servers = append(out.Servers, ServerSnapshot{n, ss.active.Load(), ss.total.Load(), ss.failures.Load(), ss.bytesIn.Load(), ss.bytesOut.Load()}) } s.mu.Unlock() sort.Slice(out.Servers, func(i, j int) bool { return out.Servers[i].Name < out.Servers[j].Name }) return out }