fix: экспорт всех текущих проблем в Zabbix + совместимость шаблона с Zabbix 5.0
collect_and_alert теперь экспортирует ВСЕ текущие проблемные DAG-и, а не только новые — Zabbix видит проблему пока DAG не починен. Zabbix XML шаблон: version 5.0, убраны tags, preprocessing params, triggers/graphs вынесены на верхний уровень, history 7d / trends 365d. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
289c308fcb
commit
f3e1ad7df3
@ -211,14 +211,15 @@ class ActionHandler:
|
|||||||
return "needs_alert"
|
return "needs_alert"
|
||||||
|
|
||||||
def collect_and_alert(self, issues: list[DagIssue]):
|
def collect_and_alert(self, issues: list[DagIssue]):
|
||||||
"""Export alerts for issues that need alerting.
|
"""Export all current issues and mark new ones as alerted.
|
||||||
|
|
||||||
Filters to only unalerted issues, builds JSON payload,
|
Always exports the full list of current problems so Zabbix sees them
|
||||||
writes to file for Zabbix agent, and marks as alerted in state.
|
until they are resolved. Only logs warnings for newly discovered issues.
|
||||||
"""
|
"""
|
||||||
to_alert = []
|
# Build export payload for ALL current issues
|
||||||
|
all_problems = []
|
||||||
|
new_issues = []
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
if not self._state.is_alerted(issue.dag_id, issue.dag_run_id):
|
|
||||||
entry = self._state.ensure_entry(issue.dag_id, issue.dag_run_id)
|
entry = self._state.ensure_entry(issue.dag_id, issue.dag_run_id)
|
||||||
alert_data = {
|
alert_data = {
|
||||||
"dag_id": issue.dag_id,
|
"dag_id": issue.dag_id,
|
||||||
@ -229,36 +230,35 @@ class ActionHandler:
|
|||||||
"error_info": issue.error_info,
|
"error_info": issue.error_info,
|
||||||
"retry_count": entry["retry_count"],
|
"retry_count": entry["retry_count"],
|
||||||
}
|
}
|
||||||
to_alert.append(alert_data)
|
all_problems.append(alert_data)
|
||||||
logger.debug(
|
if not self._state.is_alerted(issue.dag_id, issue.dag_run_id):
|
||||||
"Issue queued for alert: %s/%s type=%s retries=%d",
|
new_issues.append(issue)
|
||||||
issue.dag_id, issue.dag_run_id,
|
|
||||||
issue.issue_type, entry["retry_count"],
|
|
||||||
)
|
|
||||||
|
|
||||||
if not to_alert:
|
if not all_problems:
|
||||||
# Write empty list to clear Zabbix trigger
|
|
||||||
self._exporter.export_problems([])
|
self._exporter.export_problems([])
|
||||||
logger.info("No issues to alert, exported empty problem list")
|
logger.info("No issues, exported empty problem list")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Log new issues at warning level
|
||||||
|
if new_issues:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Exporting %d problematic DAG runs for Zabbix: %s",
|
"New problematic DAG runs: %s",
|
||||||
len(to_alert),
|
|
||||||
", ".join(
|
", ".join(
|
||||||
f"{p['dag_id']}/{p['dag_run_id']}({p['issue_type']})"
|
f"{i.dag_id}/{i.dag_run_id}({i.issue_type})"
|
||||||
for p in to_alert
|
for i in new_issues
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log full alert payload at debug level
|
logger.debug("Alert payload:\n%s", json.dumps(all_problems, indent=2, ensure_ascii=False))
|
||||||
logger.debug("Alert payload:\n%s", json.dumps(to_alert, indent=2, ensure_ascii=False))
|
|
||||||
|
|
||||||
self._exporter.export_problems(to_alert)
|
self._exporter.export_problems(all_problems)
|
||||||
|
|
||||||
for issue in issues:
|
# Mark new issues as alerted
|
||||||
if not self._state.is_alerted(issue.dag_id, issue.dag_run_id):
|
for issue in new_issues:
|
||||||
self._state.mark_alerted(issue.dag_id, issue.dag_run_id)
|
self._state.mark_alerted(issue.dag_id, issue.dag_run_id)
|
||||||
logger.debug("Marked as alerted: %s/%s", issue.dag_id, issue.dag_run_id)
|
logger.debug("Marked as alerted: %s/%s", issue.dag_id, issue.dag_run_id)
|
||||||
|
|
||||||
logger.info("Exported %d issues to problems file for Zabbix agent", len(to_alert))
|
logger.info(
|
||||||
|
"Exported %d issues to problems file (%d new)",
|
||||||
|
len(all_problems), len(new_issues),
|
||||||
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<zabbix_export>
|
<zabbix_export>
|
||||||
<version>5.4</version>
|
<version>5.0</version>
|
||||||
<groups>
|
<groups>
|
||||||
<group>
|
<group>
|
||||||
<name>Templates/Applications</name>
|
<name>Templates/Applications</name>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
- Установлен airflow-monitor (/opt/airflow-monitor)
|
- Установлен airflow-monitor (/opt/airflow-monitor)
|
||||||
- Установлен конфиг /etc/zabbix/zabbix_agentd.conf.d/airflow-monitor.conf
|
- Установлен конфиг /etc/zabbix/zabbix_agentd.conf.d/airflow-monitor.conf
|
||||||
|
|
||||||
Версия шаблона: 1.0.0 (Zabbix 5.x)</description>
|
Версия шаблона: 1.0.1 (Zabbix 5.0)</description>
|
||||||
<groups>
|
<groups>
|
||||||
<group>
|
<group>
|
||||||
<name>Templates/Applications</name>
|
<name>Templates/Applications</name>
|
||||||
@ -32,12 +32,6 @@
|
|||||||
<history>7d</history>
|
<history>7d</history>
|
||||||
<value_type>TEXT</value_type>
|
<value_type>TEXT</value_type>
|
||||||
<description>JSON-массив проблемных DAG-запусков. [] = нет проблем.</description>
|
<description>JSON-массив проблемных DAG-запусков. [] = нет проблем.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>airflow</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Problematic DAGs count -->
|
<!-- Problematic DAGs count -->
|
||||||
@ -45,16 +39,10 @@
|
|||||||
<name>Airflow: Problematic DAGs count</name>
|
<name>Airflow: Problematic DAGs count</name>
|
||||||
<key>airflow.dag.problems.count</key>
|
<key>airflow.dag.problems.count</key>
|
||||||
<delay>1m</delay>
|
<delay>1m</delay>
|
||||||
<history>90d</history>
|
<history>7d</history>
|
||||||
<trends>365d</trends>
|
<trends>365d</trends>
|
||||||
<value_type>UNSIGNED</value_type>
|
<value_type>UNSIGNED</value_type>
|
||||||
<description>Количество DAG-запусков с проблемами.</description>
|
<description>Количество DAG-запусков с проблемами.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>airflow</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Heartbeat -->
|
<!-- Heartbeat -->
|
||||||
@ -63,14 +51,9 @@
|
|||||||
<key>airflow.monitor.heartbeat</key>
|
<key>airflow.monitor.heartbeat</key>
|
||||||
<delay>1m</delay>
|
<delay>1m</delay>
|
||||||
<history>7d</history>
|
<history>7d</history>
|
||||||
|
<trends>365d</trends>
|
||||||
<value_type>UNSIGNED</value_type>
|
<value_type>UNSIGNED</value_type>
|
||||||
<description>Epoch timestamp последнего успешного цикла мониторинга.</description>
|
<description>Epoch timestamp последнего успешного цикла мониторинга.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>dag-monitor</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Status JSON -->
|
<!-- Status JSON -->
|
||||||
@ -81,12 +64,6 @@
|
|||||||
<history>7d</history>
|
<history>7d</history>
|
||||||
<value_type>TEXT</value_type>
|
<value_type>TEXT</value_type>
|
||||||
<description>JSON со статусом монитора.</description>
|
<description>JSON со статусом монитора.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>dag-monitor</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Service alive -->
|
<!-- Service alive -->
|
||||||
@ -94,19 +71,13 @@
|
|||||||
<name>Airflow Monitor: Service alive</name>
|
<name>Airflow Monitor: Service alive</name>
|
||||||
<key>airflow.monitor.alive</key>
|
<key>airflow.monitor.alive</key>
|
||||||
<delay>1m</delay>
|
<delay>1m</delay>
|
||||||
<history>90d</history>
|
<history>7d</history>
|
||||||
<trends>365d</trends>
|
<trends>365d</trends>
|
||||||
<value_type>UNSIGNED</value_type>
|
<value_type>UNSIGNED</value_type>
|
||||||
<description>1 = сервис запущен, 0 = остановлен.</description>
|
<description>1 = сервис запущен, 0 = остановлен.</description>
|
||||||
<valuemap>
|
<valuemap>
|
||||||
<name>Service state</name>
|
<name>Service state</name>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>dag-monitor</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- DAG count (dependent from status) -->
|
<!-- DAG count (dependent from status) -->
|
||||||
@ -114,7 +85,7 @@
|
|||||||
<name>Airflow Monitor: DAG count</name>
|
<name>Airflow Monitor: DAG count</name>
|
||||||
<type>DEPENDENT</type>
|
<type>DEPENDENT</type>
|
||||||
<key>airflow.monitor.dag_count</key>
|
<key>airflow.monitor.dag_count</key>
|
||||||
<history>90d</history>
|
<history>7d</history>
|
||||||
<trends>365d</trends>
|
<trends>365d</trends>
|
||||||
<value_type>UNSIGNED</value_type>
|
<value_type>UNSIGNED</value_type>
|
||||||
<description>Количество отслеживаемых DAG-ов.</description>
|
<description>Количество отслеживаемых DAG-ов.</description>
|
||||||
@ -124,23 +95,13 @@
|
|||||||
<preprocessing>
|
<preprocessing>
|
||||||
<step>
|
<step>
|
||||||
<type>JSONPATH</type>
|
<type>JSONPATH</type>
|
||||||
<parameters>
|
<params>$.dag_count</params>
|
||||||
<parameter>$.dag_count</parameter>
|
|
||||||
</parameters>
|
|
||||||
</step>
|
</step>
|
||||||
<step>
|
<step>
|
||||||
<type>DISCARD_UNCHANGED_HEARTBEAT</type>
|
<type>DISCARD_UNCHANGED_HEARTBEAT</type>
|
||||||
<parameters>
|
<params>1h</params>
|
||||||
<parameter>1h</parameter>
|
|
||||||
</parameters>
|
|
||||||
</step>
|
</step>
|
||||||
</preprocessing>
|
</preprocessing>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>airflow</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Cycle time (dependent from status) -->
|
<!-- Cycle time (dependent from status) -->
|
||||||
@ -148,7 +109,7 @@
|
|||||||
<name>Airflow Monitor: Cycle time (seconds)</name>
|
<name>Airflow Monitor: Cycle time (seconds)</name>
|
||||||
<type>DEPENDENT</type>
|
<type>DEPENDENT</type>
|
||||||
<key>airflow.monitor.cycle_time</key>
|
<key>airflow.monitor.cycle_time</key>
|
||||||
<history>90d</history>
|
<history>7d</history>
|
||||||
<trends>365d</trends>
|
<trends>365d</trends>
|
||||||
<value_type>FLOAT</value_type>
|
<value_type>FLOAT</value_type>
|
||||||
<units>s</units>
|
<units>s</units>
|
||||||
@ -159,17 +120,9 @@
|
|||||||
<preprocessing>
|
<preprocessing>
|
||||||
<step>
|
<step>
|
||||||
<type>JSONPATH</type>
|
<type>JSONPATH</type>
|
||||||
<parameters>
|
<params>$.cycle_time_seconds</params>
|
||||||
<parameter>$.cycle_time_seconds</parameter>
|
|
||||||
</parameters>
|
|
||||||
</step>
|
</step>
|
||||||
</preprocessing>
|
</preprocessing>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>dag-monitor</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- Cycle count (dependent from status) -->
|
<!-- Cycle count (dependent from status) -->
|
||||||
@ -177,7 +130,7 @@
|
|||||||
<name>Airflow Monitor: Cycle count</name>
|
<name>Airflow Monitor: Cycle count</name>
|
||||||
<type>DEPENDENT</type>
|
<type>DEPENDENT</type>
|
||||||
<key>airflow.monitor.cycle_count</key>
|
<key>airflow.monitor.cycle_count</key>
|
||||||
<history>90d</history>
|
<history>7d</history>
|
||||||
<trends>365d</trends>
|
<trends>365d</trends>
|
||||||
<value_type>UNSIGNED</value_type>
|
<value_type>UNSIGNED</value_type>
|
||||||
<description>Номер текущего цикла мониторинга.</description>
|
<description>Номер текущего цикла мониторинга.</description>
|
||||||
@ -187,42 +140,41 @@
|
|||||||
<preprocessing>
|
<preprocessing>
|
||||||
<step>
|
<step>
|
||||||
<type>JSONPATH</type>
|
<type>JSONPATH</type>
|
||||||
<parameters>
|
<params>$.cycle_count</params>
|
||||||
<parameter>$.cycle_count</parameter>
|
|
||||||
</parameters>
|
|
||||||
</step>
|
</step>
|
||||||
<step>
|
<step>
|
||||||
<type>DISCARD_UNCHANGED_HEARTBEAT</type>
|
<type>DISCARD_UNCHANGED_HEARTBEAT</type>
|
||||||
<parameters>
|
<params>1h</params>
|
||||||
<parameter>1h</parameter>
|
|
||||||
</parameters>
|
|
||||||
</step>
|
</step>
|
||||||
</preprocessing>
|
</preprocessing>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>component</tag>
|
|
||||||
<value>dag-monitor</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</item>
|
</item>
|
||||||
</items>
|
</items>
|
||||||
|
|
||||||
|
<macros>
|
||||||
|
<macro>
|
||||||
|
<macro>{$AIRFLOW.STALE_THRESHOLD}</macro>
|
||||||
|
<value>600</value>
|
||||||
|
<description>Порог устаревания данных (секунды).</description>
|
||||||
|
</macro>
|
||||||
|
<macro>
|
||||||
|
<macro>{$AIRFLOW.CYCLE_TIME_WARN}</macro>
|
||||||
|
<value>60</value>
|
||||||
|
<description>Порог предупреждения о долгом цикле (секунды).</description>
|
||||||
|
</macro>
|
||||||
|
</macros>
|
||||||
|
</template>
|
||||||
|
</templates>
|
||||||
|
|
||||||
<triggers>
|
<triggers>
|
||||||
<!-- Problematic DAGs detected -->
|
<!-- Problematic DAGs detected -->
|
||||||
<trigger>
|
<trigger>
|
||||||
<expression>{Airflow DAG Monitor:airflow.dag.problems.count.last()}>0</expression>
|
<expression>{Airflow DAG Monitor:airflow.dag.problems.count.last()}>0</expression>
|
||||||
<recovery_expression>{Airflow DAG Monitor:airflow.dag.problems.count.last()}=0</recovery_expression>
|
<recovery_expression></recovery_expression>
|
||||||
<name>Airflow: {ITEM.LASTVALUE1} problematic DAG run(s) detected</name>
|
<name>Airflow: {ITEM.LASTVALUE1} problematic DAG run(s) detected</name>
|
||||||
<priority>HIGH</priority>
|
<priority>HIGH</priority>
|
||||||
<description>Обнаружены проблемные DAG-запуски (failed или long_running).
|
<description>Обнаружены проблемные DAG-запуски (failed или long_running).
|
||||||
Подробности в item "Airflow: Problematic DAGs (JSON)".
|
Подробности в item "Airflow: Problematic DAGs (JSON)".
|
||||||
Триггер снимается автоматически при разрешении всех проблем.</description>
|
Триггер снимается автоматически при разрешении всех проблем.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>scope</tag>
|
|
||||||
<value>availability</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</trigger>
|
</trigger>
|
||||||
|
|
||||||
<!-- Heartbeat stale -->
|
<!-- Heartbeat stale -->
|
||||||
@ -238,12 +190,6 @@
|
|||||||
<expression>{Airflow DAG Monitor:airflow.monitor.alive.last()}=0</expression>
|
<expression>{Airflow DAG Monitor:airflow.monitor.alive.last()}=0</expression>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>scope</tag>
|
|
||||||
<value>availability</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</trigger>
|
</trigger>
|
||||||
|
|
||||||
<!-- Service down -->
|
<!-- Service down -->
|
||||||
@ -253,12 +199,6 @@
|
|||||||
<priority>HIGH</priority>
|
<priority>HIGH</priority>
|
||||||
<description>Сервис airflow-monitor не запущен.
|
<description>Сервис airflow-monitor не запущен.
|
||||||
Запустить: sudo systemctl start airflow-monitor</description>
|
Запустить: sudo systemctl start airflow-monitor</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>scope</tag>
|
|
||||||
<value>availability</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</trigger>
|
</trigger>
|
||||||
|
|
||||||
<!-- Slow cycle -->
|
<!-- Slow cycle -->
|
||||||
@ -267,12 +207,6 @@
|
|||||||
<name>Airflow Monitor: Cycle time is high ({ITEM.LASTVALUE1}s)</name>
|
<name>Airflow Monitor: Cycle time is high ({ITEM.LASTVALUE1}s)</name>
|
||||||
<priority>WARNING</priority>
|
<priority>WARNING</priority>
|
||||||
<description>Цикл мониторинга выполняется дольше 60 секунд.</description>
|
<description>Цикл мониторинга выполняется дольше 60 секунд.</description>
|
||||||
<tags>
|
|
||||||
<tag>
|
|
||||||
<tag>scope</tag>
|
|
||||||
<value>performance</value>
|
|
||||||
</tag>
|
|
||||||
</tags>
|
|
||||||
</trigger>
|
</trigger>
|
||||||
</triggers>
|
</triggers>
|
||||||
|
|
||||||
@ -314,21 +248,6 @@
|
|||||||
</graph>
|
</graph>
|
||||||
</graphs>
|
</graphs>
|
||||||
|
|
||||||
<macros>
|
|
||||||
<macro>
|
|
||||||
<macro>{$AIRFLOW.STALE_THRESHOLD}</macro>
|
|
||||||
<value>600</value>
|
|
||||||
<description>Порог устаревания данных (секунды).</description>
|
|
||||||
</macro>
|
|
||||||
<macro>
|
|
||||||
<macro>{$AIRFLOW.CYCLE_TIME_WARN}</macro>
|
|
||||||
<value>60</value>
|
|
||||||
<description>Порог предупреждения о долгом цикле (секунды).</description>
|
|
||||||
</macro>
|
|
||||||
</macros>
|
|
||||||
</template>
|
|
||||||
</templates>
|
|
||||||
|
|
||||||
<value_maps>
|
<value_maps>
|
||||||
<value_map>
|
<value_map>
|
||||||
<name>Service state</name>
|
<name>Service state</name>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user