Skip to content

Instantly share code, notes, and snippets.

@JohnnyMarnell
Created April 23, 2019 17:46
Show Gist options
  • Save JohnnyMarnell/0ab7e95e2994b7960fe7a32667a529df to your computer and use it in GitHub Desktop.
Save JohnnyMarnell/0ab7e95e2994b7960fe7a32667a529df to your computer and use it in GitHub Desktop.
const flat = require('flat')
class PgMetrics {
constructor() {
this.successfulMetrics = 0
this.ignore = /oid/ig
this.flattenInclude = /(bg_writer|wal_archiving)/
this.tables = this.mapKeys(o => `${o.db_name}.${o.schema_name}.${o.parent_name || 'root'}.${o.name}`)
this.indexes = this.mapKeys(o => `${o.db_name}.${o.schema_name}.${o.table_name}.${o.name}`)
this.databases = this.mapKeys(o => o.name)
this.tablespaces = this.mapKeys(o => `${o.location || 'main'}.${o.owner}.${o.name}`)
this.sequences = this.mapKeys(o => `${o.db_name}.${o.schema_name}.${o.name}`)
}
parseMetrics(json) {
let raw = JSON.parse(json), metrics = {}, finalMetrics = {}
Object.entries(raw).forEach(entry => {
let key = entry[0], val = entry[1]
if (this[key]) {
this[key](metrics, key, val)
} else if (typeof val == 'number' || key.match(this.flattenInclude)) {
metrics[key] = val
}
})
Object.entries(flat(metrics)).filter(e => typeof e[1] == 'number' && !e[0].match(this.ignore)).forEach(e => {
finalMetrics[(process.env.PREFIX || "pgmetrics") + '.' + e[0]] = e[1]
})
return finalMetrics
}
backends(metrics, key, val) {
this.aggregateList(metrics, key, val, m => `${m.db_name}.${m.role_name}.${m.wait_event_type
|| 'none'}.${m.wait_event || 'none'}`, (metric, m) => {
this.minMax(metric, m, 'backend_start xact_start query_start backend_xid backend_xid backend_xmin')
})
}
locks(metrics, key, val) {
this.aggregateList(metrics, key, val, m => `${m.locktype}.${m.db_name || 'main'}.${m.mode}.${m.granted}`)
}
aggregateList(metrics, key, val, keyMapper, agg) {
let metricGroup = metrics[key] = {}
val.forEach(m => {
let mkey = keyMapper(m)
let metric = metricGroup[mkey] = metricGroup[mkey] || { count: 0 }
metric.count++
agg && agg(metric, m)
})
}
minMax(metric, next, keys) {
keys.split(' ').forEach(key => {
metric['max_' + key] = metric.count == 1 ? next[key] : Math.max(metric['max_' + key], next[key])
metric['min_' + key] = metric.count == 1 ? next[key] : Math.min(metric['min_' + key], next[key])
})
}
mapKeys(keyMapper) {
return (metrics, key, list) => list.forEach(v => metrics[key + '.' + keyMapper(v)] = v)
}
run() {
let metrics = this.parseMetrics(require('fs').readFileSync('/dev/stdin').toString())
let now = Math.floor(new Date().getTime() / 1000)
Object.entries(metrics).forEach(e => console.log(e[0], e[1], now))
}
}
new PgMetrics().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment