fix(awooop): record grouped alert events
This commit is contained in:
@@ -128,6 +128,116 @@ async def mirror_inbound_event(
|
||||
return event_id
|
||||
|
||||
|
||||
def build_grouped_alert_provider_event_id(alert_id: str, fingerprint: str) -> str:
|
||||
"""建立 grouped child alert 的冪等 provider_event_id。"""
|
||||
safe_alert_id = str(alert_id).strip() or "unknown"
|
||||
safe_fingerprint = str(fingerprint).strip()[:32] or "no-fingerprint"
|
||||
return f"alert-group:{safe_alert_id}:{safe_fingerprint}"
|
||||
|
||||
|
||||
def format_grouped_alert_event_content(
|
||||
*,
|
||||
alert_id: str,
|
||||
alertname: str,
|
||||
severity: str,
|
||||
namespace: str,
|
||||
target_resource: str,
|
||||
group_key: str,
|
||||
count: int,
|
||||
parent_fingerprint: str | None,
|
||||
fingerprint: str,
|
||||
) -> str:
|
||||
"""格式化只落 AwoooP、不發 Telegram 的告警收斂事件摘要。"""
|
||||
parent = parent_fingerprint or "-"
|
||||
target = target_resource or "-"
|
||||
ns = namespace or "default"
|
||||
return "\n".join(
|
||||
[
|
||||
"告警已收斂,不發 Telegram",
|
||||
f"Alert ID: {alert_id}",
|
||||
f"Alert: {alertname}",
|
||||
f"Severity: {severity}",
|
||||
f"Namespace: {ns}",
|
||||
f"Target: {target}",
|
||||
f"Group: {group_key}",
|
||||
f"Group Count: {count}",
|
||||
f"Parent Fingerprint: {parent}",
|
||||
f"Child Fingerprint: {fingerprint}",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def record_grouped_alert_event(
|
||||
*,
|
||||
project_id: str,
|
||||
alert_id: str,
|
||||
alertname: str,
|
||||
severity: str,
|
||||
namespace: str,
|
||||
target_resource: str,
|
||||
group_key: str,
|
||||
count: int,
|
||||
parent_fingerprint: str | None,
|
||||
fingerprint: str,
|
||||
) -> UUID | None:
|
||||
"""
|
||||
將被 AlertGroupingService 收斂的子告警落到 AwoooP conversation_event。
|
||||
|
||||
這條路徑刻意不發 Telegram,只保留 operator-facing 脈絡:
|
||||
- 群組不洗版
|
||||
- Console 仍能看到同組告警正在持續發生
|
||||
- DB 失敗 fail-open,不影響 Alertmanager webhook ACK
|
||||
"""
|
||||
try:
|
||||
from src.db.base import get_db_context
|
||||
|
||||
provider_event_id = build_grouped_alert_provider_event_id(alert_id, fingerprint)
|
||||
content = format_grouped_alert_event_content(
|
||||
alert_id=alert_id,
|
||||
alertname=alertname,
|
||||
severity=severity,
|
||||
namespace=namespace,
|
||||
target_resource=target_resource,
|
||||
group_key=group_key,
|
||||
count=count,
|
||||
parent_fingerprint=parent_fingerprint,
|
||||
fingerprint=fingerprint,
|
||||
)
|
||||
|
||||
async with get_db_context(project_id) as db:
|
||||
event_id = await mirror_inbound_event(
|
||||
db,
|
||||
project_id=project_id,
|
||||
channel_type="internal",
|
||||
provider_event_id=provider_event_id,
|
||||
platform_subject_id="alertmanager",
|
||||
channel_user_id="alertmanager",
|
||||
channel_chat_id=f"alert-group:{group_key}",
|
||||
content_type="text",
|
||||
raw_content=content,
|
||||
provider_ts=datetime.now(timezone.utc),
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"grouped_alert_event_recorded",
|
||||
project_id=project_id,
|
||||
alert_id=alert_id,
|
||||
event_id=str(event_id),
|
||||
group_key=group_key,
|
||||
count=count,
|
||||
)
|
||||
return event_id
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"grouped_alert_event_record_failed",
|
||||
project_id=project_id,
|
||||
alert_id=alert_id,
|
||||
group_key=group_key,
|
||||
error=str(exc),
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# 出站訊息記錄
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -18,6 +18,7 @@ from sqlalchemy import func, select
|
||||
|
||||
from src.db.awooop_models import (
|
||||
AwoooPContractRevision,
|
||||
AwoooPConversationEvent,
|
||||
AwoooPProject,
|
||||
AwoooPRunState,
|
||||
)
|
||||
@@ -31,6 +32,7 @@ logger = structlog.get_logger(__name__)
|
||||
_MAX_CONTRACTS = 200
|
||||
_DEFAULT_PER_PAGE = 50
|
||||
_MAX_PER_PAGE = 200
|
||||
_MAX_EVENTS = 100
|
||||
|
||||
# =============================================================================
|
||||
# Tenants
|
||||
@@ -147,6 +149,54 @@ async def list_runs(
|
||||
return {"runs": runs, "total": total, "page": page, "per_page": per_page}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Channel Events
|
||||
# =============================================================================
|
||||
|
||||
async def list_recent_channel_events(
|
||||
*,
|
||||
project_id: str | None,
|
||||
channel_type: str | None,
|
||||
provider_prefix: str | None,
|
||||
limit: int,
|
||||
) -> dict[str, Any]:
|
||||
"""列出最近 channel events,供 Operator Console 顯示收斂/鏡像脈絡。"""
|
||||
safe_limit = max(1, min(limit, _MAX_EVENTS))
|
||||
|
||||
async with get_db_context("awoooi") as db:
|
||||
stmt = select(AwoooPConversationEvent).order_by(
|
||||
AwoooPConversationEvent.received_at.desc()
|
||||
)
|
||||
if project_id is not None:
|
||||
stmt = stmt.where(AwoooPConversationEvent.project_id == project_id)
|
||||
if channel_type is not None:
|
||||
stmt = stmt.where(AwoooPConversationEvent.channel_type == channel_type)
|
||||
if provider_prefix is not None:
|
||||
stmt = stmt.where(
|
||||
AwoooPConversationEvent.provider_event_id.like(
|
||||
f"{provider_prefix}%"
|
||||
)
|
||||
)
|
||||
|
||||
result = await db.execute(stmt.limit(safe_limit))
|
||||
rows = list(result.scalars().all())
|
||||
|
||||
events = [
|
||||
{
|
||||
"event_id": r.event_id,
|
||||
"project_id": r.project_id,
|
||||
"channel_type": r.channel_type,
|
||||
"provider_event_id": r.provider_event_id,
|
||||
"channel_chat_id": r.channel_chat_id,
|
||||
"content_preview": r.content_preview,
|
||||
"is_duplicate": r.is_duplicate,
|
||||
"received_at": r.received_at,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
return {"events": events, "total": len(events), "limit": safe_limit}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Approvals
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user