Skip to content

Commit 1a4cac6

Browse files
authored
bcachefs collector (#3523)
Add support for collecting and reporting metrics from bcachefs filesystems. Signed-off-by: Ananth Bhaskararaman <antsub@gmail.com>
1 parent a1cbf81 commit 1a4cac6

5 files changed

Lines changed: 2631 additions & 5 deletions

File tree

collector/bcachefs_linux.go

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright 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+
//go:build !nobcachefs
15+
16+
package collector
17+
18+
import (
19+
"fmt"
20+
"log/slog"
21+
"os"
22+
23+
"github.com/prometheus/client_golang/prometheus"
24+
"github.com/prometheus/procfs/bcachefs"
25+
)
26+
27+
const subsystem = "bcachefs"
28+
29+
var (
30+
bcachefsInfoDesc = prometheus.NewDesc(
31+
prometheus.BuildFQName(namespace, subsystem, "info"),
32+
"Filesystem information.",
33+
[]string{"uuid"},
34+
nil,
35+
)
36+
bcachefsBtreeCacheSizeBytes = prometheus.NewDesc(
37+
prometheus.BuildFQName(namespace, subsystem, "btree_cache_size_bytes"),
38+
"Btree cache memory usage in bytes.",
39+
[]string{"uuid"},
40+
nil,
41+
)
42+
bcachefsCompressionCompressedBytesDesc = prometheus.NewDesc(
43+
prometheus.BuildFQName(namespace, subsystem, "compression_compressed_bytes"),
44+
"Compressed size by algorithm.",
45+
[]string{"uuid", "algorithm"},
46+
nil,
47+
)
48+
bcachefsCompressionUncompressedBytesDesc = prometheus.NewDesc(
49+
prometheus.BuildFQName(namespace, subsystem, "compression_uncompressed_bytes"),
50+
"Uncompressed size by algorithm.",
51+
[]string{"uuid", "algorithm"},
52+
nil,
53+
)
54+
bcachefsErrorsTotalDesc = prometheus.NewDesc(
55+
prometheus.BuildFQName(namespace, subsystem, "errors_total"),
56+
"Error count by error type.",
57+
[]string{"uuid", "error_type"},
58+
nil,
59+
)
60+
bcachefsBtreeWritesTotalDesc = prometheus.NewDesc(
61+
prometheus.BuildFQName(namespace, subsystem, "btree_writes_total"),
62+
"Number of btree writes by type.",
63+
[]string{"uuid", "type"},
64+
nil,
65+
)
66+
bcachefsBtreeWriteAverageSizeBytesDesc = prometheus.NewDesc(
67+
prometheus.BuildFQName(namespace, subsystem, "btree_write_average_size_bytes"),
68+
"Average btree write size by type.",
69+
[]string{"uuid", "type"},
70+
nil,
71+
)
72+
bcachefsDeviceInfoDesc = prometheus.NewDesc(
73+
prometheus.BuildFQName(namespace, subsystem, "device_info"),
74+
"Device information.",
75+
[]string{"uuid", "device", "label", "state"},
76+
nil,
77+
)
78+
bcachefsDeviceBucketSizeBytesDesc = prometheus.NewDesc(
79+
prometheus.BuildFQName(namespace, subsystem, "device_bucket_size_bytes"),
80+
"Bucket size in bytes.",
81+
[]string{"uuid", "device"},
82+
nil,
83+
)
84+
bcachefsDeviceBucketsDesc = prometheus.NewDesc(
85+
prometheus.BuildFQName(namespace, subsystem, "device_buckets"),
86+
"Total number of buckets.",
87+
[]string{"uuid", "device"},
88+
nil,
89+
)
90+
bcachefsDeviceDurabilityDesc = prometheus.NewDesc(
91+
prometheus.BuildFQName(namespace, subsystem, "device_durability"),
92+
"Device durability setting.",
93+
[]string{"uuid", "device"},
94+
nil,
95+
)
96+
bcachefsDeviceIODoneBytesTotalDesc = prometheus.NewDesc(
97+
prometheus.BuildFQName(namespace, subsystem, "device_io_done_bytes_total"),
98+
"IO bytes by operation type and data type.",
99+
[]string{"uuid", "device", "operation", "data_type"},
100+
nil,
101+
)
102+
bcachefsDeviceIOErrorsTotalDesc = prometheus.NewDesc(
103+
prometheus.BuildFQName(namespace, subsystem, "device_io_errors_total"),
104+
"IO errors by error type.",
105+
[]string{"uuid", "device", "type"},
106+
nil,
107+
)
108+
)
109+
110+
func init() {
111+
registerCollector(subsystem, defaultEnabled, NewBcachefsCollector)
112+
}
113+
114+
// bcachefsCollector collects metrics from bcachefs filesystems.
115+
type bcachefsCollector struct {
116+
fs bcachefs.FS
117+
logger *slog.Logger
118+
}
119+
120+
// NewBcachefsCollector returns a new Collector exposing bcachefs statistics.
121+
func NewBcachefsCollector(logger *slog.Logger) (Collector, error) {
122+
fs, err := bcachefs.NewFS(*sysPath)
123+
if err != nil {
124+
return nil, fmt.Errorf("failed to open sysfs: %w", err)
125+
}
126+
127+
return &bcachefsCollector{
128+
fs: fs,
129+
logger: logger,
130+
}, nil
131+
}
132+
133+
// Update retrieves and exports bcachefs statistics.
134+
func (c *bcachefsCollector) Update(ch chan<- prometheus.Metric) error {
135+
stats, err := c.fs.Stats()
136+
if err != nil {
137+
if os.IsNotExist(err) {
138+
c.logger.Debug("bcachefs sysfs path does not exist", "path", sysFilePath("fs/bcachefs"))
139+
return ErrNoData
140+
}
141+
return fmt.Errorf("failed to retrieve bcachefs stats: %w", err)
142+
}
143+
144+
if len(stats) == 0 {
145+
return ErrNoData
146+
}
147+
148+
for _, s := range stats {
149+
uuid := s.UUID
150+
151+
ch <- prometheus.MustNewConstMetric(
152+
bcachefsInfoDesc,
153+
prometheus.GaugeValue,
154+
1,
155+
uuid,
156+
)
157+
158+
ch <- prometheus.MustNewConstMetric(
159+
bcachefsBtreeCacheSizeBytes,
160+
prometheus.GaugeValue,
161+
float64(s.BtreeCacheSizeBytes),
162+
uuid,
163+
)
164+
165+
for algorithm, comp := range s.Compression {
166+
ch <- prometheus.MustNewConstMetric(
167+
bcachefsCompressionCompressedBytesDesc,
168+
prometheus.GaugeValue,
169+
float64(comp.CompressedBytes),
170+
uuid, algorithm,
171+
)
172+
ch <- prometheus.MustNewConstMetric(
173+
bcachefsCompressionUncompressedBytesDesc,
174+
prometheus.GaugeValue,
175+
float64(comp.UncompressedBytes),
176+
uuid, algorithm,
177+
)
178+
}
179+
180+
for errorType, errStats := range s.Errors {
181+
ch <- prometheus.MustNewConstMetric(
182+
bcachefsErrorsTotalDesc,
183+
prometheus.CounterValue,
184+
float64(errStats.Count),
185+
uuid, errorType,
186+
)
187+
}
188+
189+
for counterName, counterStats := range s.Counters {
190+
metricName := SanitizeMetricName(counterName) + "_total"
191+
ch <- prometheus.MustNewConstMetric(
192+
prometheus.NewDesc(
193+
prometheus.BuildFQName(namespace, subsystem, metricName),
194+
fmt.Sprintf("Bcachefs counter %s since filesystem creation.", counterName),
195+
[]string{"uuid"},
196+
nil,
197+
),
198+
prometheus.CounterValue,
199+
float64(counterStats.SinceFilesystemCreation),
200+
uuid,
201+
)
202+
}
203+
204+
for writeType, writeStats := range s.BtreeWrites {
205+
ch <- prometheus.MustNewConstMetric(
206+
bcachefsBtreeWritesTotalDesc,
207+
prometheus.CounterValue,
208+
float64(writeStats.Count),
209+
uuid, writeType,
210+
)
211+
ch <- prometheus.MustNewConstMetric(
212+
bcachefsBtreeWriteAverageSizeBytesDesc,
213+
prometheus.GaugeValue,
214+
float64(writeStats.SizeBytes),
215+
uuid, writeType,
216+
)
217+
}
218+
219+
for device, devStats := range s.Devices {
220+
if devStats == nil {
221+
continue
222+
}
223+
224+
ch <- prometheus.MustNewConstMetric(
225+
bcachefsDeviceInfoDesc,
226+
prometheus.GaugeValue,
227+
1,
228+
uuid, device, devStats.Label, devStats.State,
229+
)
230+
231+
ch <- prometheus.MustNewConstMetric(
232+
bcachefsDeviceBucketSizeBytesDesc,
233+
prometheus.GaugeValue,
234+
float64(devStats.BucketSizeBytes),
235+
uuid, device,
236+
)
237+
238+
ch <- prometheus.MustNewConstMetric(
239+
bcachefsDeviceBucketsDesc,
240+
prometheus.GaugeValue,
241+
float64(devStats.Buckets),
242+
uuid, device,
243+
)
244+
245+
ch <- prometheus.MustNewConstMetric(
246+
bcachefsDeviceDurabilityDesc,
247+
prometheus.GaugeValue,
248+
float64(devStats.Durability),
249+
uuid, device,
250+
)
251+
252+
for op, dataTypes := range devStats.IODone {
253+
for dataType, value := range dataTypes {
254+
ch <- prometheus.MustNewConstMetric(
255+
bcachefsDeviceIODoneBytesTotalDesc,
256+
prometheus.CounterValue,
257+
float64(value),
258+
uuid, device, op, dataType,
259+
)
260+
}
261+
}
262+
263+
for errorType, value := range devStats.IOErrors {
264+
ch <- prometheus.MustNewConstMetric(
265+
bcachefsDeviceIOErrorsTotalDesc,
266+
prometheus.CounterValue,
267+
float64(value),
268+
uuid, device, errorType,
269+
)
270+
}
271+
}
272+
}
273+
274+
return nil
275+
}

0 commit comments

Comments
 (0)