Skip to content

Commit 28758f6

Browse files
committed
[FEATURE] Add fscache collector
Signed-off-by: Marek Molisch <marek.molisch@innovatrics.com>
1 parent 38d32a3 commit 28758f6

File tree

5 files changed

+427
-1
lines changed

5 files changed

+427
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ devstat | Exposes device statistics | Dragonfly, FreeBSD
194194
drm | Expose GPU metrics using sysfs / DRM, `amdgpu` is the only driver which exposes this information through DRM | Linux
195195
drbd | Exposes Distributed Replicated Block Device statistics (to version 8.4) | Linux
196196
ethtool | Exposes network interface information and network driver statistics equivalent to `ethtool`, `ethtool -S`, and `ethtool -i`. | Linux
197+
fscache | Exposes FS-Cache statistics from `/proc/fs/fscache/stats`. | Linux
197198
interrupts | Exposes detailed interrupts statistics. | Linux, OpenBSD
198199
ksmd | Exposes kernel and system statistics from `/sys/kernel/mm/ksm`. | Linux
199200
lnstat | Exposes stats from `/proc/net/stat/`. | Linux
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FS-Cache statistics
2+
Cookies: idx=16 dat=31970 spc=0
3+
Objects: alc=0 nal=0 avl=0 ded=0
4+
ChkAux : non=0 ok=0 upd=0 obs=0
5+
Pages : mrk=0 unc=0
6+
Acquire: n=31998 nul=0 noc=0 ok=31986 nbf=0 oom=0
7+
Lookups: n=0 neg=0 pos=0 crt=0 tmo=0
8+
Invals : n=409 run=0
9+
Updates: n=0 nul=0 run=0
10+
Relinqs: n=31939 nul=0 wcr=0 rtr=0
11+
AttrChg: n=0 ok=0 nbf=0 oom=0 run=0
12+
Allocs : n=0 ok=0 wt=0 nbf=0 int=0
13+
Allocs : ops=0 owt=0 abt=0
14+
Retrvls: n=2551742 ok=0 wt=0 nod=0 nbf=2551742 int=0 oom=0
15+
Retrvls: ops=0 owt=0 abt=0
16+
Stores : n=0 ok=0 agn=0 nbf=0 oom=0
17+
Stores : ops=0 run=0 pgs=0 rxd=0 olm=0
18+
VmScan : nos=0 gon=0 bsy=0 can=0 wt=0
19+
Ops : pend=0 run=0 enq=0 can=0 rej=0
20+
Ops : ini=0 dfr=0 rel=0 gc=0
21+
CacheOp: alo=0 luo=0 luc=0 gro=0
22+
CacheOp: inv=0 upo=0 dro=0 pto=0 atc=0 syn=0
23+
CacheOp: rap=0 ras=0 alp=0 als=0 wrp=0 ucp=0 dsp=0
24+
CacheEv: nsp=0 stl=0 rtr=0 cul=0
25+
RdHelp : RA=0 RP=0 WB=0 WBZ=0 rr=0 sr=0
26+
RdHelp : ZR=0 sh=0 sk=0
27+
RdHelp : DL=0 ds=0 df=0 di=0
28+
RdHelp : RD=0 rs=0 rf=0
29+
RdHelp : WR=0 ws=0 wf=0

