feat(syslog): pattern alerts: telling phrases counted per source each minute for alert rules, storage-trouble presets (#6001)
A pattern (phrases, a severity, a source, a window) is counted every minute into plugin_metrics as lines_matching_<key> per added source, zero included, so an ordinary alert rule fires on a match and clears once the window passes. Each count reads only the lines stored since the last, by line id; a new or changed pattern reads its window once. Patterns page with ready-made storage-trouble presets and "Alert on it", which opens the rule form aimed at the count. Source names are kept unique, and a rename moves its counts and the rules on them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import BigInteger, DateTime, ForeignKey, Index, SmallInteger, String, Text
|
||||
from sqlalchemy import JSON, BigInteger, Boolean, DateTime, ForeignKey, Index, SmallInteger, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from steward.core.ids import time_ordered_id
|
||||
@@ -65,3 +65,33 @@ class SyslogLine(Base):
|
||||
|
||||
|
||||
STATES = ("offered", "accepted", "ignored")
|
||||
|
||||
|
||||
class SyslogPattern(Base):
|
||||
"""A telling phrase to count (#6001): lines whose message holds any of
|
||||
`phrases` (case-insensitive) and, when `max_severity` is set, whose
|
||||
severity is that or worse. Every minute each added source it covers (all,
|
||||
or `source_id`'s) gets a sample of how many of its lines matched in the
|
||||
last `window_minutes`, as metric `lines_matching_<key>`, so an ordinary
|
||||
alert rule fires on a match and clears once the window passes it."""
|
||||
__tablename__ = "syslog_patterns"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
|
||||
key: Mapped[str] = mapped_column(String(48), nullable=False, unique=True)
|
||||
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
phrases: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
||||
max_severity: Mapped[int | None] = mapped_column(SmallInteger, nullable=True)
|
||||
source_id: Mapped[str | None] = mapped_column(
|
||||
String(36), ForeignKey("syslog_sources.id", ondelete="CASCADE"), nullable=True)
|
||||
window_minutes: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=15)
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|
||||
|
||||
@property
|
||||
def metric(self) -> str:
|
||||
return metric_for(self.key)
|
||||
|
||||
|
||||
def metric_for(key: str) -> str:
|
||||
return f"lines_matching_{key}"
|
||||
|
||||
Reference in New Issue
Block a user