122 lines
3.7 KiB
Awk
122 lines
3.7 KiB
Awk
# Library for generation of Prometheus metrics in GNU Awk
|
||
#
|
||
# Copyright (C) 2021 Mike Gerwitz
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
#
|
||
# This library is not written for my particular uses (and style) and is not
|
||
# comprehensive.
|
||
|
||
|
||
# Declare a counter of the name NAME with the help string HELP.
|
||
function prom_declare_counter(name, help) {
|
||
_prom_declare_metric(name, "counter", help)
|
||
}
|
||
|
||
|
||
# Declare a gauge of the name NAME with the help string HELP.
|
||
function prom_declare_gauge(name, help) {
|
||
_prom_declare_metric(name, "gauge", help)
|
||
}
|
||
|
||
|
||
# Declare a metric of the name NAME with the type TYPE and the help string
|
||
# HELP.
|
||
#
|
||
# Prometheus metric names (NAME) must follow the pattern
|
||
# `[a-zA-Z_:][a-zA-Z0-9_:]*`, but colons are reserved for user-defined
|
||
# metrics (e.g. rules), and should not be used by exporters (like us).
|
||
function _prom_declare_metric(name, type, help) {
|
||
# Omit colons (see docblock) when verifying name.
|
||
if (!(name ~ /^[a-zA_Z_][a-zA-Z0-9_]*/)) {
|
||
_prom_fatal("invalid metric name: " name)
|
||
}
|
||
|
||
PROM_TYPE[name] = type
|
||
PROM_HELP[name] = help
|
||
|
||
# Whether we've output this metric yet (so we can determine whether to
|
||
# output the HELP and TYPE lines)
|
||
PROM_OUT[name] = 0
|
||
}
|
||
|
||
|
||
# Output a metric NAME with the numeric value VALUE and labels specified by
|
||
# the associative array LABELS.
|
||
#
|
||
# The metric must have previously been declared to ensure that its `HELP`
|
||
# and `TYPE` lines will be properly output the first time this function is
|
||
# invoked for that NAME.
|
||
#
|
||
# The keys of LABELS ought to be hard-coded to ensure that they are both
|
||
# valid and bounded.
|
||
function prom_metric(name, value, labels) {
|
||
if (PROM_OUT[name] != 1) {
|
||
printf "# HELP %s %s\n", name, PROM_HELP[name]
|
||
printf "# TYPE %s %s\n", name, PROM_TYPE[name]
|
||
|
||
PROM_OUT[name] = 1
|
||
}
|
||
|
||
printf "%s{%s} %d\n", name, _prom_labels(labels), value
|
||
}
|
||
|
||
|
||
# Serialize and escape associative array of labels LABELS.
|
||
#
|
||
# Prometheus label names must be of the form `[a-zA-Z_][a-zA-Z0-9_]*`, with
|
||
# a leading `__` reserved for internal use (though this does not bother
|
||
# checking for leading underscores). Labels not matching that pattern will
|
||
# be rejected in error rather than reformatting, since it surely represents
|
||
# a bug and I don't need my database polluted with garbage values.
|
||
function _prom_labels(labels, _delim, _result) {
|
||
_delim=""
|
||
|
||
for (name in labels) {
|
||
_result = _result sprintf("%s%s=\"%s\"",
|
||
_delim,
|
||
_prom_assert_valid_label(name),
|
||
_prom_label_escape(labels[name]))
|
||
|
||
_delim=", "
|
||
}
|
||
|
||
return _result
|
||
}
|
||
|
||
|
||
# Exit with non-zero status and error message if label name NAME is not
|
||
# valid (see `_prom_labels`).
|
||
function _prom_assert_valid_label(name) {
|
||
if (!(name ~ /^[a-zA-Z_][a-zA-Z0-9_]*/)) {
|
||
_prom_fatal("invalid label name: " name)
|
||
}
|
||
|
||
return name
|
||
}
|
||
|
||
|
||
# Escape double quotes in label value VALUE and return the result.
|
||
function _prom_label_escape(value) {
|
||
return gensub(/"/, "\\\\\"", "g", value)
|
||
}
|
||
|
||
|
||
# Display error message MSG and exit with non-zero status STATUS
|
||
# (default 1).
|
||
function _prom_fatal(msg, status) {
|
||
printf "error: prom: %s\n", msg > "/dev/stderr"
|
||
exit status ? status : 1
|
||
}
|