mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
206 lines
4.3 KiB
Go
206 lines
4.3 KiB
Go
package monitor
|
|
|
|
import (
|
|
"expvar"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/shirou/gopsutil/v3/cpu"
|
|
"github.com/shirou/gopsutil/v3/load"
|
|
"github.com/shirou/gopsutil/v3/mem"
|
|
"github.com/shirou/gopsutil/v3/net"
|
|
"github.com/shirou/gopsutil/v3/process"
|
|
)
|
|
|
|
// Stats holds the process and operating system statistics values.
|
|
//
|
|
// Note that each statistic has its own expvar metric that you can use
|
|
// to render e.g. through statsd. Available values:
|
|
// * pid_cpu
|
|
// * pid_ram
|
|
// * pid_conns
|
|
// * os_cpu
|
|
// * os_ram
|
|
// * os_total_ram
|
|
// * os_load_avg
|
|
// * os_conns
|
|
type Stats struct {
|
|
PIDCPU float64 `json:"pid_cpu" yaml:"PIDCPU"`
|
|
PIDRAM uint64 `json:"pid_ram" yaml:"PIDRAM"`
|
|
PIDConns int64 `json:"pid_conns" yaml:"PIDConns"`
|
|
|
|
OSCPU float64 `json:"os_cpu" yaml:"OSCPU"`
|
|
OSRAM uint64 `json:"os_ram" yaml:"OSRAM"`
|
|
OSTotalRAM uint64 `json:"os_total_ram" yaml:"OSTotalRAM"`
|
|
OSLoadAvg float64 `json:"os_load_avg" yaml:"OSLoadAvg"`
|
|
OSConns int64 `json:"os_conns" yaml:"OSConns"`
|
|
}
|
|
|
|
// StatsHolder holds and refreshes the statistics.
|
|
type StatsHolder struct {
|
|
proc *process.Process
|
|
|
|
stats *Stats
|
|
mu sync.RWMutex
|
|
|
|
started bool
|
|
closeCh chan struct{}
|
|
errCh chan error
|
|
}
|
|
|
|
func startNewStatsHolder(proc *process.Process, refreshInterval time.Duration) *StatsHolder {
|
|
sh := newStatsHolder(proc)
|
|
sh.start(refreshInterval)
|
|
return sh
|
|
}
|
|
|
|
func newStatsHolder(proc *process.Process) *StatsHolder {
|
|
sh := &StatsHolder{
|
|
proc: proc,
|
|
stats: new(Stats),
|
|
closeCh: make(chan struct{}),
|
|
errCh: make(chan error, 1),
|
|
}
|
|
|
|
return sh
|
|
}
|
|
|
|
// Err returns a read-only channel which may be filled with errors
|
|
// came from the refresh stats operation.
|
|
func (sh *StatsHolder) Err() <-chan error {
|
|
return sh.errCh
|
|
}
|
|
|
|
// Stop terminates the routine retrieves the stats.
|
|
// Note that no other monitor can be initialized after Stop.
|
|
func (sh *StatsHolder) Stop() {
|
|
if !sh.started {
|
|
return
|
|
}
|
|
|
|
sh.closeCh <- struct{}{}
|
|
sh.started = false
|
|
}
|
|
|
|
func (sh *StatsHolder) start(refreshInterval time.Duration) {
|
|
if sh.started {
|
|
return
|
|
}
|
|
sh.started = true
|
|
|
|
once.Do(func() {
|
|
go func() {
|
|
ticker := time.NewTicker(refreshInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-sh.closeCh:
|
|
// close(sh.errCh)
|
|
return
|
|
case <-ticker.C:
|
|
err := refresh(sh.proc)
|
|
if err != nil {
|
|
// push the error to the channel and continue the execution,
|
|
// the only way to stop it is through its "Stop" method.
|
|
sh.errCh <- err
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
})
|
|
}
|
|
|
|
var (
|
|
once = new(sync.Once)
|
|
|
|
metricPidCPU = expvar.NewFloat("pid_cpu")
|
|
metricPidRAM = newUint64("pid_ram")
|
|
metricPidConns = expvar.NewInt("pid_conns")
|
|
|
|
metricOsCPU = expvar.NewFloat("os_cpu")
|
|
metricOsRAM = newUint64("os_ram")
|
|
metricOsTotalRAM = newUint64("os_total_ram")
|
|
metricOsLoadAvg = expvar.NewFloat("os_load_avg")
|
|
metricOsConns = expvar.NewInt("os_conns")
|
|
)
|
|
|
|
// refresh updates the process and operating system statistics.
|
|
func refresh(proc *process.Process) error {
|
|
// Collect the stats.
|
|
//
|
|
// Process.
|
|
pidCPU, err := proc.CPUPercent()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pidRAM, err := proc.MemoryInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pidConns, err := net.ConnectionsPid("tcp", proc.Pid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Operating System.
|
|
osCPU, err := cpu.Percent(0, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
osRAM, err := mem.VirtualMemory()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
osLoadAvg, err := load.Avg()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
osConns, err := net.Connections("tcp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the fields.
|
|
//
|
|
// Process.
|
|
metricPidCPU.Set(pidCPU / 10)
|
|
metricPidRAM.Set(pidRAM.RSS)
|
|
metricPidConns.Set(int64(len(pidConns)))
|
|
|
|
// Operating System.
|
|
if len(osCPU) > 0 {
|
|
metricOsCPU.Set(osCPU[0])
|
|
}
|
|
metricOsRAM.Set(osRAM.Used)
|
|
metricOsTotalRAM.Set(osRAM.Total)
|
|
metricOsLoadAvg.Set(osLoadAvg.Load1)
|
|
metricOsConns.Set(int64(len(osConns)))
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetStats returns a copy of the latest stats available.
|
|
func (sh *StatsHolder) GetStats() Stats {
|
|
sh.mu.Lock()
|
|
statsCopy := Stats{
|
|
PIDCPU: metricPidCPU.Value(),
|
|
PIDRAM: metricPidRAM.Value(),
|
|
PIDConns: metricPidConns.Value(),
|
|
|
|
OSCPU: metricOsCPU.Value(),
|
|
OSRAM: metricOsRAM.Value(),
|
|
OSTotalRAM: metricOsTotalRAM.Value(),
|
|
OSLoadAvg: metricOsLoadAvg.Value(),
|
|
OSConns: metricOsConns.Value(),
|
|
}
|
|
sh.mu.Unlock()
|
|
|
|
return statsCopy
|
|
}
|