5 JavaScript 预处理

概述

本节提供使用 JavaScript 进行预处理的详细信息。

请勿在预处理 JavaScript 中使用未声明的赋值。 请使用 var 声明局部变量。

JavaScript 预处理

JavaScript 预处理是通过调用带有单个参数“value”和用户提供的函数体的 JavaScript 函数来完成的。
该预处理步骤的结果是此函数返回的值。例如,要执行华氏度到摄氏度的转换,请在 JavaScript 预处理参数中输入:

return (value - 32)  * 5 / 9

服务器会将其包装为一个 JavaScript 函数:

function (value)
{
   return (value - 32) * 5 / 9
}

输入参数“value”始终以字符串形式传递。
返回值会通过 toString() 方法自动强制转换为字符串(如果失败,则错误会作为字符串值返回),但有少数例外:

  • 返回 undefined 值将导致错误;
  • 返回 null 值将导致输入值被丢弃,这与“Custom on fail”操作中的“Discard value”预处理类似。

错误可以通过抛出值/对象来返回(通常是字符串或 Error 对象)。

例如:

if (value == 0)
    throw "Zero input value"
return 1/value

每个脚本都有 10 秒的执行超时时间(取决于脚本,触发超时可能需要更长时间);超过该时间将返回错误。
强制实施 512 MB 的堆内存限制。

JavaScript 预处理步骤的字节码会被缓存,并在下次应用该步骤时复用。
对监控项预处理步骤的任何更改都会导致缓存的脚本被重置,并在之后重新编译。

连续运行失败(连续 3 次)将导致引擎被重新初始化,以降低某个脚本破坏后续脚本执行环境的可能性(此操作会在 DebugLevel 4 及更高级别时记录日志)。

JavaScript 预处理使用 Duktape JavaScript 引擎实现。

另请参见:附加的 JavaScript 对象和全局函数

在脚本中使用宏

可以在 JavaScript 代码中使用用户宏(以及在低级别发现上下文中使用的 LLD macros)。 如果脚本包含用户宏,这些宏会在执行特定预处理步骤之前由服务器/proxy 解析。 请注意,在前端中测试预处理步骤时,不会拉取宏值,需要手动输入。

当宏被替换为其值时,上下文会被忽略。 宏值会按原样插入到代码中,无法在将该值放入 JavaScript 代码之前额外添加转义。 请注意,这在某些情况下可能会导致 JavaScript 错误。

在下面的示例中,如果接收到的值超过 {$THRESHOLD} 宏值,则将返回阈值(如果存在)而不是接收到的值:

var threshold = '{$THRESHOLD}';
return (!isNaN(threshold) && value > threshold) ? threshold : value;

示例

以下示例说明了如何使用JavaScript预处理。

每个示例包含一个简要描述、JavaScript预处理参数的函数体以及预处理步骤的结果 - 函数返回的值。

示例 1:将数字(科学计数法转换为整数)

将数字 "2.62128e+07" 从科学计数法转换为整数。

return (Number(value))

函数返回的值:26212800。

示例2:将数字转换为十进制(二进制转十进制)

将二进制数"11010010"转换为十进制数。

return(parseInt(value,2))

函数返回的值:210。

示例3:四舍五入

将数字"18.2345"四舍五入为2位小数。

return(Math.round(value* 100) / 100)

函数返回的值:18.23。

示例4:计算字符串中的字母数

计算字符串"Zabbix"中的字母数。

return (value.length)

函数返回的值:6。

示例5:获取剩余时间

获取距离证书到期日期(Feb 12 12:33:56 2022 GMT)的剩余时间(以秒为单位)。

var split = value.split(' '),
    MONTHS_LIST = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    month_index = ('0' + (MONTHS_LIST.indexOf(split[0]) + 1)).slice(-2),
    ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],
    now = Date.now();

return parseInt((Date.parse(ISOdate) - now) / 1000);

函数返回的值:44380233。

示例6:移除JSON属性

通过删除具有键为"data_size""index_size"的属性来修改JSON数据结构。

var obj=JSON.parse(value);

for (i = 0; i < Object.keys(obj).length; i++) {
    delete obj[i]["data_size"];
    delete obj[i]["index_size"];
}

return JSON.stringify(obj)

函数接受的值:

[
    {
        "table_name":"history",
        "data_size":"326.05",
        "index_size":"174.34"
    },
    {
        "table_name":"history_log",
        "data_size":"6.02",
        "index_size":"3.45"
    }
]

函数返回的值:

[
    {
        "table_name":"history"
    },
    {
        "table_name":"history_log"
    }
]
示例 7:将 Apache 状态转换为 JSON

