Updates
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Unbound DNS collector via unbound-control or remote HTTP stats.
|
||||
* Enable: PEARDATA_UNBOUND=1
|
||||
*/
|
||||
import { spawnSync } from 'child_process'
|
||||
import { CollectorPlugin } from './plugin.js'
|
||||
import { registerChart } from '../../../shared/metrics.js'
|
||||
|
||||
export function isUnboundEnabled() {
|
||||
const v = process.env.PEARDATA_UNBOUND
|
||||
return v === '1' || v === 'on' || v === 'true'
|
||||
}
|
||||
|
||||
export class UnboundCollector extends CollectorPlugin {
|
||||
constructor() {
|
||||
super({ name: 'unbound' })
|
||||
this.prev = null
|
||||
this.prevTs = 0
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
return isUnboundEnabled()
|
||||
}
|
||||
|
||||
async collect() {
|
||||
const ts = Date.now()
|
||||
/** @type {Record<string, number>} */
|
||||
const stats = {}
|
||||
const http = process.env.PEARDATA_UNBOUND_URL
|
||||
if (http) {
|
||||
try {
|
||||
const res = await fetch(http, { signal: AbortSignal.timeout(2000) })
|
||||
const text = await res.text()
|
||||
for (const line of text.split('\n')) {
|
||||
const [k, v] = line.split('=')
|
||||
if (k && v != null && Number.isFinite(Number(v))) stats[k.trim()] = Number(v)
|
||||
}
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
if (!Object.keys(stats).length) {
|
||||
const res = spawnSync('unbound-control', ['stats_noreset'], {
|
||||
encoding: 'utf8',
|
||||
timeout: 2000,
|
||||
})
|
||||
if (res.status === 0 && res.stdout) {
|
||||
for (const line of res.stdout.split('\n')) {
|
||||
const [k, v] = line.split('=')
|
||||
if (k && v != null && Number.isFinite(Number(v))) stats[k.trim()] = Number(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
registerChart({
|
||||
id: 'unbound.up',
|
||||
name: 'unbound.up',
|
||||
context: 'unbound.up',
|
||||
title: 'Unbound up',
|
||||
units: 'boolean',
|
||||
family: 'unbound',
|
||||
chartType: 'line',
|
||||
priority: 8900,
|
||||
plugin: 'unbound',
|
||||
dimensions: [{ id: 'up', name: 'up', algorithm: 'absolute' }],
|
||||
})
|
||||
if (!Object.keys(stats).length) {
|
||||
return [{ chart: 'unbound.up', context: 'unbound.up', ts, values: { up: 0 } }]
|
||||
}
|
||||
const dt = this.prevTs ? (ts - this.prevTs) / 1000 : 0
|
||||
const prev = this.prev
|
||||
const num = (k) => stats[k] || 0
|
||||
const rate = (k) => (prev && dt > 0 ? Math.max(0, num(k) - (prev[k] || 0)) / dt : 0)
|
||||
registerChart({
|
||||
id: 'unbound.queries',
|
||||
name: 'unbound.queries',
|
||||
context: 'unbound.queries',
|
||||
title: 'Unbound queries',
|
||||
units: 'queries/s',
|
||||
family: 'unbound',
|
||||
chartType: 'line',
|
||||
priority: 8910,
|
||||
plugin: 'unbound',
|
||||
dimensions: [
|
||||
{ id: 'total', name: 'total', algorithm: 'incremental' },
|
||||
{ id: 'cache_hits', name: 'cache_hits', algorithm: 'incremental' },
|
||||
{ id: 'cache_misses', name: 'cache_misses', algorithm: 'incremental' },
|
||||
],
|
||||
})
|
||||
this.prev = stats
|
||||
this.prevTs = ts
|
||||
return [
|
||||
{ chart: 'unbound.up', context: 'unbound.up', ts, values: { up: 1 } },
|
||||
{
|
||||
chart: 'unbound.queries',
|
||||
context: 'unbound.queries',
|
||||
ts,
|
||||
values: {
|
||||
total: rate('total.num.queries') || rate('num.queries'),
|
||||
cache_hits: rate('total.num.cachehits') || rate('num.cachehits'),
|
||||
cache_misses: rate('total.num.cachemiss') || rate('num.cachemiss'),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
let singleton = null
|
||||
export function getUnboundCollector() {
|
||||
if (!singleton) singleton = new UnboundCollector()
|
||||
return singleton
|
||||
}
|
||||
Reference in New Issue
Block a user