1
0
Fork 0

prom.awk: New basic library

This introduces the beginning of a very basic library for generating
Prometheus metrics with GNU Awk.  It is not intended to be complete, and
it'll evolve as I need it to.

At the moment, there are no tests, because manual verification is
trivial and I'm not yet sure how far I'll be going with this.  If it
evolves much further, though, I'll be adding a test suite.
master
Mike Gerwitz 2021-03-01 21:56:30 -05:00
parent a3e4bc22c6
commit 9c7961bb5c
Signed by: mikegerwitz
GPG Key ID: 8C917B7F5DC51BA2
3 changed files with 141 additions and 14 deletions

View File

@ -30,25 +30,28 @@
# container is 50px, so we take the height of the image and simply double it
# to get the ink level of that tank.
@include "../../../prom.awk"
BEGIN {
FS = "[ _.']"
count = 0
expected_count = 4
print "# HELP printer_ink_level Percentage of each ink tank remaining."
print "# TYPE printer_ink_level gauge"
prom_declare_gauge("printer_ink_level",
"Percentage of each ink tank remaining.")
prom_declare_gauge("printer_ink_level_success",
"Whether scraping was successful.")
}
# ex: <img class='color' src='../../IMAGE/Ink_K.PNG' height='23' style=''>
/IMAGE\/Ink_.\.PNG/ {
printf "printer_ink_level{color=\"%s\"} %d\n", $11, ($15 * 2)
L["color"] = $11
prom_metric("printer_ink_level", ($15 * 2), L)
count++
}
END {
print "# HELP printer_ink_level_success Whether scraping was successful."
print "# TYPE printer_ink_level_success gauge"
printf "printer_ink_level_success{} %d\n", (count == expected_count)
prom_metric("printer_ink_level_success", (count == expected_count))
}

View File

@ -25,6 +25,8 @@
# Consequently, you may find that this script does not work for your
# printer, even if it is the same model.
@include "../../../prom.awk"
# Rather than parsing start and end tags, we'll set the field separator to
# any opening angled bracket and the record separator to any closing. This
# leaves us with the node names, attributes, and cdata.
@ -35,8 +37,10 @@ BEGIN {
count = 0
expected_count = 8
print "# HELP printer_pages_count Number of pages."
print "# TYPE printer_pages_count counter"
prom_declare_counter("printer_pages_count",
"Number of pages.");
prom_declare_gauge("printer_pages_count_success",
"Whether scraping was successful.")
}
# Simplify matching.
@ -73,14 +77,13 @@ BEGIN {
# Assume that the next line beginning with a number is the value.
while (!($1 ~ /^[0-9]/)) getline
printf "printer_pages_count{color=\"%s\", source=\"%s\"} %d\n", \
color, source, $1
L["color"] = color
L["source"] = source
prom_metric("printer_pages_count", $1, L)
count++
}
END {
print "# HELP printer_pages_count_success Whether scraping was successful."
print "# TYPE printer_pages_count_success gauge"
printf "printer_pages_count_success{} %d\n", (count == expected_count)
prom_metric("printer_pages_count_success", (count == expected_count))
}

121
prom.awk 100644
View File

@ -0,0 +1,121 @@
# 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
}