collector/fscache_linux.go

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright 2024 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 !nofscache
15+
// +build !nofscache
16+
17+
package collector
18+
19+
import (
20+
"fmt"
21+
"log/slog"
22+
"os"
23+
24+
"github.com/prometheus/client_golang/prometheus"
25+
"github.com/prometheus/procfs"
26+
)
27+
28+
const (
29+
fscacheSubsystem = "fscache"
30+
)
31+
32+
type fscacheCollector struct {
33+
// Metrics
34+
objectsAllocated *prometheus.Desc
35+
objectsAvailable *prometheus.Desc
36+
37+
acquireAttempts *prometheus.Desc
38+
acquireSuccess *prometheus.Desc
39+
40+
lookupsTotal *prometheus.Desc
41+
lookupsPositive *prometheus.Desc
42+
lookupsNegative *prometheus.Desc
43+
44+
invalidationsTotal *prometheus.Desc
45+
46+
updatesTotal *prometheus.Desc
47+
48+
relinquishesTotal *prometheus.Desc
49+
50+
attributeChangesTotal *prometheus.Desc
51+
attributeChangesSuccess *prometheus.Desc
52+
53+
allocationsTotal *prometheus.Desc
54+
allocationsSuccess *prometheus.Desc
55+
56+
retrievalsTotal *prometheus.Desc
57+
retrievalsSuccess *prometheus.Desc
58+
retrievalsNoBuffer *prometheus.Desc
59+
60+
storesTotal *prometheus.Desc
61+
storesSuccess *prometheus.Desc
62+
63+
cacheEventRetired *prometheus.Desc
64+
cacheEventCulled *prometheus.Desc
65+
66+
fs procfs.FS
67+
logger *slog.Logger
68+
}
69+
70+
var _ prometheus.Collector = (*fscacheCollector)(nil)
71+
72+
func init() {
73+
registerCollector("fscache", defaultEnabled, func(logger *slog.Logger) (Collector, error) {
74+
return NewFscacheCollector(logger)
75+
})
76+
}
77+
78+
// NewFscacheCollector returns a new Collector exposing fscache stats.
79+
func NewFscacheCollector(logger *slog.Logger) (*fscacheCollector, error) {
80+
fs, err := procfs.NewFS(*procPath)
81+
if err != nil {
82+
return nil, fmt.Errorf("failed to open procfs: %w", err)
83+
}
84+
85+
return &fscacheCollector{
86+
objectsAllocated: prometheus.NewDesc(
87+
prometheus.BuildFQName(namespace, fscacheSubsystem, "objects_allocated_total"),
88+
"Number of index cookies allocated (Cookies: idx=allocated/available/unused).",
89+
nil, nil,
90+
),
91+
objectsAvailable: prometheus.NewDesc(
92+
prometheus.BuildFQName(namespace, fscacheSubsystem, "objects_available_total"),
93+
"Number of index cookies available (Cookies: idx=allocated/available/unused).",
94+
nil, nil,
95+
),
96+
acquireAttempts: prometheus.NewDesc(
97+
prometheus.BuildFQName(namespace, fscacheSubsystem, "acquire_attempts_total"),
98+
"Number of acquire operations attempted (Acquire: n=attempts).",
99+
nil, nil,
100+
),
101+
acquireSuccess: prometheus.NewDesc(
102+
prometheus.BuildFQName(namespace, fscacheSubsystem, "acquire_success_total"),
103+
"Number of acquire operations successful (Acquire: ok=success).",
104+
nil, nil,
105+
),
106+
lookupsTotal: prometheus.NewDesc(
107+
prometheus.BuildFQName(namespace, fscacheSubsystem, "lookups_total"),
108+
"Number of lookup operations (Lookups: n=tot).",
109+
nil, nil,
110+
),
111+
lookupsPositive: prometheus.NewDesc(
112+
prometheus.BuildFQName(namespace, fscacheSubsystem, "lookups_positive_total"),
113+
"Number of positive lookup operations (Lookups: pos=positive).",
114+
nil, nil,
115+
),
116+
lookupsNegative: prometheus.NewDesc(
117+
prometheus.BuildFQName(namespace, fscacheSubsystem, "lookups_negative_total"),
118+
"Number of negative lookup operations (Lookups: neg=negative).",
119+
nil, nil,
120+
),
121+
invalidationsTotal: prometheus.NewDesc(
122+
prometheus.BuildFQName(namespace, fscacheSubsystem, "invalidations_total"),
123+
"Number of invalidation operations (Invals: n=tot).",
124+
nil, nil,
125+
),
126+
updatesTotal: prometheus.NewDesc(
127+
prometheus.BuildFQName(namespace, fscacheSubsystem, "updates_total"),
128+
"Number of update operations (Updates: n=tot).",
129+
nil, nil,
130+
),
131+
relinquishesTotal: prometheus.NewDesc(
132+
prometheus.BuildFQName(namespace, fscacheSubsystem, "relinquishes_total"),
133+
"Number of relinquish operations (Relinqs: n=tot).",
134+
nil, nil,
135+
),
136+
attributeChangesTotal: prometheus.NewDesc(
137+
prometheus.BuildFQName(namespace, fscacheSubsystem, "attribute_changes_total"),
138+
"Number of attribute change operations attempted (AttrChg: n=attempts).",
139+
nil, nil,
140+
),
141+
attributeChangesSuccess: prometheus.NewDesc(
142+
prometheus.BuildFQName(namespace, fscacheSubsystem, "attribute_changes_success_total"),
143+
"Number of successful attribute change operations (AttrChg: ok=success).",
144+
nil, nil,
145+
),
146+
allocationsTotal: prometheus.NewDesc(
147+
prometheus.BuildFQName(namespace, fscacheSubsystem, "allocations_total"),
148+
"Number of allocation operations attempted (Allocs: n=attempts).",
149+
nil, nil,
150+
),
151+
allocationsSuccess: prometheus.NewDesc(
152+
prometheus.BuildFQName(namespace, fscacheSubsystem, "allocations_success_total"),
153+
"Number of successful allocation operations (Allocs: ok=success).",
154+
nil, nil,
155+
),
156+
retrievalsTotal: prometheus.NewDesc(
157+
prometheus.BuildFQName(namespace, fscacheSubsystem, "retrievals_total"),
158+
"Number of retrieval (read) operations attempted (Retrvls: n=attempts).",
159+
nil, nil,
160+
),
161+
retrievalsSuccess: prometheus.NewDesc(
162+
prometheus.BuildFQName(namespace, fscacheSubsystem, "retrievals_success_total"),
163+
"Number of successful retrieval (read) operations (Retrvls: ok=success).",
164+
nil, nil,
165+
),
166+
retrievalsNoBuffer: prometheus.NewDesc(
167+
prometheus.BuildFQName(namespace, fscacheSubsystem, "retrievals_nobuffer_total"),
168+
"Number of retrieval (read) operations failed due to no buffer (Retrvls: nbf=nobuff).",
169+
nil, nil,
170+
),
171+
storesTotal: prometheus.NewDesc(
172+
prometheus.BuildFQName(namespace, fscacheSubsystem, "stores_total"),
173+
"Number of store (write) operations attempted (Stores: n=attempts).",
174+
nil, nil,
175+
),
176+
storesSuccess: prometheus.NewDesc(
177+
prometheus.BuildFQName(namespace, fscacheSubsystem, "stores_success_total"),
178+
"Number of successful store (write) operations (Stores: ok=success).",
179+
nil, nil,
180+
),
181+
cacheEventRetired: prometheus.NewDesc(
182+
prometheus.BuildFQName(namespace, fscacheSubsystem, "objects_retired_total"),
183+
"Number of objects retired (CacheEv: rtr=retired).",
184+
nil, nil,
185+
),
186+
cacheEventCulled: prometheus.NewDesc(
187+
prometheus.BuildFQName(namespace, fscacheSubsystem, "objects_culled_total"),
188+
"Number of objects culled (CacheEv: cul=culled).",
189+
nil, nil,
190+
),
191+
fs: fs,
192+
logger: logger,
193+
}, nil
194+
}
195+
196+
// Describe implements prometheus.Collector.
197+
func (c *fscacheCollector) Describe(ch chan<- *prometheus.Desc) {
198+
ch <- c.objectsAllocated
199+
ch <- c.objectsAvailable
200+
ch <- c.acquireAttempts
201+
ch <- c.acquireSuccess
202+
ch <- c.lookupsTotal
203+
ch <- c.lookupsPositive
204+
ch <- c.lookupsNegative
205+
ch <- c.invalidationsTotal
206+
ch <- c.updatesTotal
207+
ch <- c.relinquishesTotal
208+
ch <- c.attributeChangesTotal
209+
ch <- c.attributeChangesSuccess
210+
ch <- c.allocationsTotal
211+
ch <- c.allocationsSuccess
212+
ch <- c.retrievalsTotal
213+
ch <- c.retrievalsSuccess
214+
ch <- c.retrievalsNoBuffer
215+
ch <- c.storesTotal
216+
ch <- c.storesSuccess
217+
ch <- c.cacheEventRetired
218+
ch <- c.cacheEventCulled
219+
}
220+
221+
// Collect implements prometheus.Collector.
222+
func (c *fscacheCollector) Collect(ch chan<- prometheus.Metric) {
223+
// Let the collector helper handle scrape success/failure based on Update's error.
224+
if err := c.Update(ch); err != nil {
225+
// Optionally log the error, but don't send invalid metrics here.
226+
c.logger.Error("Error updating fscache stats", "err", err)
227+
}
228+
}
229+
230+
// Update gathers metrics from the fscache subsystem.
231+
func (c *fscacheCollector) Update(ch chan<- prometheus.Metric) error {
232+
stats, err := c.fs.Fscacheinfo()
233+
if err != nil {
234+
if os.IsNotExist(err) {
235+
c.logger.Debug("Not collecting fscache statistics, as /proc/fs/fscache/stats is not available", "err", err)
236+
return ErrNoData
237+
}
238+
return fmt.Errorf("could not get fscache stats: %w", err)
239+
}
240+
241+
ch <- prometheus.MustNewConstMetric(c.objectsAllocated, prometheus.CounterValue, float64(stats.IndexCookiesAllocated))
242+
ch <- prometheus.MustNewConstMetric(c.objectsAvailable, prometheus.CounterValue, float64(stats.ObjectsAvailable))
243+
ch <- prometheus.MustNewConstMetric(c.acquireAttempts, prometheus.CounterValue, float64(stats.AcquireCookiesRequestSeen))
244+
ch <- prometheus.MustNewConstMetric(c.acquireSuccess, prometheus.CounterValue, float64(stats.AcquireRequestsSucceeded))
245+
ch <- prometheus.MustNewConstMetric(c.lookupsTotal, prometheus.CounterValue, float64(stats.LookupsNumber))
246+
ch <- prometheus.MustNewConstMetric(c.lookupsPositive, prometheus.CounterValue, float64(stats.LookupsPositive))
247+
ch <- prometheus.MustNewConstMetric(c.lookupsNegative, prometheus.CounterValue, float64(stats.LookupsNegative))
248+
ch <- prometheus.MustNewConstMetric(c.invalidationsTotal, prometheus.CounterValue, float64(stats.InvalidationsNumber))
249+
ch <- prometheus.MustNewConstMetric(c.updatesTotal, prometheus.CounterValue, float64(stats.UpdateCookieRequestSeen))
250+
ch <- prometheus.MustNewConstMetric(c.relinquishesTotal, prometheus.CounterValue, float64(stats.RelinquishCookiesRequestSeen))
251+
ch <- prometheus.MustNewConstMetric(c.attributeChangesTotal, prometheus.CounterValue, float64(stats.AttributeChangedRequestsSeen))
252+
ch <- prometheus.MustNewConstMetric(c.attributeChangesSuccess, prometheus.CounterValue, float64(stats.AttributeChangedOps))
253+
ch <- prometheus.MustNewConstMetric(c.allocationsTotal, prometheus.CounterValue, float64(stats.AllocationRequestsSeen))
254+
ch <- prometheus.MustNewConstMetric(c.allocationsSuccess, prometheus.CounterValue, float64(stats.AllocationOkRequests))
255+
ch <- prometheus.MustNewConstMetric(c.retrievalsTotal, prometheus.CounterValue, float64(stats.RetrievalsReadRequests))
256+
ch <- prometheus.MustNewConstMetric(c.retrievalsSuccess, prometheus.CounterValue, float64(stats.RetrievalsOk))
257+
ch <- prometheus.MustNewConstMetric(c.retrievalsNoBuffer, prometheus.CounterValue, float64(stats.RetrievalsRejectedDueToEnobufs))
258+
ch <- prometheus.MustNewConstMetric(c.storesTotal, prometheus.CounterValue, float64(stats.StoreWriteRequests))
259+
ch <- prometheus.MustNewConstMetric(c.storesSuccess, prometheus.CounterValue, float64(stats.StoreSuccessfulRequests))
260+
ch <- prometheus.MustNewConstMetric(c.cacheEventRetired, prometheus.CounterValue, float64(stats.CacheevRetiredWhenReliquished))
261+
ch <- prometheus.MustNewConstMetric(c.cacheEventCulled, prometheus.CounterValue, float64(stats.CacheevObjectsCulled))
262+
263+
return nil
264+
}

0 commit comments

Comments
 (0)