Javascript support in item preprocessing

With new preprocessing steps added to Zabbix in nearly every release, it became obvious that it’s impossible to predict every business case, so we had to come up with a universal solution. This, in turn, raised an important question – which embedded scripting language/engine to use.

First, we decided on the following evaluation criteria:

  • Engine integration. How easy the engine can be integrated, which libraries are required and what’s their availability on Zabbix-supported platforms. The engine must also support Windows builds.
  • Resource usage. Invocation/callback performance is the top priority here, with memory usage and execution speed being secondary. The common use of the script would be to execute small bits of code when calling from C program. This basically requires support of script precompilation and bytecode caching.
  • Safety. By default potentially dangerous functionality like file/socket access must be disabled. The engine must support timeouts and memory limits to avoid the script being stuck or allocating all system memory.

A number of test cases were created to measure performance and evaluate scripting engines according to the described criteria:

  • Fahrenheit to Celsius conversion. Test simple mathematical formula.
  • Word count. Count the number of occurrences of a specified word in input data.
  • JSON parsing. Count the number of objects matching a specified tag/value.
  • Parse apache status page and extract the required metrics in JSON format.
  • Obtaining data from Zabbix. Obtain the specified number of history values from Zabbix and calculate sum.

The following languages/engines were evaluated:

  • Lua – Lua 5.1 (the older version was chosen because of its availability on older platforms)
  • Lua – LuaJIT
  • Javascript – Duktape
  • Javascript – JerryScript
  • Embedded Python
  • Embedded Perl

On a side note, we also tried a few not quite successful tests with Chrome V8 and SpiderMonkey – too heavy for short script execution.

And the results were as follows:
Features:

Lua Javascript (ECMAScript) Python Perl
Lua 5.1 LuaJIT Duktape Jerryscript CPython 2.7 Perl 5.x
Language features Might need third party libraries (json), no native regular expression support (lua patterns are similar, but less powerful). Might need third party libraries (json), no native regular expression support (lua patterns are similar, but less powerful). No out of the box support for file access, network functionality.

Additonal features must be provided with third party or custom modules.

No out of the box support for file access, network functionality.

Additonal features must be provided with third party or custom modules.

Wide collection of various modules. Wide collection of various modules.
Language feature limitation It’s possible to disable ‘dangerous’ modules and functions, but might still be accessible if engine internals are known. It’s possible to disable ‘dangerous’ modules and functions, but might still be accessible if engine internals are known. No file/network access is available by default. No file/network access is available by default. Disabling modules is possible by overriding the default import function, however, it would still be possible to import those modules by using a low level python howto. Disabling modules is possible by overriding the default import function, however, it would still be possible to import those modules by using a low level python howto.
Engine integration Link to lua and a few common libraries. Link to lua and a few common libraries. Distributed as single C and two header files to be included in project. Must compile jerryscript libraries from sources. Link to python and a few common libraries. Link to perl and a few common libraries.
Resource limits Can limit memory usage with custom allocator and CPU usage by limiting CPU cycles. Can limit CPU usage by limiting CPU cycles. Custom memory allocator is not supported. Can limit memory usage with custom allocator and CPU usage by setting script timeout. JerryScript does not provide means to limit memory usage, but it seems to have default 512KB heap limit. Execution timeouts are not supported yet. The memory and CPU usage can be limited with resources module. However that seems to be *nix specific, based on process ulimits. Only OS based process resource limits can be applied.
Script precompilation and loading Supports bytecode precompilation and loading. Supports bytecode precompilation and loading. Supports bytecode precompilation and loading. Supports bytecode precompilation and loading. Supports function marshaling/unmarshaling. No bytecode precompilation/ loading is supported.
Notes Crashed when used an insufficient sized buffer when compiling the script. Indent based code structure, hard to write more complex code in just text area. Would need more advanced editor.
Simple script performance
Complex script performance
Engine initialization performance
Script compilation performance

Performance (precompiled code):

The main focus was on precompiled bytecode execution performance, however, on preprocessing changes the scripts will be recompiled and on errors the javascript engine might get reinitialized – so this also should be taken in account.

When looking at the overall performance, Lua (especially LuaJIT) edged ahead. Python and Perl had strong string manipulation performance, but the script compilation and engine initialization had the worst performance. Also, the cpu/memory resource limitation was supported only on OS level. Duktape had an average overall performance, but the worst simple script performance. Still, with the worst performance, it managed to process 400k expressions per second – that should be more than enough for a single worker.

So that left Lua or Javascript. After looking beyond performance Javascript won simply because of language popularity. Lua is a niche language, with its own syntax peculiarities. Lua patterns, while being somewhat similar, are still different from regular expressions. Moreover, native JSON support is preferable, though it would be possible to precompile and preload third party JSON module. And finally, it was hard to beat Duktape integration simplicity.

Example

As an example let’s show how the Fahrenheit to Celsius conversion can be configured with JavaScript preprocessing step. In item’s preprocessing configuration add new step and select Custom scripts/JavaScript:

The first line of the script is displayed in parameters (which currently has the placeholder “script”):

Clicking on it will open a simple editor window:

Currently, the editor is a simple multi-line editor using monospace fonts. Tabulation and syntax coloring are not supported.

Enter the conversion formula

return (value - 32) * 5 / 9

and press apply:

For complex scripts that take more than single line it’s recommended to write script description in the first line commentary like:

Then the script will be self-explanatory in preprocessing steps:

While this example is quite a trivial, Javascript preprocessing can be used in complex things like analyzing input data, converting text data to other formats (JSON), etc. Basically, whenever Zabbix standard preprocessing options fall short, there is a good chance it can be done with Javascript preprocessing.

Author: Andris Zeila

Developer at Zabbix.

Leave a Reply