92 lines
2.6 KiB
JavaScript
92 lines
2.6 KiB
JavaScript
/**
|
|
* BCache collector via /sys/fs/bcache and per-block bcache sysfs.
|
|
* Enable: PEARDATA_BCACHE=1 (auto when sysfs present)
|
|
*/
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import { CollectorPlugin } from './plugin.js'
|
|
import { registerChart } from '../../../shared/metrics.js'
|
|
|
|
export function isBcacheEnabled() {
|
|
const v = process.env.PEARDATA_BCACHE
|
|
if (v === '0' || v === 'off' || v === 'false') return false
|
|
if (v === '1' || v === 'on' || v === 'true') return true
|
|
return fs.existsSync('/sys/fs/bcache')
|
|
}
|
|
|
|
function readNum(p) {
|
|
try {
|
|
return Number(fs.readFileSync(p, 'utf8').trim()) || 0
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
export class BcacheCollector extends CollectorPlugin {
|
|
constructor() {
|
|
super({ name: 'bcache' })
|
|
this.prev = new Map()
|
|
}
|
|
|
|
isEnabled() {
|
|
return isBcacheEnabled()
|
|
}
|
|
|
|
async collect() {
|
|
const ts = Date.now()
|
|
/** @type {Array<{ chart: string, context: string, ts: number, values: object }>} */
|
|
const batch = []
|
|
let dirs = []
|
|
try {
|
|
dirs = fs.readdirSync('/sys/fs/bcache')
|
|
} catch {
|
|
return batch
|
|
}
|
|
for (const id of dirs) {
|
|
const base = path.join('/sys/fs/bcache', id)
|
|
if (!fs.existsSync(path.join(base, 'stats_total'))) continue
|
|
const hits = readNum(path.join(base, 'stats_total', 'cache_hits'))
|
|
const misses = readNum(path.join(base, 'stats_total', 'cache_misses'))
|
|
const bypass = readNum(path.join(base, 'stats_total', 'cache_bypass_hits'))
|
|
const safe = id.slice(0, 12)
|
|
const chart = `bcache.${safe}`
|
|
registerChart({
|
|
id: chart,
|
|
name: chart,
|
|
context: 'bcache.cache',
|
|
title: `BCache ${safe}`,
|
|
units: 'events/s',
|
|
family: safe,
|
|
chartType: 'line',
|
|
priority: 4700,
|
|
plugin: 'bcache',
|
|
dimensions: [
|
|
{ id: 'hits', name: 'hits', algorithm: 'incremental' },
|
|
{ id: 'misses', name: 'misses', algorithm: 'incremental' },
|
|
{ id: 'bypass', name: 'bypass', algorithm: 'incremental' },
|
|
],
|
|
})
|
|
const prev = this.prev.get(id)
|
|
const dt = this.intervalMs / 1000
|
|
batch.push({
|
|
chart,
|
|
context: 'bcache.cache',
|
|
ts,
|
|
values: {
|
|
hits: prev ? Math.max(0, hits - prev.hits) / dt : 0,
|
|
misses: prev ? Math.max(0, misses - prev.misses) / dt : 0,
|
|
bypass: prev ? Math.max(0, bypass - prev.bypass) / dt : 0,
|
|
},
|
|
})
|
|
this.prev.set(id, { hits, misses, bypass })
|
|
}
|
|
return batch
|
|
}
|
|
}
|
|
|
|
let singleton = null
|
|
export function getBcacheCollector() {
|
|
if (!singleton) singleton = new BcacheCollector()
|
|
return singleton
|
|
}
|