fix(alerts): expose alertmanager webhook request metrics
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 2m0s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 2m0s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
This commit is contained in:
@@ -25,6 +25,7 @@ Endpoints:
|
||||
import asyncio
|
||||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
import uuid
|
||||
from typing import Literal
|
||||
|
||||
@@ -34,7 +35,7 @@ from pydantic import BaseModel, Field
|
||||
from src.core.config import settings
|
||||
from src.core.constants import is_cicd_alertname, is_heartbeat_alertname
|
||||
from src.core.logging import get_logger
|
||||
from src.core.metrics import record_alert_chain_success
|
||||
from src.core.metrics import record_alert_chain_success, record_webhook_request
|
||||
from src.models.approval import (
|
||||
ApprovalRequestCreate,
|
||||
BlastRadius,
|
||||
@@ -2606,6 +2607,27 @@ async def alertmanager_webhook(
|
||||
- 內網 IP (192.168.x.x, 10.x.x.x, 172.x.x.x): 免 HMAC
|
||||
- 外網 IP: 拒絕
|
||||
"""
|
||||
metric_started_at = time.monotonic()
|
||||
metric_recorded = False
|
||||
|
||||
def _record_webhook_metric(metric_status: str) -> None:
|
||||
nonlocal metric_recorded
|
||||
if metric_recorded:
|
||||
return
|
||||
metric_recorded = True
|
||||
try:
|
||||
record_webhook_request(
|
||||
"alertmanager",
|
||||
metric_status,
|
||||
time.monotonic() - metric_started_at,
|
||||
)
|
||||
except Exception as metric_err:
|
||||
logger.warning(
|
||||
"alertmanager_webhook_metric_record_failed",
|
||||
status=metric_status,
|
||||
error=str(metric_err),
|
||||
)
|
||||
|
||||
# 取得客戶端 IP
|
||||
client_ip = request.client.host if request.client else "unknown"
|
||||
forwarded_for = request.headers.get("X-Forwarded-For", "").split(",")[0].strip()
|
||||
@@ -2618,6 +2640,7 @@ async def alertmanager_webhook(
|
||||
client_ip=actual_ip,
|
||||
reason="External IP must use /alerts with HMAC",
|
||||
)
|
||||
_record_webhook_metric("error")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="External sources must use /alerts endpoint with HMAC signature",
|
||||
@@ -2633,6 +2656,7 @@ async def alertmanager_webhook(
|
||||
# 只處理第一個 firing 告警 (避免告警風暴)
|
||||
firing_alerts = [a for a in payload.alerts if a.status == "firing"]
|
||||
if not firing_alerts:
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message="No firing alerts to process",
|
||||
@@ -2712,6 +2736,7 @@ async def alertmanager_webhook(
|
||||
)
|
||||
|
||||
record_alert_chain_success("alertmanager")
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message=f"CI/CD alert processed (simple format): {alertname}",
|
||||
@@ -2721,6 +2746,7 @@ async def alertmanager_webhook(
|
||||
except Exception as e:
|
||||
logger.error("cicd_telegram_failed", error=str(e), alertname=alertname)
|
||||
# CI/CD 通知失敗不阻擋流程
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message=f"CI/CD alert logged (telegram failed): {alertname}",
|
||||
@@ -2866,6 +2892,7 @@ async def alertmanager_webhook(
|
||||
parent_fingerprint=grouping_result.parent_fingerprint,
|
||||
fingerprint=fingerprint,
|
||||
)
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message=(
|
||||
@@ -2940,6 +2967,7 @@ async def alertmanager_webhook(
|
||||
notification_type=notification_type,
|
||||
)
|
||||
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message=f"🛡️ 告警收斂 (x{updated_approval.hit_count}) - 已排程節流再通知",
|
||||
@@ -3004,6 +3032,7 @@ async def alertmanager_webhook(
|
||||
severity=severity,
|
||||
)
|
||||
record_alert_chain_success("alertmanager")
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message="✅ TYPE-1 純資訊告警已通知 (no LLM)",
|
||||
@@ -3050,6 +3079,7 @@ async def alertmanager_webhook(
|
||||
notification_type=notification_type,
|
||||
recurrence_stage="llm_inflight",
|
||||
)
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message="🛡️ 告警已由同指紋背景 AI 分析處理中,已排程節流再通知",
|
||||
@@ -3096,6 +3126,7 @@ async def alertmanager_webhook(
|
||||
)
|
||||
|
||||
record_alert_chain_success("alertmanager")
|
||||
_record_webhook_metric("success")
|
||||
return AlertResponse(
|
||||
success=True,
|
||||
message="✅ 告警已排入背景分析 (202 Accepted)",
|
||||
@@ -3110,6 +3141,7 @@ async def alertmanager_webhook(
|
||||
fingerprint=fingerprint,
|
||||
error=str(e),
|
||||
)
|
||||
_record_webhook_metric("error")
|
||||
return AlertResponse(
|
||||
success=False,
|
||||
message="⚠️ 告警已接收但處理降級,避免 Alertmanager retry storm;已交由背景治理/人工介入追蹤",
|
||||
|
||||
Reference in New Issue
Block a user