This repository has been archived by the owner on Aug 30, 2019. It is now read-only.
/
info.go
179 lines (152 loc) · 4.25 KB
/
info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package watchdog
import (
"os"
"runtime"
"sync"
"time"
log "github.com/cihub/seelog"
"github.com/shirou/gopsutil/process"
)
const (
// cacheDelay should be long enough so that we don't poll the info
// too often and waste resources doing it, and also long enough
// so that it's not jittering (CPU can be volatile).
// OTOH it should be short enough to get up-to-date recent info.
cacheDelay = 20 * time.Second
)
// CPUInfo contains basic CPU info
type CPUInfo struct {
// UserAvg is the average of the user CPU usage since last time
// it was polled. 0 means "not used at all" and 1 means "1 CPU was
// totally full for that period". So it might be greater than 1 if
// the process is monopolizing several cores.
UserAvg float64
}
// MemInfo contains basic memory info
type MemInfo struct {
// Alloc is the number of bytes allocated and not yet freed
// as described in runtime.MemStats.Alloc
Alloc uint64
// AllocPerSec is the average number of bytes allocated, per second,
// since last time this function was called.
AllocPerSec float64
}
// NetInfo contains basic networking info
type NetInfo struct {
// Connections is the number of connections opened by this process.
Connections int32
}
// Info contains all the watchdog infos, to be published by expvar
type Info struct {
// CPU contains basic CPU info
CPU CPUInfo
// Mem contains basic Mem info
Mem MemInfo
// Net contains basic Net info
Net NetInfo
}
// CurrentInfo is used to query CPU and Mem info, it keeps data from
// the previous calls to calculate averages. It is not thread safe.
type CurrentInfo struct {
p *process.Process
mu sync.Mutex
cacheDelay time.Duration
lastCPUTime time.Time
lastCPUUser float64
lastCPU CPUInfo
lastMemTime time.Time
lastMemTotalAlloc uint64
lastMem MemInfo
lastNetTime time.Time
lastNet NetInfo
}
// globalCurrentInfo is a global default object one can safely use
// if only one goroutine is polling for CPU() and Mem()
var globalCurrentInfo *CurrentInfo
func init() {
var err error
globalCurrentInfo, err = NewCurrentInfo()
if err != nil {
log.Errorf("unable to create global Process: %v", err)
}
}
// NewCurrentInfo creates a new CurrentInfo referring to the current running program.
func NewCurrentInfo() (*CurrentInfo, error) {
p, err := process.NewProcess(int32(os.Getpid()))
if err != nil {
return nil, err
}
return &CurrentInfo{
p: p,
cacheDelay: cacheDelay,
}, nil
}
// CPU returns basic CPU info.
func (pi *CurrentInfo) CPU() CPUInfo {
pi.mu.Lock()
defer pi.mu.Unlock()
now := time.Now()
dt := now.Sub(pi.lastCPUTime)
if dt <= pi.cacheDelay {
return pi.lastCPU // don't query too often, cache a little bit
}
pi.lastCPUTime = now
times, err := pi.p.Times()
if err != nil {
log.Debugf("unable to get CPU times: %v", err)
return pi.lastCPU
}
dua := times.User - pi.lastCPUUser
pi.lastCPUUser = times.User
if dua <= 0 {
pi.lastCPU.UserAvg = 0 // shouldn't happen, but make sure result is always > 0
} else {
pi.lastCPU.UserAvg = float64(time.Second) * dua / float64(dt)
pi.lastCPUUser = times.User
}
return pi.lastCPU
}
// Mem returns basic memory info.
func (pi *CurrentInfo) Mem() MemInfo {
pi.mu.Lock()
defer pi.mu.Unlock()
now := time.Now()
dt := now.Sub(pi.lastMemTime)
if dt <= pi.cacheDelay {
return pi.lastMem // don't query too often, cache a little bit
}
pi.lastMemTime = now
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
ret := MemInfo{Alloc: ms.Alloc, AllocPerSec: pi.lastMem.AllocPerSec}
dta := int64(ms.TotalAlloc) - int64(pi.lastMemTotalAlloc)
pi.lastMemTotalAlloc = ms.TotalAlloc
if dta <= 0 {
pi.lastMem.AllocPerSec = 0 // shouldn't happen, but make sure result is always > 0
} else {
pi.lastMem.AllocPerSec = float64(time.Second) * float64(dta) / float64(dt)
}
ret.AllocPerSec = pi.lastMem.AllocPerSec
return ret
}
// CPU returns basic CPU info.
func CPU() CPUInfo {
if globalCurrentInfo == nil {
return CPUInfo{}
}
return globalCurrentInfo.CPU()
}
// Mem returns basic memory info.
func Mem() MemInfo {
if globalCurrentInfo == nil {
return MemInfo{}
}
return globalCurrentInfo.Mem()
}
// Net returns basic network info.
func Net() NetInfo {
if globalCurrentInfo == nil {
return NetInfo{}
}
return globalCurrentInfo.Net()
}