Skip to content

Instantly share code, notes, and snippets.

@stokito
Created August 24, 2022 19:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stokito/1b1b19c344d3614a3208dc81e44bfd97 to your computer and use it in GitHub Desktop.
Save stokito/1b1b19c344d3614a3208dc81e44bfd97 to your computer and use it in GitHub Desktop.
parse Prometheus metrics from JavaScript
class Metric {
/**
* @param {string} help
* @param {string} value
* @param {string[]} values
* @param {string[]} labels
*/
constructor(help, value, values, labels) {
this.help = help
this.value = value
this.values = values
this.labels = labels
}
}
/**
* @param {string} metricsText
* @returns {Map<string, Metric>}
*/
function parsePrometheusMetrics(metricsText) {
let metricsLines = metricsText.split("\n")
let metrics = new Map()
for (let metricsLinesKey in metricsLines) {
let metricsLine = metricsLines[metricsLinesKey]
if (metricsLine === "") {
continue
}
if (metricsLine.startsWith("# HELP ")) {
let metricNameEndPos = metricsLine.substr(7).indexOf(" ")
let metricsName = metricsLine.substr(7, metricNameEndPos)
let metricsHelp = metricsLine.substr(7 + metricNameEndPos + 1)
metrics[metricsName] = new Metric(metricsHelp, "", null, null)
} else if (metricsLine.startsWith("# TYPE ")) {
// not interesting
} else {
// name with labels
let metricNameFqEndPos = metricsLine.lastIndexOf(" ")
let metricsValue = metricsLine.substr(metricNameFqEndPos + 1)
// "go_threads" or "agw_req{SSP="ssp1",result="accepted"}"
let metricsNameFq = metricsLine.substr(0, metricNameFqEndPos)
let metricsName
let labelsStart = metricsNameFq.indexOf("{")
if (labelsStart < 0) {
metricsName = metricsNameFq
let metric = metrics[metricsName];
if (!metric) {
metric = new Metric(null, null, null, null)
metrics[metricsName] = metric
}
metric.value = metricsValue
} else {
metricsName = metricsNameFq.substr(0, labelsStart)
let metricsLabels = metricsNameFq.substr(labelsStart + 1, metricsNameFq.length - 2)
let metric = metrics[metricsName];
if (!metric) {
metric = new Metric(null, null, null, null)
metrics[metricsName] = metric
}
metric.labels ||= []
metric.labels.push(metricsLabels)
metric.values ||= []
metric.values.push(metricsValue)
}
}
}
return metrics
}
/**
* @param metrics {Map<string, Metric>}
* @param metricName {string}
* @param labelKey {string}
* @param labelVal {string}
* @return string
*/
function getMetricVal(metrics, metricName, labelKey, labelVal) {
let metric = metrics[metricName]
if (!metric || !metric.labels) return null
let needle = `"${labelKey}"="${labelVal}"`
for (let i = 0; i < metric.labels.length; i++) {
let labelStr = metric.labels[i]
if (labelStr.includes(needle)) {
return metric.values[i]
}
}
return null
}
/**
* @param metric {Metric}
* @param labelNeedles {string}
* @return {string}
*/
function getMetricValByLabel(metric, ...labelNeedles) {
for (let i = 0; i < metric.labels.length; i++) {
let labelStr = metric.labels[i]
if (labelsMatched(labelNeedles, labelStr)) {
return metric.values[i]
}
}
return null
}
/**
* @param metric {Metric}
* @param labelNeedles {string}
* @return {string[]}
*/
function filterMetricValByLabel(metric, ...labelNeedles) {
let matchedValues = []
for (let i = 0; i < metric.labels.length; i++) {
let labelStr = metric.labels[i]
if (labelsMatched(labelNeedles, labelStr)) {
matchedValues.push(metric.values[i])
}
}
return matchedValues
}
function labelsMatched(labelNeedles, labelStr) {
let allMatched = true
for (let j = 0; j < labelNeedles.length; j++) {
let labelNeedle = labelNeedles[j]
if (!labelStr.includes(labelNeedle)) {
allMatched = false
break
}
}
return allMatched
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment