Skip to content

Commit 871b9bb

Browse files
tukeJonnypgier
authored andcommitted
Add support for /proc/loadavg
Signed-off-by: tukeJonny <[email protected]>
1 parent 8a40bcd commit 871b9bb

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

fixtures.ttar

+5
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,11 @@ blocksize : 16
15271527
min keysize : 16
15281528
max keysize : 32
15291529

1530+
Mode: 444
1531+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1532+
Path: fixtures/proc/loadavg
1533+
Lines: 1
1534+
0.02 0.04 0.05 1/497 11947
15301535
Mode: 444
15311536
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
15321537
Path: fixtures/proc/diskstats

loadavg.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2019 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package procfs
15+
16+
import (
17+
"fmt"
18+
"strconv"
19+
"strings"
20+
21+
"github.com/prometheus/procfs/internal/util"
22+
)
23+
24+
// LoadAvg represents an entry in /proc/loadavg
25+
type LoadAvg struct {
26+
Load1 float64
27+
Load5 float64
28+
Load15 float64
29+
}
30+
31+
// LoadAvg returns loadavg from /proc.
32+
func (fs FS) LoadAvg() (*LoadAvg, error) {
33+
path := fs.proc.Path("loadavg")
34+
35+
data, err := util.ReadFileNoStat(path)
36+
if err != nil {
37+
return nil, err
38+
}
39+
return parseLoad(data)
40+
}
41+
42+
// Parse /proc loadavg and return 1m, 5m and 15m.
43+
func parseLoad(loadavgBytes []byte) (*LoadAvg, error) {
44+
loads := make([]float64, 3)
45+
parts := strings.Fields(string(loadavgBytes))
46+
if len(parts) < 3 {
47+
return nil, fmt.Errorf("malformed loadavg line: too few fields in loadavg string: %s", string(loadavgBytes))
48+
}
49+
50+
var err error
51+
for i, load := range parts[0:3] {
52+
loads[i], err = strconv.ParseFloat(load, 64)
53+
if err != nil {
54+
return nil, fmt.Errorf("could not parse load '%s': %s", load, err)
55+
}
56+
}
57+
return &LoadAvg{
58+
Load1: loads[0],
59+
Load5: loads[1],
60+
Load15: loads[2],
61+
}, nil
62+
}

loadavg_test.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2019 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package procfs
15+
16+
import (
17+
"testing"
18+
19+
"github.com/google/go-cmp/cmp"
20+
)
21+
22+
func TestLoadAvg(t *testing.T) {
23+
fs, err := NewFS(procTestFixtures)
24+
if err != nil {
25+
t.Fatalf("failed to open procfs: %v", err)
26+
}
27+
28+
loadavg, err := fs.LoadAvg()
29+
if err != nil {
30+
t.Fatalf("failed to get loadavg: %v", err)
31+
}
32+
33+
if diff := cmp.Diff(0.02, loadavg.Load1); diff != "" {
34+
t.Fatalf("unexpected LoadAvg Per a minute:\n%s", diff)
35+
}
36+
if diff := cmp.Diff(0.04, loadavg.Load5); diff != "" {
37+
t.Fatalf("unexpected LoadAvg Per five minutes:\n%s", diff)
38+
}
39+
if diff := cmp.Diff(0.05, loadavg.Load15); diff != "" {
40+
t.Fatalf("unexpected LoadAvg Per fifteen minutes:\n%s", diff)
41+
}
42+
}
43+
44+
func Test_parseLoad(t *testing.T) {
45+
tests := []struct {
46+
name string
47+
s string
48+
ok bool
49+
loadavg *LoadAvg
50+
}{
51+
{
52+
name: "empty",
53+
ok: false,
54+
},
55+
{
56+
name: "not enough fields",
57+
s: `0.00 0.03`,
58+
ok: false,
59+
},
60+
{
61+
name: "invalid line",
62+
s: `malformed line`,
63+
ok: false,
64+
},
65+
{
66+
name: "valid line",
67+
s: `0.00 0.03 0.05 1/502 33634`,
68+
ok: true,
69+
loadavg: &LoadAvg{Load1: 0, Load5: 0.03, Load15: 0.05},
70+
},
71+
}
72+
73+
for _, tt := range tests {
74+
t.Run(tt.name, func(t *testing.T) {
75+
loadavg, err := parseLoad([]byte(tt.s))
76+
if err != nil {
77+
if tt.ok {
78+
t.Fatalf("failed to parse loadavg: %v", err)
79+
}
80+
81+
t.Logf("OK error: %v", err)
82+
return
83+
}
84+
if !tt.ok {
85+
t.Fatal("expected an error, but none occurred")
86+
}
87+
88+
if diff := cmp.Diff(tt.loadavg, loadavg); diff != "" {
89+
t.Errorf("unexpected loadavg(-want +got):\n%s", diff)
90+
}
91+
})
92+
}
93+
}

0 commit comments

Comments
 (0)