Что такого в числе 65 535 и как оно относится к Zabbix webhook
Почему длинное оповещение из Zabbix приходит битым JSON или с половиной текста при статусе Sent.
Длинный лог в оповещении Zabbix может сломать JSON и дать Failed с SyntaxError, однако есть проблема и похуже: в Action log написано Sent, а получатель видит только часть текста. Оба кейса связаны с пределом 65 535, о котором Zabbix не предупреждает ни в интерфейсе, ни в журнале сервера.
TL;DR: Речь про ограничение размера сообщения. Если лень читать, то сразу смотрите чек-лист (семь коротких правил и готовый скрипт).
Как проверяли
Предел воспроизвёлся на Zabbix 7.0 с PostgreSQL и MySQL, а также на 6.0 с PostgreSQL. Эксперимент поставили следующим образом: отправляли в элемент данных латинский и русский текст длиной около границы. Одно событие попадало в несколько типов оповещения с разными параметрами. Длину сверяли в сохранённом значении Zabbix, на входе скрипта и у тестового приёмника.
Предел общий для всех параметров
Zabbix собирает параметры типа оповещения в один JSON и передаёт скрипту строкой value. Обратите внимание, что в предел 65 535 входит вся строка вместе с именами параметров, кавычками и запятыми. Если два параметра содержат один {ITEM.VALUE}, текст занимает место дважды. Лимит расходует даже перевод строки (еще бы, это ведь по сути тоже символ), при том в двойном размере, потому что в JSON он превращается в \n.
Нюанс в том, что PostgreSQL считает символы, а MySQL байты. При этом для латиницы результат одинаковый. С кириллицей ситуация отличается: буква в UTF-8 занимает два байта и вот тут исход уже зависит от конкретной СУБД. Таблица показывает, когда оповещение получит Failed, а когда сохранит Sent при потерянном тексте.
| Что в оповещении | PostgreSQL | MySQL |
|---|---|---|
| латиница длиннее 65 500 символов | Failed, JSON обрезан | Failed, JSON обрезан |
| кириллица длиннее 32 700 символов, один параметр | Sent, но хвост потерян | Failed, JSON обрезан |
| тот же текст в двух параметрах | Failed, JSON обрезан | Failed, JSON обрезан |
| кириллица до 30 000 символов, один параметр | Sent, текст целиком | Sent, текст целиком |
Кириллический текст до 30 000 символов с одним параметром дошёл целиком на обеих базах. Остальные случаи проявились двумя разными симптомами.
Failed с SyntaxError означает обрезанный JSON
Обрезанная строка value вызывает исключение в JSON.parse, после чего оповещение получает Failed. Скрипт из чек-листа добавляет длину строки в ошибку. Первый пример получен на PostgreSQL, второй на MySQL с русским текстом.
len=65535 показывает, что строка оборвалась на границе. На MySQL с кириллицей получаем длину в символах примерно вдвое меньше, но смещение ошибки достигает тех же 65 535 в любом случае.
На MySQL русский текст проходит через две границы. Zabbix сначала укорачивает значение элемента данных до 65 535 байт, а затем имена параметров и синтаксис JSON добавляют ещё несколько байт, из-за чего JSON обрывается даже с одним параметром.
Если ваш скрипт пересылает value без разбора, получаем повреждённый JSON и он может уйти получателю и при этом получить статус Sent! Собственно по этой причине защитный скрипт начинает работу с JSON.parse(value). Ошибка видна уже в Action log.
Sent не гарантирует полный текст
На PostgreSQL целый JSON и статус Sent могут скрыть потерю части русского текста (а вы вообще отправляете текст алертов на русском?). Значения {ITEM.VALUE}, {ITEM.LASTVALUE} и {ALERT.MESSAGE} (если шаблон сообщения содержит {ITEM.VALUE}) доходят до скрипта не длиннее 65 535 байт. Латинский текст достигает общего предела и ломает JSON. У кириллического текста может исчезнуть хвост при целой JSON-строке.
На стенде Zabbix сохранил в истории элемента данных почти 65 000 русских символов целиком, но через каждый из трёх макросов скрипт получил примерно половину (что как бы ожидаемо ввиду размера символа кириллицы, мы упоминали это выше). Журнал сервера с уровнем по умолчанию не сообщил об обрезке. На снимке два оповещения одного события из Action log. У верхнего текст оборван на полуслове, хотя статус Sent и поле ошибки пустое. У нижнего с двумя параметрами обрезался JSON, и скрипт поставил Failed с len=65535.

Совет: ограничьте длинный текст до 30 000 символов ещё до Zabbix.
Чек-лист и скрипт с защитой от обрезки
- Передавайте большой текст одним параметром. Если шаблон сообщения содержит
{ITEM.VALUE}, используйтеmessage={ALERT.MESSAGE}. Дополнительныйvalue={ITEM.VALUE}потратит запас повторно. - Считайте запас с экранированием. Каждый перевод строки стоит два символа, поэтому 30 000 строк по одному символу в 65 535 уже не влезают.
- Ограничьте текст до отправки в Zabbix 30 000 символами, если в нём бывает кириллица. Например на Python это можно сделать через
value = value[:30000]. - Разбирайте
valueпервой строкой и кладите длину в текст исключения, чтобы Failed показал причину падения: - Помечайте текст, упёршийся в 65 535 байт, вместо исключения, например
message += '\n[message cut by Zabbix]';приutf8Bytes(message) >= 65532. - Проверяйте JSON у получателя, если скрипт пересылает
valueкак есть. На невалидное тело получатель отвечает400, а скрипт выбрасывает исключение на любой ответ не 2xx, как в первой части серии. - Пишите подозрение на обрезку в журнал сами, сервер о ней молчит:
Zabbix.log(3, 'webhook: message may be cut by Zabbix, bytes=' + bytes);.
Скрипт выполняет пункты 4, 5 и 7, а также рекомендации из чек-листа первой части. Он сначала разбирает value, затем считает байты в message перед отправкой. Используйте те же параметры типа оповещения: url=<адрес получателя>, event_id={EVENT.ID}, event_value={EVENT.VALUE}, alert_date={DATE}, alert_time={TIME}, subject={ALERT.SUBJECT}, message={ALERT.MESSAGE}.
Ну а если серьезно, то не отправляйте такие гигантские сообщения в алерты. Алерт должен обозначить проблему, но не предоставить детальный лог её возникновения.
Что оставить в оповещении
Короткий алерт о загрузке процессора почти никогда не достигает предела 65 535. Полный лог в оповещении может его превысить. Дежурному обычно достаточно первых строк ошибки, имени хоста и ссылки на полный лог вместо текста на 60K+ символов. Такое сообщение быстро читается и помещается в предел.
Помните, что агрегировать сообщения от любых систем мониторинга, в том числе от Zabbix, можно в Incident Garden.