Parsing structured JSON in Zabbix is easy with JSONPath—until you need more advanced filtering or conditional logic. That’s where JavaScript preprocessing comes in.
This post is likely pretty niche. I drafted it after needing to go beyond a simple value lookup inside a JSON block to something more involved, namely evaluating multiple conditions to arrive at a final true/false value.
Zabbix is a monitoring tool where you typically have a set of items (values to monitor) and then triggers (thresholds/conditions beyond which alerts should kick in).
Zabbix has built in support for structured data like JSON. The whole JSON value is stored in a text item, which can then have derived items associated with it. You can use JSONPath to extract inner values.
Say we have a JSON payload coming from an API:
{
"status": "DOWN",
"services": {
"ServiceA": {"status": "down", "endpoints": ["10.0.0.1", "10.0.0.2"]},
"ServiceB": {"status": "up", "endpoints": ["80.0.0.123"]},
"ServiceC": {"status": "down", "endpoints": ["10.0.0.4", "10.0.0.5"]}
}
}
We can have an item derived from this that uses JSONPath to reach in to the
top-level status and store the result as text. We can then trigger if the
status != "UP"
.
What if we need to fine tune this to consider the underlying service? Or you need to filter out certain values before alerting. Recently, I needed to parse a JSON payload, but some specific values within an array needed to be ignored.
Changing from JSONPath to JavaScript Preprocessing
Let’s go back to our example above. We want to trigger alerts when status is down, but ignore failures where the endpoint contains one of a predefined dummy list (e.g., 10.0.0.2 and 10.0.0.5).
Zabbix lets us use JavaScript Preprocessing to create a function that takes the full text as
input and create a new output (typically simplified to a number or small string). In the GUI you’re dropped into an editor
window which is effectively the inside of a function(value) { ... }
block. My first
attempt looked something like this:
var ignored_endpoints = ["10.0.0.2", "10.0.0.5"];
var services = JSON.parse(value).services;
var failures = {};
for (var key in services) {
var entry = services[key];
if (entry.status === "down") {
if (!entry.endpoints.some(e => ignored_endpoints.includes(e))) {
failures[key] = entry;
}
}
}
return JSON.stringify(failures);
Unfortunately, this threw a TypeError: undefined not callable (property
'includes' of [object Array])
error in my version of Zabbix.
Zabbix uses the Duktape JS engine, and is
not necessarily super up-to-date.
This was easy enough to work around, I replaced .includes()
with indexOf()
:
var ignored_endpoints = ["10.0.0.2", "10.0.0.5"];
var services = JSON.parse(value).services;
var failures = {};
for (var key in services) {
var entry = services[key];
if (entry.status === "down") {
var should_ignore = false;
for (var i = 0; i < entry.endpoints.length; i++) {
if (ignored_endpoints.indexOf(entry.endpoints[i]) !== -1) {
should_ignore = true;
break;
}
}
if (!should_ignore) {
failures[key] = entry;
}
}
}
return JSON.stringify(failures);
This worked as expected, filtering out ignored endpoints without breaking preprocessing.
Bonus Tip: Sharing Ignored Values Across Multiple Items
One final tip here. Instead of hardcoding ignored values, you can store them as a Zabbix macro (e.g.
{$IGNORED_ENDPOINTS}
) and split them dynamically:
var ignored_endpoints = "{$IGNORED_ENDPOINTS}".split(",");
Then, define the macro in Administration → General → Macros with a value like:
10.0.0.2,10.0.0.5
Now, if the list of ignored values changes, you only need to update the macro rather than modifying potentially multiple items.
Handy To Know
Whilst I am not sure I will end up Javascript Preprocessing this super regularly, it’s useful to file away in the zabbix-tips-and-tricks-toolbox. Definitely worth trying to keep the JS simple mind, not only is it executed potentially a very large number of times, you might be stuck with a JS engine that isn’t particularly up-to-date.