Zabbix使用JavaScript配置webhook发送告警通知

背景

从zabbix4.4开始,zabbix支持使用自定义的JavaScript代码来配置webhook媒介类型来实现故障报警通知,这又为用户提供了一种使用前端代码来进行报警通知的方式,相比于在后端配置告警脚本的好处是代码可维护性更强,支持媒介类型的导入导出,省去了后端调试代码的步骤,使zabbix更加的开箱即用。

实现原理

首先看一下官方文档有详细介绍https://www.zabbix.com/documentation/5.0/zh/manual/config/notifications/media/webhook,我只是一个搬运工,zabbix官方提供了大量的webhook媒介类型,虽然很多我们都用不上,但是我们可以参考他们的代码实现,来照葫芦画瓢编写适合我们自己的webhook媒介类型,之前我本人也看了一点点JavaScript的基础,但后来看官方编写的代码和文档的时候,发现其实并不需要有多好的JavaScript基础,有些代码官方又直接封装了一下,可以拿过来直接用。
实现原理简单来说就是通过传入参数到JavaScript代码中,JavaScript会构造http请求并将传入的参数作为请求的一部分发送出去。

zabbix封装的JavaScript对象

zabbix官方为方便我们使用JavaScript的预处理和使用webhook的媒介类型,特意在上面封装了一些对象给我们使用,同样目的也是为了能够更好的开箱即用。主要封装了两个对象。

写入zabbix日志的对象

方法 描述
Log(loglevel, message) 按照 日志级别将 写入 Zabbix 日志,日志级别可参考配置文件

例如:

Zabbix.Log(3, "this is a log entry written with 'Warning' log level")

http请求对象

官方还对http请求进行了封装,可以很方便的构造http请求

方法 描述
AddHeader(name, value) 添加HTTP报头字段。此字段用于所有后续请求,直到使用ClearHeader()方法清除为止
ClearHeader() 清除 HTTP 标头。如果没有设置报头字段,且发送的数据是 json 格式,默认会将 Content-Type 设置为 application/json,否则设置为 text/plain。
GetHeaders() 返回接收到的 HTTP 标头字段的对象。
Get(url, data) 将HTTP GET请求发送到带有可选_data_的URL,并返回响应
Put(url, data) 将HTTP PUT请求发送到带有可选data的URL,并返回响应
Post(url, data) 将HTTP POST请求发送到带有可选_data_的URL,并返回响应
Delete(url, data) 将HTTP DELETE请求发送到带有可选data的URL,并返回响应
Status() 返回最后一个HTTP请求的状态码
SetProxy(proxy) 设置HTTP代理为“proxy”值。如果该参数为空,则不使用代理

官方在这里提供了一个例子来介绍如何使用封装的对象

try {
    Zabbix.Log(4, 'jira webhook script value='+value);

    var result = {
        'tags': {
            'endpoint': 'jira'
        }
    },
    params = JSON.parse(value),
    req = new CurlHttpRequest(),
    fields = {},
    resp;

    req.AddHeader('Content-Type: application/json');
    req.AddHeader('Authorization: Basic '+params.authentication);

    fields.summary = params.summary;
    fields.description = params.description;
    fields.project = {"key": params.project_key};
    fields.issuetype = {"id": params.issue_id};
    resp = req.Post('https://tsupport.zabbix.lan/rest/api/2/issue/',
        JSON.stringify({"fields": fields})
    );

    if (req.Status() != 201) {
        throw 'Response code: '+req.Status();
    }

    resp = JSON.parse(resp);
    result.tags.issue_id = resp.id;
    result.tags.issue_key = resp.key;
} catch (error) {
    Zabbix.Log(4, 'jira issue creation failed json : '+JSON.stringify({"fields": fields}));
    Zabbix.Log(4, 'jira issue creation failed : '+error);

    result = {};
}

return JSON.stringify(result);

配置webhook

如果我们想用官方现有的webhook媒介类型,则可以从git仓库或者源码包中下载并导入现有的xml,如果官方的webhook不能满足我们的需求则可以自己手动创建一个。
webhook媒体类型的具体参数
前文说过,webhook的原理就是通过传递参数,然后对参数进行校验之后构建http请求并发送,webhook默认包含几个常用变量(URL :, HTTPProxy:, To:{ALERT.SENDTO}, Subject:{ALERT.SUBJECT}, Message:{ALERT.MESSAGE}),传入的参数也支持问题通知中支持的所有宏,也支持使用http代理。
选中Process tags_,_会讲webhook返回的json属性值作为问题事件标签。
选中事件菜单复选框,可以在事件发生时点击问题名称访问外部的地址。

Webhook开发指南

官方的webhook开发指南说明https://www.zabbix.com/documentation/guidelines/webhooks

数据验证

整个数据验证过程可以分为两部分:输入数据验证和外部系统响应数据验证。
输入数据验证包括

  • Webhook 配置或 Webhook 测试中使用的值无效。

  • 缺少网络钩子参数。
    webhook 代码应该验证所需的参数并确定必需的参数是否存在。

  • 宏是否被解析。