将从 web.page.get Zabbix agent 监控项接收到的值(例如,web.page.get[http://127.0.0.1:80/server-status?auto])转换为 JSON 对象。

// 将 Apache 状态转换为 JSON

// 将值拆分为子字符串,并将这些子字符串放入数组中
var lines = value.split('\n');

// 创建一个空对象 "output"
var output = {};

// 创建一个具有预定义属性的对象 "workers"
var workers = {
    '_': 0, 'S': 0, 'R': 0, 'W': 0,
    'K': 0, 'D': 0, 'C': 0, 'L': 0,
    'G': 0, 'I': 0, '.': 0
};

// 将 "lines" 数组中的子字符串作为属性(键值对)添加到 "output" 对象中
for (var i = 0; i < lines.length; i++) {
    var line = lines[i].match(/([A-z0-9 ]+): (.*)/);

    if (line !== null) {
        output[line[1]] = isNaN(line[2]) ? line[2] : Number(line[2]);
    }
}

// 多版本指标
output.ServerUptimeSeconds = output.ServerUptimeSeconds || output.Uptime;
output.ServerVersion = output.ServerVersion || output.Server;

// 解析 "Scoreboard" 属性以获取 worker 计数
if (typeof output.Scoreboard === 'string') {
    for (var i = 0; i < output.Scoreboard.length; i++) {
        var char = output.Scoreboard[i];

        workers[char]++;
    }
}

// 将 worker 数据添加到 "output" 对象中
output.Workers = {
    waiting: workers['_'], starting: workers['S'], reading: workers['R'],
    sending: workers['W'], keepalive: workers['K'], dnslookup: workers['D'],
    closing: workers['C'], logging: workers['L'], finishing: workers['G'],
    cleanup: workers['I'], slot: workers['.']
};

// 返回 JSON 字符串
return JSON.stringify(output);

函数接受的值:

HTTP/1.1 200 OK
Date: Mon, 27 Mar 2023 11:08:39 GMT
Server: Apache/2.4.52 (Ubuntu)
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 405
Content-Type: text/plain; charset=ISO-8859-1

127.0.0.1
ServerVersion: Apache/2.4.52 (Ubuntu)
ServerMPM: prefork
Server Built: 2023-03-08T17:32:01
CurrentTime: Monday, 27-Mar-2023 14:08:39 EEST
RestartTime: Monday, 27-Mar-2023 12:19:59 EEST
ParentServerConfigGeneration: 1
ParentServerMPMGeneration: 0
ServerUptimeSeconds: 6520
ServerUptime: 1 hour 48 minutes 40 seconds
Load1: 0.56
Load5: 0.33
Load15: 0.28
Total Accesses: 2476
Total kBytes: 8370
Total Duration: 52718
CPUUser: 8.16
CPUSystem: 3.44
CPUChildrenUser: 0
CPUChildrenSystem: 0
CPULoad: .177914
Uptime: 6520
ReqPerSec: .379755
BytesPerSec: 3461.58
BytesPerReq: 3461.58
DurationPerReq: 21.2916
BusyWorkers: 2
IdleWorkers: 6
Scoreboard: ____KW__..............................................................................................................................................

函数返回的值:

{
    "Date": "Mon, 27 Mar 2023 11:08:39 GMT",
    "Server": "Apache/2.4.52 (Ubuntu)",
    "Vary": "Accept-Encoding",
    "Encoding": "gzip",
    "Length": 405,
    "Type": "text/plain; charset=ISO-8859-1",
    "ServerVersion": "Apache/2.4.52 (Ubuntu)",
    "ServerMPM": "prefork",
    "Server Built": "2023-03-08T17:32:01",
    "CurrentTime": "Monday, 27-Mar-2023 14:08:39 EEST",
    "RestartTime": "Monday, 27-Mar-2023 12:19:59 EEST",
    "ParentServerConfigGeneration": 1,
    "ParentServerMPMGeneration": 0,
    "ServerUptimeSeconds": 6520,
    "ServerUptime": "1 hour 48 minutes 40 seconds",
    "Load1": 0.56,
    "Load5": 0.33,
    "Load15": 0.28,
    "Total Accesses": 2476,
    "Total kBytes": 8370,
    "Total Duration": 52718,
    "CPUUser": 8.16,
    "CPUSystem": 3.44,
    "CPUChildrenUser": 0,
    "CPUChildrenSystem": 0,
    "CPULoad": 0.177914,
    "Uptime": 6520,
    "ReqPerSec": 0.379755,
    "BytesPerSec": 1314.55,
    "BytesPerReq": 3461.58,
    "DurationPerReq": 21.2916,
    "BusyWorkers": 2,
    "IdleWorkers": 6,
    "Scoreboard": "____KW__..............................................................................................................................................",
    "Workers": {
        "waiting": 6,
        "starting": 0,
        "reading": 0,
        "sending": 1,
        "keepalive": 1,
        "dnslookup": 0,
        "closing": 0,
        "logging": 0,
        "finishing": 0,
        "cleanup": 0,
        "slot": 142
    }
}