-
Notifications
You must be signed in to change notification settings - Fork 13
/
kernel.go
132 lines (112 loc) · 3.45 KB
/
kernel.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
//go:build linux
// +build linux
package kernel
import (
"bytes"
"context"
"fmt"
"os"
"strconv"
"strings"
"github.com/circonus-labs/circonus-unified-agent/cua"
"github.com/circonus-labs/circonus-unified-agent/plugins/inputs"
)
// /proc/stat file line prefixes to gather stats on:
var (
interrupts = []byte("intr")
contextSwitches = []byte("ctxt")
processesForked = []byte("processes")
diskPages = []byte("page")
bootTime = []byte("btime")
)
type Kernel struct {
statFile string
entropyStatFile string
}
func (k *Kernel) Description() string {
return "Get kernel statistics from /proc/stat"
}
func (k *Kernel) SampleConfig() string {
return `
instance_id = "" # unique instance identifier (REQUIRED)
`
}
func (k *Kernel) Gather(ctx context.Context, acc cua.Accumulator) error {
data, err := k.getProcStat()
if err != nil {
return err
}
entropyData, err := os.ReadFile(k.entropyStatFile)
if err != nil {
return fmt.Errorf("readfile: %w", err)
}
entropyString := string(entropyData)
entropyValue, err := strconv.ParseInt(strings.TrimSpace(entropyString), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint %s: %w", strings.TrimSpace(entropyString), err)
}
fields := make(map[string]interface{})
fields["entropy_avail"] = entropyValue
dataFields := bytes.Fields(data)
for i, field := range dataFields {
switch {
case bytes.Equal(field, interrupts):
m, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint interrupts %s: %w", string(dataFields[i+1]), err)
}
fields["interrupts"] = m
case bytes.Equal(field, contextSwitches):
m, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint context_switches %s: %w", string(dataFields[i+1]), err)
}
fields["context_switches"] = m
case bytes.Equal(field, processesForked):
m, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint processes_forked %s: %w", string(dataFields[i+1]), err)
}
fields["processes_forked"] = m
case bytes.Equal(field, bootTime):
m, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint boot_time %s: %w", string(dataFields[i+1]), err)
}
fields["boot_time"] = m
case bytes.Equal(field, diskPages):
in, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint disk_pages_in %s: %w", string(dataFields[i+1]), err)
}
out, err := strconv.ParseInt(string(dataFields[i+2]), 10, 64)
if err != nil {
return fmt.Errorf("kernel parseint disk_pages_out %s: %w", string(dataFields[i+2]), err)
}
fields["disk_pages_in"] = in
fields["disk_pages_out"] = out
}
}
acc.AddCounter("kernel", fields, map[string]string{})
return nil
}
func (k *Kernel) getProcStat() ([]byte, error) {
if _, err := os.Stat(k.statFile); os.IsNotExist(err) {
return nil, fmt.Errorf("kernel: %s does not exist", k.statFile)
} else if err != nil {
return nil, fmt.Errorf("kernel stat (%s): %w", k.statFile, err)
}
data, err := os.ReadFile(k.statFile)
if err != nil {
return nil, fmt.Errorf("kernel readfile (%s): %w", k.statFile, err)
}
return data, nil
}
func init() {
inputs.Add("kernel", func() cua.Input {
return &Kernel{
statFile: "/proc/stat",
entropyStatFile: "/proc/sys/kernel/random/entropy_avail",
}
})
}