forked from asticode/go-astiencoder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stat.go
186 lines (161 loc) · 3.87 KB
/
stat.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
180
181
182
183
184
185
186
package astiencoder
import (
"context"
"fmt"
"os"
"sync"
"time"
"github.com/asticode/go-astikit"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/process"
)
const (
StatNameHostUsage = "astiencoder.host.usage"
)
// EventStat represents a stat event
type EventStat struct {
Description string
Label string
Name string
Target interface{}
Unit string
Value interface{}
}
// Stater represents an object that can compute and handle stats
type Stater struct {
eh *EventHandler
m *sync.Mutex // Locks ts
ts map[*astikit.StatMetadata]interface{} // Targets indexed by stats metadata
s *astikit.Stater
}
// NewStater creates a new stater
func NewStater(period time.Duration, eh *EventHandler) (s *Stater) {
s = &Stater{
eh: eh,
m: &sync.Mutex{},
ts: make(map[*astikit.StatMetadata]interface{}),
}
s.s = astikit.NewStater(astikit.StaterOptions{
HandleFunc: s.handle,
Period: period,
})
return
}
// AddStats adds stats
func (s *Stater) AddStats(target interface{}, os ...astikit.StatOptions) {
s.m.Lock()
defer s.m.Unlock()
for _, o := range os {
s.ts[o.Metadata] = target
}
s.s.AddStats(os...)
}
// DelStats deletes stats
func (s *Stater) DelStats(target interface{}, os ...astikit.StatOptions) {
s.m.Lock()
defer s.m.Unlock()
for _, o := range os {
delete(s.ts, o.Metadata)
}
s.s.DelStats(os...)
}
// Start starts the stater
func (s *Stater) Start(ctx context.Context) { s.s.Start(ctx) }
// Stop stops the stater
func (s *Stater) Stop() { s.s.Stop() }
func (s *Stater) handle(stats []astikit.StatValue) {
// No stats
if len(stats) == 0 {
return
}
// Loop through stats
ss := []EventStat{}
for _, stat := range stats {
// Get target
s.m.Lock()
t, ok := s.ts[stat.StatMetadata]
s.m.Unlock()
// No target
if !ok {
continue
}
// Append
ss = append(ss, EventStat{
Description: stat.Description,
Label: stat.Label,
Name: stat.Name,
Target: t,
Unit: stat.Unit,
Value: stat.Value,
})
}
// Send event
s.eh.Emit(Event{
Name: EventNameStats,
Payload: ss,
})
}
type StatValueHostUsage struct {
CPU StatValueHostUsageCPU `json:"cpu"`
Memory StatValueHostUsageMemory `json:"memory"`
}
type StatValueHostUsageCPU struct {
Individual []float64 `json:"individual"`
Process float64 `json:"process"`
Total float64 `json:"total"`
}
type StatValueHostUsageMemory struct {
Resident uint64 `json:"resident"`
Total uint64 `json:"total"`
Used uint64 `json:"used"`
Virtual uint64 `json:"virtual"`
}
type statPSUtil struct {
lastTimes *cpu.TimesStat
lastTimesAt time.Time
p *process.Process
}
func newStatPSUtil() (u *statPSUtil, err error) {
// Create util
u = &statPSUtil{}
// Create process
if u.p, err = process.NewProcess(int32(os.Getpid())); err != nil {
err = fmt.Errorf("astiencoder: creating process failed: %w", err)
return
}
// Get times
if u.lastTimes, err = u.p.Times(); err != nil {
err = fmt.Errorf("astiencoder: getting times failed: %w", err)
return
}
u.lastTimesAt = time.Now()
return
}
func (s *statPSUtil) Value(_ time.Duration) interface{} {
// Get process CPU
var v StatValueHostUsage
if t, err := s.p.Times(); err == nil {
n := time.Now()
v.CPU.Process = (t.Total() - t.Idle - (s.lastTimes.Total() - s.lastTimes.Idle)) / n.Sub(s.lastTimesAt).Seconds() * 100
s.lastTimes = t
s.lastTimesAt = n
}
// Get global CPU
if ps, err := cpu.Percent(0, true); err == nil {
v.CPU.Individual = ps
}
if ps, err := cpu.Percent(0, false); err == nil && len(ps) > 0 {
v.CPU.Total = ps[0]
}
// Get memory
if i, err := s.p.MemoryInfo(); err == nil {
v.Memory.Resident = i.RSS
v.Memory.Virtual = i.VMS
}
if s, err := mem.VirtualMemory(); err == nil {
v.Memory.Total = s.Total
v.Memory.Used = s.Used
}
return v
}