/** * Metric contexts, charts, and dimension catalog. * * Context IDs follow family.metric style (e.g. system.cpu). * Static charts cover host-wide system stats; instance charts * (per-CPU, per-disk, per-iface, per-mount) are registered at runtime. */ import { DEEP_STATIC_CHARTS } from './metrics-deep.js' import { MORE_STATIC_CHARTS } from './metrics-more.js' export const SAMPLE_INTERVAL_MS = 1000 export { makeDiskAwaitChart, makeDiskAvgszChart, makeDiskQopsChart, makeDiskBusyChart, makeDiskIotimeChart, makeDiskDiscardChart, makeNetSpeedChart, makeCgroupCpuChart, makeCgroupMemChart, makeCgroupIoChart, makeSensorTempChart, makeThermalZoneChart, } from './metrics-deep.js' export { makeDiskSvctmChart, makeDiskMergedChart, makeDiskFlushChart, makeNetDuplexChart, makeNetMtuChart, makeNetQueueChart, makeIrqChart, makeCpuIdleChart, makeNumaNodeMemChart, makeCgroupMemDetailChart, makeCgroupThrottleChart, } from './metrics-more.js' /** * @typedef {{ id: string, name: string, algorithm: 'absolute'|'incremental', multiplier?: number, divisor?: number }} DimensionDef * @typedef {{ * id: string, * name: string, * context: string, * title: string, * units: string, * family: string, * chartType: 'line'|'area'|'stacked', * priority: number, * dimensions: DimensionDef[], * plugin?: string, * }} ChartDef */ /** @type {ChartDef[]} */ export const STATIC_CHART_DEFS = [ // ── CPU / scheduler ─────────────────────────────────────── { id: 'system.cpu', name: 'system.cpu', context: 'system.cpu', title: 'Total CPU utilization', units: 'percentage', family: 'cpu', chartType: 'stacked', priority: 100, plugin: 'proc', dimensions: [ { id: 'guest_nice', name: 'guest_nice', algorithm: 'absolute' }, { id: 'guest', name: 'guest', algorithm: 'absolute' }, { id: 'steal', name: 'steal', algorithm: 'absolute' }, { id: 'softirq', name: 'softirq', algorithm: 'absolute' }, { id: 'irq', name: 'irq', algorithm: 'absolute' }, { id: 'user', name: 'user', algorithm: 'absolute' }, { id: 'system', name: 'system', algorithm: 'absolute' }, { id: 'nice', name: 'nice', algorithm: 'absolute' }, { id: 'iowait', name: 'iowait', algorithm: 'absolute' }, { id: 'idle', name: 'idle', algorithm: 'absolute' }, ], }, { id: 'system.intr', name: 'system.intr', context: 'system.intr', title: 'CPU Interrupts', units: 'interrupts/s', family: 'interrupts', chartType: 'line', priority: 110, plugin: 'proc', dimensions: [{ id: 'interrupts', name: 'interrupts', algorithm: 'incremental' }], }, { id: 'system.ctxt', name: 'system.ctxt', context: 'system.ctxt', title: 'CPU Context Switches', units: 'context switches/s', family: 'processes', chartType: 'line', priority: 120, plugin: 'proc', dimensions: [{ id: 'switches', name: 'switches', algorithm: 'incremental' }], }, { id: 'system.forks', name: 'system.forks', context: 'system.forks', title: 'Started Processes', units: 'processes/s', family: 'processes', chartType: 'line', priority: 130, plugin: 'proc', dimensions: [{ id: 'started', name: 'started', algorithm: 'incremental' }], }, { id: 'system.processes', name: 'system.processes', context: 'system.processes', title: 'System Processes', units: 'processes', family: 'processes', chartType: 'line', priority: 140, plugin: 'proc', dimensions: [ { id: 'running', name: 'running', algorithm: 'absolute' }, { id: 'blocked', name: 'blocked', algorithm: 'absolute' }, ], }, { id: 'system.active_processes', name: 'system.active_processes', context: 'system.active_processes', title: 'System Active Processes', units: 'processes', family: 'processes', chartType: 'line', priority: 145, plugin: 'proc', dimensions: [{ id: 'active', name: 'active', algorithm: 'absolute' }], }, { id: 'system.load', name: 'system.load', context: 'system.load', title: 'System Load Average', units: 'load', family: 'load', chartType: 'line', priority: 150, plugin: 'proc', dimensions: [ { id: 'load1', name: 'load1', algorithm: 'absolute' }, { id: 'load5', name: 'load5', algorithm: 'absolute' }, { id: 'load15', name: 'load15', algorithm: 'absolute' }, ], }, { id: 'system.uptime', name: 'system.uptime', context: 'system.uptime', title: 'System Uptime', units: 'seconds', family: 'uptime', chartType: 'line', priority: 160, plugin: 'proc', dimensions: [{ id: 'uptime', name: 'uptime', algorithm: 'absolute' }], }, { id: 'system.entropy', name: 'system.entropy', context: 'system.entropy', title: 'Available Entropy', units: 'entropy', family: 'entropy', chartType: 'line', priority: 170, plugin: 'proc', dimensions: [{ id: 'entropy', name: 'entropy', algorithm: 'absolute' }], }, // ── Memory ──────────────────────────────────────────────── { id: 'system.ram', name: 'system.ram', context: 'system.ram', title: 'System RAM', units: 'MiB', family: 'ram', chartType: 'stacked', priority: 200, plugin: 'proc', dimensions: [ { id: 'free', name: 'free', algorithm: 'absolute' }, { id: 'used', name: 'used', algorithm: 'absolute' }, { id: 'cached', name: 'cached', algorithm: 'absolute' }, { id: 'buffers', name: 'buffers', algorithm: 'absolute' }, ], }, { id: 'mem.available', name: 'mem.available', context: 'mem.available', title: 'Available RAM for applications', units: 'MiB', family: 'ram', chartType: 'area', priority: 210, plugin: 'proc', dimensions: [{ id: 'avail', name: 'avail', algorithm: 'absolute' }], }, { id: 'mem.swap', name: 'mem.swap', context: 'mem.swap', title: 'System Swap', units: 'MiB', family: 'swap', chartType: 'stacked', priority: 220, plugin: 'proc', dimensions: [ { id: 'free', name: 'free', algorithm: 'absolute' }, { id: 'used', name: 'used', algorithm: 'absolute' }, ], }, { id: 'mem.swap_cached', name: 'mem.swap_cached', context: 'mem.swap_cached', title: 'Swap Cached', units: 'MiB', family: 'swap', chartType: 'area', priority: 225, plugin: 'proc', dimensions: [{ id: 'cached', name: 'cached', algorithm: 'absolute' }], }, { id: 'mem.kernel', name: 'mem.kernel', context: 'mem.kernel', title: 'Memory Used by Kernel', units: 'MiB', family: 'kernel', chartType: 'stacked', priority: 230, plugin: 'proc', dimensions: [ { id: 'slab', name: 'slab', algorithm: 'absolute' }, { id: 'kernel_stack', name: 'kernel_stack', algorithm: 'absolute' }, { id: 'page_tables', name: 'page_tables', algorithm: 'absolute' }, { id: 'vmalloc_used', name: 'vmalloc_used', algorithm: 'absolute' }, { id: 'percpu', name: 'percpu', algorithm: 'absolute' }, ], }, { id: 'mem.slab', name: 'mem.slab', context: 'mem.slab', title: 'Reclaimable Kernel Memory', units: 'MiB', family: 'slab', chartType: 'stacked', priority: 235, plugin: 'proc', dimensions: [ { id: 'reclaimable', name: 'reclaimable', algorithm: 'absolute' }, { id: 'unreclaimable', name: 'unreclaimable', algorithm: 'absolute' }, ], }, { id: 'mem.writeback', name: 'mem.writeback', context: 'mem.writeback', title: 'Writeback Memory', units: 'MiB', family: 'writeback', chartType: 'line', priority: 240, plugin: 'proc', dimensions: [ { id: 'dirty', name: 'dirty', algorithm: 'absolute' }, { id: 'writeback', name: 'writeback', algorithm: 'absolute' }, { id: 'FuseWriteback', name: 'FuseWriteback', algorithm: 'absolute' }, { id: 'NfsWriteback', name: 'NfsWriteback', algorithm: 'absolute' }, { id: 'Bounce', name: 'Bounce', algorithm: 'absolute' }, ], }, { id: 'mem.committed', name: 'mem.committed', context: 'mem.committed', title: 'Committed (Allocated) Memory', units: 'MiB', family: 'committed', chartType: 'area', priority: 245, plugin: 'proc', dimensions: [{ id: 'Committed_AS', name: 'Committed_AS', algorithm: 'absolute' }], }, { id: 'mem.swapio', name: 'mem.swapio', context: 'mem.swapio', title: 'Swap I/O', units: 'KiB/s', family: 'swap', chartType: 'area', priority: 250, plugin: 'proc', dimensions: [ { id: 'in', name: 'in', algorithm: 'incremental' }, { id: 'out', name: 'out', algorithm: 'incremental' }, ], }, { id: 'system.pgpgio', name: 'system.pgpgio', context: 'system.pgpgio', title: 'Memory Page I/O', units: 'KiB/s', family: 'pgpgio', chartType: 'area', priority: 260, plugin: 'proc', dimensions: [ { id: 'in', name: 'in', algorithm: 'incremental' }, { id: 'out', name: 'out', algorithm: 'incremental' }, ], }, { id: 'system.pgfaults', name: 'system.pgfaults', context: 'system.pgfaults', title: 'Memory Page Faults', units: 'faults/s', family: 'pgfaults', chartType: 'line', priority: 270, plugin: 'proc', dimensions: [ { id: 'minor', name: 'minor', algorithm: 'incremental' }, { id: 'major', name: 'major', algorithm: 'incremental' }, ], }, // ── Aggregate disk / network ────────────────────────────── { id: 'system.io', name: 'system.io', context: 'system.io', title: 'Disk I/O', units: 'KiB/s', family: 'disk', chartType: 'area', priority: 400, plugin: 'proc', dimensions: [ { id: 'in', name: 'in', algorithm: 'incremental' }, { id: 'out', name: 'out', algorithm: 'incremental' }, ], }, { id: 'system.net', name: 'system.net', context: 'system.net', title: 'Physical Network Interfaces Bandwidth', units: 'kilobits/s', family: 'network', chartType: 'area', priority: 500, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], }, { id: 'system.ip', name: 'system.ip', context: 'system.ip', title: 'IP Bandwidth', units: 'kilobits/s', family: 'ip', chartType: 'area', priority: 600, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], }, { id: 'system.ipv6', name: 'system.ipv6', context: 'system.ipv6', title: 'IPv6 Bandwidth', units: 'kilobits/s', family: 'ipv6', chartType: 'area', priority: 610, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], }, // ── IP / TCP / UDP aggregates ───────────────────────────── { id: 'ip.tcppackets', name: 'ip.tcppackets', context: 'ip.tcppackets', title: 'TCP Packets', units: 'packets/s', family: 'tcp', chartType: 'line', priority: 700, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], }, { id: 'ip.tcperrors', name: 'ip.tcperrors', context: 'ip.tcperrors', title: 'TCP Errors', units: 'packets/s', family: 'tcp', chartType: 'line', priority: 710, plugin: 'proc', dimensions: [ { id: 'InErrs', name: 'InErrs', algorithm: 'incremental' }, { id: 'InCsumErrors', name: 'InCsumErrors', algorithm: 'incremental' }, { id: 'RetransSegs', name: 'RetransSegs', algorithm: 'incremental' }, ], }, { id: 'ip.tcpopens', name: 'ip.tcpopens', context: 'ip.tcpopens', title: 'TCP Connections', units: 'connections/s', family: 'tcp', chartType: 'line', priority: 720, plugin: 'proc', dimensions: [ { id: 'active', name: 'active', algorithm: 'incremental' }, { id: 'passive', name: 'passive', algorithm: 'incremental' }, ], }, { id: 'ip.tcpsock', name: 'ip.tcpsock', context: 'ip.tcpsock', title: 'TCP Connections Current', units: 'connections', family: 'tcp', chartType: 'line', priority: 725, plugin: 'proc', dimensions: [{ id: 'connections', name: 'connections', algorithm: 'absolute' }], }, { id: 'ipv4.packets', name: 'ipv4.packets', context: 'ipv4.packets', title: 'IPv4 Packets', units: 'packets/s', family: 'packets', chartType: 'line', priority: 740, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, { id: 'forwarded', name: 'forwarded', algorithm: 'incremental' }, { id: 'delivered', name: 'delivered', algorithm: 'incremental' }, ], }, { id: 'ipv4.errors', name: 'ipv4.errors', context: 'ipv4.errors', title: 'IPv4 Errors', units: 'packets/s', family: 'errors', chartType: 'line', priority: 750, plugin: 'proc', dimensions: [ { id: 'InDiscards', name: 'InDiscards', algorithm: 'incremental' }, { id: 'OutDiscards', name: 'OutDiscards', algorithm: 'incremental' }, { id: 'InHdrErrors', name: 'InHdrErrors', algorithm: 'incremental' }, { id: 'OutNoRoutes', name: 'OutNoRoutes', algorithm: 'incremental' }, ], }, { id: 'ipv4.udppackets', name: 'ipv4.udppackets', context: 'ipv4.udppackets', title: 'IPv4 UDP Packets', units: 'packets/s', family: 'udp', chartType: 'line', priority: 760, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], }, { id: 'ipv4.udperrors', name: 'ipv4.udperrors', context: 'ipv4.udperrors', title: 'IPv4 UDP Errors', units: 'packets/s', family: 'udp', chartType: 'line', priority: 770, plugin: 'proc', dimensions: [ { id: 'RcvbufErrors', name: 'RcvbufErrors', algorithm: 'incremental' }, { id: 'SndbufErrors', name: 'SndbufErrors', algorithm: 'incremental' }, { id: 'InErrors', name: 'InErrors', algorithm: 'incremental' }, { id: 'NoPorts', name: 'NoPorts', algorithm: 'incremental' }, ], }, // ── Pressure stall (PSI) ────────────────────────────────── { id: 'system.cpu_some_pressure', name: 'system.cpu_some_pressure', context: 'system.cpu_some_pressure', title: 'CPU Some Pressure', units: 'percentage', family: 'pressure', chartType: 'line', priority: 800, plugin: 'proc', dimensions: [ { id: 'some10', name: 'some10', algorithm: 'absolute' }, { id: 'some60', name: 'some60', algorithm: 'absolute' }, { id: 'some300', name: 'some300', algorithm: 'absolute' }, ], }, { id: 'system.memory_some_pressure', name: 'system.memory_some_pressure', context: 'system.memory_some_pressure', title: 'Memory Some Pressure', units: 'percentage', family: 'pressure', chartType: 'line', priority: 810, plugin: 'proc', dimensions: [ { id: 'some10', name: 'some10', algorithm: 'absolute' }, { id: 'some60', name: 'some60', algorithm: 'absolute' }, { id: 'some300', name: 'some300', algorithm: 'absolute' }, ], }, { id: 'system.io_some_pressure', name: 'system.io_some_pressure', context: 'system.io_some_pressure', title: 'I/O Some Pressure', units: 'percentage', family: 'pressure', chartType: 'line', priority: 820, plugin: 'proc', dimensions: [ { id: 'some10', name: 'some10', algorithm: 'absolute' }, { id: 'some60', name: 'some60', algorithm: 'absolute' }, { id: 'some300', name: 'some300', algorithm: 'absolute' }, ], }, ...DEEP_STATIC_CHARTS, ...MORE_STATIC_CHARTS, ] /** @type {Map} */ const runtimeCharts = new Map() /** Static (host-wide catalog) chart ids — used for warm HyperDB cardinality gating. */ const STATIC_CHART_IDS = new Set(STATIC_CHART_DEFS.map((c) => c.id)) /** * Whether a chart should be flushed to HyperDB warm storage. * High-cardinality instance charts (per-disk, per-iface, cgroup, docker, …) * stay in RAM rings only unless PEARDATA_WARM_ALL_CHARTS=1. * * @param {string} chartId * @returns {boolean} */ export function isWarmPersistableChart(chartId) { const all = process.env.PEARDATA_WARM_ALL_CHARTS if (all === '1' || all === 'true' || all === 'on') return true return STATIC_CHART_IDS.has(String(chartId)) } /** * Register or refresh a dynamic instance chart (per-cpu / disk / iface / mount). * @param {ChartDef} def */ export function registerChart(def) { runtimeCharts.set(def.id, def) } /** * @returns {ChartDef[]} */ export function getAllChartDefs() { return [...STATIC_CHART_DEFS, ...runtimeCharts.values()] } /** Alias — prefer getAllChartDefs() when instance charts matter. */ export const CHART_DEFS = STATIC_CHART_DEFS export const CHART_BY_ID = { get(id) { const sid = String(id) return STATIC_CHART_DEFS.find((c) => c.id === sid) || runtimeCharts.get(sid) || null }, has(id) { return this.get(id) != null }, keys() { return getAllChartDefs().map((c) => c.id) }, values() { return getAllChartDefs() }, get size() { return getAllChartDefs().length }, } export function getContextIds() { return [...new Set(getAllChartDefs().map((c) => c.context))] } /** @deprecated use getContextIds() */ export const CONTEXT_IDS = { map(fn) { return getContextIds().map(fn) }, includes(id) { return getContextIds().includes(id) }, [Symbol.iterator]() { return getContextIds()[Symbol.iterator]() }, get length() { return getContextIds().length }, } /** * @param {string} context */ export function chartsForContext(context) { return getAllChartDefs().filter((c) => c.context === context) } /** * Instance chart helpers */ export function makeCpuCoreChart(coreId) { return { id: `cpu.cpu${coreId}`, name: `cpu.cpu${coreId}`, context: 'cpu.cpu', title: `Core ${coreId} utilization`, units: 'percentage', family: `cpu${coreId}`, chartType: 'stacked', priority: 1000 + Number(coreId), plugin: 'proc', dimensions: STATIC_CHART_DEFS.find((c) => c.id === 'system.cpu').dimensions.slice(), } } export function makeDiskIoChart(disk) { return { id: `disk_io.${disk}`, name: `disk_io.${disk}`, context: 'disk.io', title: `Disk ${disk} I/O`, units: 'KiB/s', family: disk, chartType: 'area', priority: 2000, plugin: 'proc', dimensions: [ { id: 'reads', name: 'reads', algorithm: 'incremental' }, { id: 'writes', name: 'writes', algorithm: 'incremental' }, ], } } export function makeDiskOpsChart(disk) { return { id: `disk_ops.${disk}`, name: `disk_ops.${disk}`, context: 'disk.ops', title: `Disk ${disk} Completed I/O Operations`, units: 'operations/s', family: disk, chartType: 'line', priority: 2010, plugin: 'proc', dimensions: [ { id: 'reads', name: 'reads', algorithm: 'incremental' }, { id: 'writes', name: 'writes', algorithm: 'incremental' }, ], } } export function makeDiskUtilChart(disk) { return { id: `disk_util.${disk}`, name: `disk_util.${disk}`, context: 'disk.util', title: `Disk ${disk} Utilization Time`, units: '% of time working', family: disk, chartType: 'area', priority: 2020, plugin: 'proc', dimensions: [{ id: 'utilization', name: 'utilization', algorithm: 'absolute' }], } } export function makeNetChart(iface) { return { id: `net.${iface}`, name: `net.${iface}`, context: 'net.net', title: `Bandwidth ${iface}`, units: 'kilobits/s', family: iface, chartType: 'area', priority: 3000, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, ], } } export function makeNetPacketsChart(iface) { return { id: `net_packets.${iface}`, name: `net_packets.${iface}`, context: 'net.packets', title: `Packets ${iface}`, units: 'packets/s', family: iface, chartType: 'line', priority: 3010, plugin: 'proc', dimensions: [ { id: 'received', name: 'received', algorithm: 'incremental' }, { id: 'sent', name: 'sent', algorithm: 'incremental' }, { id: 'multicast', name: 'multicast', algorithm: 'incremental' }, ], } } export function makeNetErrorsChart(iface) { return { id: `net_errors.${iface}`, name: `net_errors.${iface}`, context: 'net.errors', title: `Interface ${iface} Errors`, units: 'errors/s', family: iface, chartType: 'line', priority: 3020, plugin: 'proc', dimensions: [ { id: 'inbound', name: 'inbound', algorithm: 'incremental' }, { id: 'outbound', name: 'outbound', algorithm: 'incremental' }, ], } } export function makeNetDropsChart(iface) { return { id: `net_drops.${iface}`, name: `net_drops.${iface}`, context: 'net.drops', title: `Interface ${iface} Drops`, units: 'drops/s', family: iface, chartType: 'line', priority: 3030, plugin: 'proc', dimensions: [ { id: 'inbound', name: 'inbound', algorithm: 'incremental' }, { id: 'outbound', name: 'outbound', algorithm: 'incremental' }, ], } } export function makeDiskSpaceChart(mountId, mountPath) { return { id: `disk_space.${mountId}`, name: `disk_space.${mountId}`, context: 'disk.space', title: `Disk Space Usage ${mountPath}`, units: 'GiB', family: mountPath, chartType: 'stacked', priority: 4000, plugin: 'diskspace', dimensions: [ { id: 'avail', name: 'avail', algorithm: 'absolute' }, { id: 'used', name: 'used', algorithm: 'absolute' }, { id: 'reserved_for_root', name: 'reserved_for_root', algorithm: 'absolute' }, ], } } export function makeDiskInodesChart(mountId, mountPath) { return { id: `disk_inodes.${mountId}`, name: `disk_inodes.${mountId}`, context: 'disk.inodes', title: `Disk Files (inodes) Usage ${mountPath}`, units: 'inodes', family: mountPath, chartType: 'stacked', priority: 4010, plugin: 'diskspace', dimensions: [ { id: 'avail', name: 'avail', algorithm: 'absolute' }, { id: 'used', name: 'used', algorithm: 'absolute' }, { id: 'reserved_for_root', name: 'reserved_for_root', algorithm: 'absolute' }, ], } } /** Static aggregate when Docker collector is enabled */ export const DOCKER_CONTAINERS_CHART = { id: 'docker.containers', name: 'docker.containers', context: 'docker.containers', title: 'Running containers', units: 'containers', family: 'containers', chartType: 'line', priority: 5000, plugin: 'docker', dimensions: [ { id: 'running', name: 'running', algorithm: 'absolute' }, { id: 'total', name: 'total', algorithm: 'absolute' }, ], } export function makeDockerCpuChart(shortId, name) { const id = shortId.replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 64) const display = name || `container ${id}` return { id: `docker.cpu.${id}`, name: `docker.cpu.${id}`, context: 'docker.cpu', title: `${display} · CPU`, units: 'percentage', family: display, chartType: 'area', priority: 5100, plugin: 'docker', dimensions: [{ id: 'usage', name: 'usage', algorithm: 'absolute' }], } } export function makeDockerMemChart(shortId, name) { const id = shortId.replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 64) const display = name || `container ${id}` return { id: `docker.mem.${id}`, name: `docker.mem.${id}`, context: 'docker.mem', title: `${display} · memory`, units: 'MiB', family: display, chartType: 'area', priority: 5200, plugin: 'docker', dimensions: [ { id: 'usage', name: 'usage', algorithm: 'absolute' }, { id: 'limit', name: 'limit', algorithm: 'absolute' }, ], } } /** * Build a chart summary object for discovery APIs. * @param {ChartDef} def * @param {{ firstEntry?: number, lastEntry?: number, updateEvery?: number }} [meta] */ export function chartSummary(def, meta = {}) { const dimensions = {} for (const d of def.dimensions) { dimensions[d.id] = { name: d.name, algorithm: d.algorithm, multiplier: d.multiplier ?? 1, divisor: d.divisor ?? 1, } } return { id: def.id, name: def.name, type: def.plugin || 'system', family: def.family, context: def.context, title: def.title, units: def.units, chart_type: def.chartType, priority: def.priority, plugin: def.plugin || 'proc', update_every: meta.updateEvery ?? SAMPLE_INTERVAL_MS / 1000, first_entry: meta.firstEntry ?? 0, last_entry: meta.lastEntry ?? 0, dimensions, } }