输入数据验证的所有错误消息都应包含有关问题原因的信息和解决方法

响应数据验证包括
与用户输入验证一样,响应数据验证应确保来自外部系统的响应采用预期格式。
这包括以下验证:

  • 是否有响应并且没有 HTTP 错误。

  • 响应是否包含预期格式的数据(原始/JSON/XML/等)。

  • 响应是否包含所有必需的字段或数据。

  • 响应数据中有没有错误。

返回值

虽然没有要求返回特定值,但在定义 webhook 的响应时应使用以下方法之一:
如果 webhook 不使用标记:建议返回一个通用字符串(例如,OK)来表示执行成功。
如果 webhook 使用标签(标记Process tags复选框):webhook 应始终返回一个 JSON 对象,其中至少包含一个空对象的标签:{tags: {}}

日志记录

Webhooks 应该使用 Zabbix 提供的日志记录功能来为用户存储调试信息。应满足以下日志记录要求:

  • 应提供调试级别信息。 这可用于找出 webhook 逻辑中错误的原因。
  • 不需要在更高级别创建日志条目,因为 Zabbix 服务器会自动在“警告”(3)级别记录失败的 webhooks。  
  • 日志条目应以 WEBHOOK 名称为前缀。这样是为了区分 webhook 创建的日志与和Zabbix Server 日志文件中的其他日志。  
  • 无需在 WEBHOOK 的每一步都创建日志条目。

编写自定义webhook媒介类型实现发送钉钉报警通知

之前的文章已经介绍过编写脚本实现飞书的报警通知https://mp.weixin.qq.com/s/fV4R2jKy9Q35MvK0AkKsKg,这次将以webhook的媒介类型发送钉钉报警为例为大家介绍编写自定义的webhook媒介。

创建钉钉机器人

首先在电脑端创建自定义机器人,安全设置使用自定义关键词
image.png
,完成设置,保存好webhook,https://oapi.dingtalk.com/robot/send?access_token=e5906a9b879fe615323bd9489f46334f5539d6a46f8e29c5740ba02c7ee90e84
image.png
image.png

创建自定义报警媒介

进入报警媒介类型,选择webhook媒介类型,参数只需要填告警信息,接收人,webhook地址就可以了,脚本为

var Dingding = {
    params: {},
    proxy: null,
    setParams: function (params) {
        if (typeof params !== 'object') {
            return;
        }
        Dingding.params = params; 
    },
    request: function () {
        var data = {
          msgtype: "markdown",
          markdown: {
            title: "报警",
            text: "## 通知:\n " + Dingding.params.Message,
          },
          at: {
            atUserIds: [Dingding.params.To],
            isAtAll: false,
          },
        },
        response,
        url = Dingding.params.URL,
        request = new HttpRequest();

        request.addHeader('Content-Type: application/json');
        if (typeof Dingding.HTTPProxy !== 'undefined' && Dingding.HTTPProxy !== '') {
            request.setProxy(Dingding.HTTPProxy);
        }

        if (typeof data !== 'undefined') {
            data = JSON.stringify(data);
        }
        Zabbix.Log(4, "[Dingding Webhook] message is: " + data);
        response = request.post(url, data);

        Zabbix.log(4, '[ Dingding Webhook ] Received response with status code ' +
            request.getStatus() + '\n' + response);

        if (response !== null) {
            try {
                response = JSON.parse(response);
            }
            catch (error) {
                Zabbix.log(4, '[ Dingding Webhook ] Failed to parse response received from Dingding');
                response = null;
            }
        }

        if (request.getStatus() !== 200 || response.errcode !== 0) {
            var message = 'Request failed with status code '+request.getStatus();

            if (response !== null && typeof response.errmsg !== 'undefined') {
                message += ': '+ JSON.stringify(response.errmsg) ;
            }

            throw message + '. Check debug log for more information.';
        }

        return response;
    },
};

try {
    var params = JSON.parse(value);

    if (typeof params.URL !== 'undefined' 
         && typeof params.To !== 'undefined'
         && typeof params.Message !== 'undefined') {
           Zabbix.log(4, '[ Dingding Webhook ] webhookURL "' + params.URL +
                            '" sendto "'+ params.To )+'"';
        } 
        else {
          throw 'Missing parameter. URL, message, to parameter is required'
        }
    if (params.HTTPProxy) {
        Dingding.proxy = params.HTTPProxy;
    } 
    Dingding.setParams(params);
    Dingding.request();
    return 'OK';
}catch (error) {
    Zabbix.log(3, '[ Dingding Webhook ] ERROR: ' + error);
    throw 'Sending failed: ' + error;
}

image.png
之后点击媒介类型后面的测试,输入参数就可以进行验证编写的自定义报警类型是否有问题

image.png
告警消息是支持markdown的,可以将消息模板改成Markdown格式,看起来会更好看一点
image.png

------本页内容已结束,喜欢请分享------

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发
wanger的头像-运维开发故事

昵称

取消
昵称表情代码图片