package stats import "time" // Server is the latest server statistics: named's process, memory, cache // and sockets, and the host's memory and load. It is sampled with the // counters, once a minute. type Server struct { At time.Time `json:"at"` Version string `json:"version,omitempty"` BootTime string `json:"boot_time,omitempty"` ConfigTime string `json:"config_time,omitempty"` // From /proc (0 when named's process isn't visible). PID int `json:"pid,omitempty"` RSS int64 `json:"rss,omitempty"` // resident memory, bytes Threads int64 `json:"threads,omitempty"` CPUPercent float64 `json:"cpu_percent"` // of one CPU, over the last minute CPUSeconds float64 `json:"cpu_seconds,omitempty"` // user + system time since named started // From the statistics channel. MemInUse int64 `json:"mem_in_use"` // named's own count of the memory it uses CacheRRsets int64 `json:"cache_rrsets"` CacheMem int64 `json:"cache_mem"` SocketsUDP int64 `json:"sockets_udp"` SocketsTCP int64 `json:"sockets_tcp"` RecursionHighwater int64 `json:"recursion_highwater"` // most recursive clients at once since named started TCPHighwater int64 `json:"tcp_highwater"` Host *Host `json:"host"` } // Gauge keys: each is stored as two counters, "g:KEY" (the sum of the // minute samples) and "gn:KEY" (their number), so hours and days add up // like counters and a bucket's value is their average (see Timeline). const ( GaugeRSS = "named:rss" GaugeMem = "named:mem" GaugeCPU = "named:cpu" // tenths of a percent of one CPU GaugeCacheMem = "cache:mem" GaugeCacheRRs = "cache:rrsets" GaugeHostMem = "host:mem-used" GaugeHostLoad = "host:load" // the 1-minute load average, times 100 ) // gauge records a sample of a gauge for a minute (none when minute < 0). func (c *Collector) gauge(minute int64, key string, v int64) { if minute < 0 { return } c.count(minute, "g:"+key, v) c.count(minute, "gn:"+key, 1) } // sampleServer records the gauges for a minute and keeps the latest // values (minute -1: only those). snap is nil when named's statistics // channel didn't answer. // Called with c.mu held. func (c *Collector) sampleServer(minute int64, now time.Time, snap *Snapshot) { srv := &Server{At: now, Host: readHost()} if c.proc == nil || c.proc.PID == 0 { c.proc = &namedProc{PID: findNamed(c.opts.MainConfig)} } p, ok := readNamedProc(c.proc.PID, now) if !ok { // named restarted with a new PID: look again next minute. c.proc = nil } else { srv.PID, srv.RSS, srv.Threads = p.PID, p.RSS, p.Threads srv.CPUSeconds = float64(p.CPU) / clockTicks if prev := c.proc; prev.At.Before(now) && !prev.At.IsZero() && p.CPU >= prev.CPU { // Tenths of a percent of one CPU: ticks / clockTicks is CPU seconds. permille := (p.CPU - prev.CPU) * 1000 / clockTicks / max(int64(now.Sub(prev.At)/time.Second), 1) srv.CPUPercent = float64(permille) / 10 c.gauge(minute, GaugeCPU, permille) } c.proc = p c.gauge(minute, GaugeRSS, p.RSS) } if h := srv.Host; h.MemTotal > 0 { c.gauge(minute, GaugeHostMem, h.MemTotal-h.MemAvailable) } if h := srv.Host; len(h.Load) > 0 { c.gauge(minute, GaugeHostLoad, int64(h.Load[0]*100)) } if snap != nil && snap.Gauges != nil { g := snap.Gauges srv.Version, srv.BootTime, srv.ConfigTime = snap.Version, snap.BootTime, snap.ConfigTime srv.MemInUse, srv.CacheRRsets, srv.CacheMem = g["named:mem"], g["cache:rrsets"], g["cache:mem"] srv.SocketsUDP, srv.SocketsTCP = g["sockets:udp"], g["sockets:tcp"] srv.RecursionHighwater, srv.TCPHighwater = g["named:recursion-highwater"], g["named:tcp-highwater"] c.gauge(minute, GaugeMem, srv.MemInUse) c.gauge(minute, GaugeCacheMem, srv.CacheMem) c.gauge(minute, GaugeCacheRRs, srv.CacheRRsets) } c.server = srv } // Server returns the latest server statistics (nil before the first // minute). func (c *Collector) Server() *Server { c.mu.Lock() defer c.mu.Unlock() if c.server == nil { return nil } s := *c.server return &s } // Disk is the space on a file system Jarl and named use. type Disk struct { Path string `json:"path"` // the first of the directories on it Holds []string `json:"holds"` // what is on it: zone files, logs, ... Total int64 `json:"total"` Free int64 `json:"free"` // available to unprivileged users }