LiteLLM Gateway Slack 告警集成:预算告警框架、批量投递与高流量下的告警性能设计
【免费下载链接】litellmThe fastest, litest AI Gateway. Rust core with Python SDK. Call 100+ LLM APIs in OpenAI (or native) format with cost tracking, guardrails, load balancing, and logging [Bedrock, Azure, OpenAI, Anthropic, OpenAI, VertexAI, vLLM, Nvidia NIM]项目地址: https://gitcode.com/GitHub_Trending/li/litellm
本篇技术指南以 LiteLLM 仓库中 Slack Alerting 集成模块文档 为主体,系统讲解 LiteLLM Gateway(Proxy)的 Slack 告警子系统:它如何把预算超限、模型宕机、请求挂起、日报/周报等事件批量聚合后投递到 Slack 或 MS Teams,如何依靠缓存做告警去重与降噪,以及budget_alert_types.py中预算告警策略框架的设计。读完本篇,你能理解该模块的完整文件结构、核心配置参数与默认值、批量投递链路,并知道如何扩展一种新的预算告警类型。
模块定位与文件结构
LiteLLM Gateway 在承载多团队、多 API Key、多部署(deployment)的 LLM 流量时,需要一套统一的可观测告警通道。litellm/integrations/SlackAlerting/目录就是这套通道的实现:它继承 LiteLLM 的回调日志体系(CustomLogger),在请求成功、失败、预算变化等事件发生时收集告警,再通过 Webhook 批量推送到 Slack(以及 MS Teams、Webhook、Email 等旁路通道)。
模块文档给出的目录职责如下(结合仓库当前实际文件整理):
| 文件 | 职责 |
|---|---|
| slack_alerting.py | 主文件。SlackAlerting类,负责各类告警的判定、格式化、入队与投递 |
| batching_handler.py | 批处理 + 通过 Httpx 向 Slack 发送 POST 请求。告警每 10 秒或队列事件数超过 X 时发送,以保证高流量下 LiteLLM 的性能 |
| budget_alert_types.py | 预算告警类型的策略框架:抽象基类 + 各实体级别的告警实现 + 工厂函数 |
| utils.py | Slack 告警专用工具函数,如解析alert_to_webhook_url中的os.environ/...环境变量 |
| ms_teams.py | MS Teams 投递适配:把告警文本包装为 Adaptive Card 负载 |
| hanging_request_check.py | 检测“挂起”的 LLM 请求(超过阈值仍未返回) |
| user_spend_alerts.py | 按用户维度的日/月消费阈值与消费异常检测逻辑 |
需要说明的一点:文档中提到的types.py(AlertType 枚举所在文件)在当前仓库结构中已迁移到公共类型目录 litellm/types/integrations/slack_alerting.py,其中定义了AlertType枚举、SlackAlertingArgs参数模型、DEFAULT_ALERT_TYPES等,后文会逐一展开。
主类 SlackAlerting:构造参数与配置项
SlackAlerting定义在 slack_alerting.py,继承自CustomBatchLogger(位于 litellm/integrations/custom_batch_logger.py)。构造函数签名与默认值如下:
def __init__( self, internal_usage_cache: DualCache | None = None, # 内部用量缓存(内存 + Redis),用于告警去重 alerting_threshold: float | None = None, # 慢请求/挂起请求阈值(秒),None 时默认 300 alerting: list | None = [], # 启用的通道:["slack"]、["slack", "email"] 等 alert_types: list[AlertType] = DEFAULT_ALERT_TYPES, alert_to_webhook_url: dict[AlertType, list[str] | str] | None = None, # 按告警类型分流到不同频道 alerting_args={}, # 对应 SlackAlertingArgs 的可选覆盖项 default_webhook_url: str | None = None, alert_type_config: dict[str, dict] | None = None, # 按类型启用 digest 聚合模式 **kwargs, ):几个关键行为:
- 慢请求阈值:
alerting_threshold缺省为 300 秒(见 slack_alerting.py),即一次 LLM 调用耗时超过 5 分钟才触发llm_too_slow告警。 - Webhook URL 解析:
alert_to_webhook_url会先经过 utils.py 的process_slack_alerting_variables处理——值中以os.environ/开头的条目会被替换为对应环境变量的实际取值,从而支持“不同告警类型路由到不同 Slack 频道,且频道 URL 不写死在配置里”。 - HTTP 客户端:实例自带一个专用的异步 Httpx 客户端(
get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback)),所有告警请求都不占用 LLM 调用所使用的连接池。
SlackAlertingArgs:可调参数与默认值
alerting_args会被构造为 Pydantic 模型SlackAlertingArgs(types/integrations/slack_alerting.py)。它的全部字段与默认值如下表:
| 参数 | 默认值 | 说明 |
|---|---|---|
daily_report_frequency | 43200(12 小时) | 部署延迟/失败日报的发送频率(秒),支持环境变量SLACK_DAILY_REPORT_FREQUENCY覆盖 |
report_check_interval | 300(5 分钟) | 后台进程检查“是否该发日报”的轮询间隔(秒) |
budget_alert_ttl | 86400(24 小时) | 预算告警的缓存 TTL,同一预算事件在 TTL 内不重复告警 |
outage_alert_ttl | 60 | 模型级宕机告警的错误统计时间窗口(秒) |
region_outage_alert_ttl | 60 | 提供商区域级宕机告警的错误统计时间窗口(秒) |
minor_outage_alert_threshold | 5 | 窗口内错误数达到 5 触发“次要宕机”告警(400 不计入) |
major_outage_alert_threshold | 10 | 错误数达到 10 触发“主要宕机”告警 |
max_outage_alert_list_size | 10 | 缓存中最多保存的错误码数量,防止内存泄漏 |
log_to_console | False | 为 True 时把告警 payload 打印到控制台(调试用) |
daily_spend_per_user_threshold | None | 单用户当日(UTC)消费超过该美元金额即告警,默认关闭 |
monthly_spend_per_user_threshold | None | 单用户当月消费阈值,默认关闭 |
spend_anomaly_multiplier | 3.0 | 今日消费超过“过去 N 天日均”的该倍数时判定为异常 |
spend_anomaly_baseline_days | 7 | 异常检测使用的基线天数 |
spend_anomaly_min_spend | 10.0 | 触发异常告警所需的当日最低消费,降低误报 |
user_spend_check_interval | 3600 | 用户消费阈值/异常检查的周期(秒),最小 60 |
Gateway 配置示例
结合 ProxyConfig 的update_values接受的字段(alerting、alerting_threshold、alert_types、alerting_args、alert_to_webhook_url、alert_type_config),一个典型的 Gateway YAML 配置如下(该示例为配置文件写法,非仓库内文件):
model_list: - model_name: gpt-4o litellm_params: model: gpt-4o api_key: os.environ/OPENAI_API_KEY general_settings: # 启用的告警通道;webhook/email 为预算告警的旁路通道 alerting: - "slack" - "email" # 慢请求阈值(秒) alerting_threshold: 300 # 按告警类型把消息路由到不同频道,URL 用环境变量注入 alert_to_webhook_url: budget_alerts: "os.environ/SLACK_BUDGET_WEBHOOK_URL" outage_alerts: "os.environ/SLACK_OUTAGE_WEBHOOK_URL" alerting_args: budget_alert_ttl: 86400 # 预算告警 24 小时内不重复 minor_outage_alert_threshold: 5 major_outage_alert_threshold: 10 log_to_console: true # 排障时建议打开运行时,SlackAlerting按“alert_to_webhook_url[alert_type]→default_webhook_url→ 环境变量SLACK_WEBHOOK_URL或ALERTING_WEBHOOK_URL”的优先级解析投递地址(slack_alerting.py);三者都缺失时会抛出ValueError。若alerting中包含ms_teams,则额外要求设置MS_TEAMS_WEBHOOK_URL环境变量,否则该条告警会被丢弃并记录错误日志(见 slack_alerting.py 与 ms_teams.py)。
批量投递机制:10 秒窗口 + 队列合并
batching_handler.py是整个模块的投递核心。模块文档明确指出:“Slack alerts are sent every 10s or when events are greater than X events. Done to ensure litellm has good performance under high traffic”(见 batching_handler.py 的模块注释)。其工作链路是:
- 入队:
SlackAlerting.send_alert(...)(slack_alerting.py)并不直接发 HTTP 请求,而是把{url, headers, payload, alert_type}追加进log_queue。send_alert内部还承担了通道分发:webhook通道把预算事件以结构化 JSON(WebhookEvent)POST 到WEBHOOK_URL;email通道把预算事件通过 SMTP 发信;slack/ms_teams通道才进入消息队列。 - 定时冲刷:继承自
CustomBatchLogger的periodic_flush每 10 秒(litellm.DEFAULT_FLUSH_INTERVAL_SECONDS)把队列内容一次性发出;SlackAlerting覆写了periodic_flush,在冲刷队列前先冲刷 digest 聚合桶(见下文)(slack_alerting.py)。 - 满额即时冲刷:队列长度达到
batch_size(litellm.DEFAULT_BATCH_SIZE)时立即触发flush_queue,避免高流量下告警积压。 - 合并去重(squash):
async_send_batch先调用 squash_payloads,把同一轮窗口内相同 (url, alert_type)的告警合并为一个条目并累加count,再用asyncio.gather并发发送。发送时若count > 1,会在消息头部加上[Num Alerts: {count}]前缀(batching_handler.py)——一次宕机引发的 50 条重复告警在 Slack 里只会看到 1 条带计数的前缀消息。 - 容错:单条发送失败(非 200 或异常)只写 debug 日志,不中断整批;当
alerting_args.log_to_console为 True 时 payload 会同时打到verbose_proxy_logger,方便没有 Slack 权限时本地排障。
MS Teams 走同一个队列:队列项带format=ms_teams标记,发送前由 build_ms_teams_payload 把纯文本包装成AdaptiveCard v1.4的 message attachment(Teams Incoming Webhook 要求负载形态不同,Slack 则是{"text": ...}的裸 payload)。
Digest 聚合模式
send_alert还内建了比 squash 更强的聚合:若某告警类型在alert_type_config中启用了digest(模型AlertTypeConfig,types/integrations/slack_alerting.py),该类型的告警不会逐条发送,而是按(alert_type, model, api_base)三元组分桶,累计count,直到digest_interval(默认 86400 秒即 24 小时)到期,由_flush_digest_buckets生成一条“Digest”汇总消息再走正常批量通道(slack_alerting.py)。这是面向高频低价值告警(如单条 LLM 异常)的降噪手段。
告警类型体系:AlertType 与 DEFAULT_ALERT_TYPES
AlertType是字符串枚举(types/integrations/slack_alerting.py),覆盖 LLM 运行、预算与消费、数据库、报表、部署、宕机、回退与资源管理等事件族:
- LLM 相关:
llm_exceptions(调用失败)、llm_too_slow(响应过慢)、llm_requests_hanging(请求挂起); - 预算与消费:
budget_alerts、spend_reports、failed_tracking_spend、user_spend_thresholds、user_spend_anomalies; - 数据库:
db_exceptions; - 报表:
daily_reports; - 部署:
cooldown_deployment、new_model_added、model_deprecation_warnings; - 宕机:
outage_alerts(模型级)、region_outage_alerts(提供商区域级); - 回退:
fallback_reports; - 资源管理事件:虚拟 Key 的创建/更新/删除、团队与内部用户的创建/更新/删除。
未显式指定alert_types时,使用DEFAULT_ALERT_TYPES白名单(types/integrations/slack_alerting.py),它开启了慢/挂起/异常、预算与消费报表、数据库异常、日报、部署、宕机、回退等绝大多数类型,唯独不含虚拟 Key/团队/用户管理事件——这类审计告警需显式加入alert_types才会投递。
每条告警统一由send_alert组装消息头:Alert type / Level / Timestamp / Message,并附请求模型、API Base(如有)与PROXY_BASE_URL,便于从 Slack 消息直接定位实例与部署(slack_alerting.py)。
预算告警框架(budget_alert_types.py)详解
这是模块文档着墨最多的部分:budget_alert_types.py提供了一套面向不同预算实体(代理、用户、团队、Key 等)的策略框架,用于回答“这条预算告警属于哪个实体、消息前缀是什么、用哪个 ID 做去重”。
抽象基类与工厂函数
文档定义的框架接口为:
get_event_group():返回该告警对应的Litellm_EntityType;get_event_message():返回告警消息前缀;get_id(user_info):返回用于缓存/追踪的实体 ID。
对照当前源码(budget_alert_types.py),抽象基类BaseBudgetAlertType保留了其中两个抽象方法get_event_message()与get_id(user_info);从源码结构看,事件分组信息改由调用方传入的CallInfo.event_group携带,budget_alerts()直接使用user_info.event_group判断是否发送(slack_alerting.py),因此文档示例中的get_event_group()调用在当前版本中应理解为该演进前的接口。
工厂函数get_budget_alert_type(type)按告警类型字符串返回对应策略实例:
from litellm.integrations.SlackAlerting.budget_alert_types import get_budget_alert_type budget_alert_class = get_budget_alert_type("user_budget") event_message = budget_alert_class.get_event_message() # "User Budget: " cache_id = budget_alert_class.get_id(user_info) # user_id当前工厂支持的全部字符串键及其映射(budget_alert_types.py):
| 类型字符串 | 策略类 | 消息前缀 | 去重 ID 来源 |
|---|---|---|---|
proxy_budget | ProxyBudgetAlert | Proxy Budget: | 固定"default_id"(全局一条) |
soft_budget | SoftBudgetAlert | Soft Budget Crossed: | 团队场景取team_id,否则取token |
user_budget | UserBudgetAlert | User Budget: | user_id |
team_budget | TeamBudgetAlert | Team Budget: | team_id |
organization_budget | OrganizationBudgetAlert | Organization Budget: | organization_id |
token_budget/max_budget_alert | TokenBudgetAlert | Key Budget: | token |
projected_limit_exceeded | ProjectedLimitExceededAlert | Key Budget: Projected Limit Exceeded | token |
project_budget | ProjectBudgetAlert | Project Budget: | token |
注意两点实现细节:其一,token_budget与max_budget_alert复用同一个TokenBudgetAlert实例(Key 预算的两种触发口径);其二,传入未知字符串时工厂兜底返回ProxyBudgetAlert(),而不是抛错——从源码结构看,这是让未知预算事件也能以代理级告警形式被看见的防御性设计。
触发条件与防骚扰去重
预算事件的判定在_get_event_and_event_message中完成(slack_alerting.py),核心规则:
- 软预算:
spend >= soft_budget时产生soft_budget_crossed事件,消息追加Total Soft Budget: {soft_budget}; - 硬预算三档:
spend >= max_budget产生budget_crossed(预算已击穿);剩余预算占比 ≤ 5% 产生threshold_crossed(“5% Threshold Crossed”);≤ 15% 产生threshold_crossed(“15% Threshold Crossed”)。占比由_get_percent_of_max_budget_left计算,5%/15% 常量定义在 types/integrations/slack_alerting.py; - 预防性告警:
projected_limit_exceeded事件在“按当前速率推算将超预算”时提前触发。
发送前还有一层缓存去重(slack_alerting.py):以budget_alerts:{event}:{实体ID}为缓存键查询internal_usage_cache(DualCache:内存 + Redis),命中则跳过;未命中则发送并写入SENT标记,TTL 即alerting_args.budget_alert_ttl(默认 24 小时)。这保证“同一 Key 同一天击穿同一档预算只会收到一条 Slack 消息”,多实例部署下依靠 Redis 共享去重状态。
预算告警同时是多通道事件:send_alert中若alerting含webhook,会先把结构化WebhookEvent(含 spend、max_budget、token、team_id、user_email、projected_spend 等字段)POST 到WEBHOOK_URL;若含email,则通过 SMTP 发送预算邮件(团队预算击穿还会触发send_team_budget_alert补充团队邮件,slack_alerting.py)。
扩展新预算告警类型
文档给出的扩展方式在源码中依然成立:新建一个继承BaseBudgetAlertType的类,实现get_event_message()与get_id(),再把它注册进get_budget_alert_type()的alert_types字典,并同步扩展该函数的Literal类型参数即可。策略对象是单例式的(工厂内直接实例化),因此无需依赖注入。
其他核心告警链路
除了预算告警,模块文档提到的“不同种类的告警”还覆盖以下链路,均复用同一套批量投递与去重基础设施:
慢请求与挂起请求
llm_too_slow:注册为 success 回调(ProxyConfig.update_values 会把response_taking_too_long_callback加入litellm成功回调)。响应耗时超过alerting_threshold即发送,消息包含模型、API Base、前 100 字符的 messages(受litellm.redact_messages_in_exceptions保护)以及各部署延迟(slack_alerting.py)。llm_requests_hanging:请求进入时写入AlertingHangingRequestCheck的内存缓存(TTL =alerting_threshold * 1.5 + 60s,保证跨过阈值后仍有检查窗口,hanging_request_check.py);后台任务每alerting_threshold / 2秒扫描最旧的 20 条记录(MAX_OLDEST_HANGING_REQUESTS_TO_CHECK),对既无request_status:success/fail标记又超龄的请求发送一条 Medium 级告警,并用alerted标志保证每次挂起只告警一次。
宕机告警(模型级 / 区域级)
- 模型级
outage_alerts:失败回调中仅统计 408 与 ≥500 的错误码,以model_id为缓存键在outage_alert_ttl(默认 60s)窗口内累积;错误数达到minor_outage_alert_threshold发“Minor Service Outage”(Medium 级),达到major_outage_alert_threshold发“Major Service Outage”(High 级),消息含提供商、API Base、错误码分布与最后检查时间(slack_alerting.py)。 - 区域级
region_outage_alerts:缓存键为provider + region,且额外要求至少2 个不同 deployment在同一区域报错才判定区域宕机,避免单个坏部署刷爆告警(slack_alerting.py)。
日报、消费报表与用户消费监控
daily_reports:每次成功/失败事件把“失败次数”“每 output token 延迟”累加进缓存(键形如{deployment_id}:failed_requests_daily_metrics);调度器每report_check_interval(默认 5 分钟,带 ±3s 抖动)检查一次,距上次发送达到daily_report_frequency(默认 12 小时)后推送“Top 5 失败最多部署 + Top 5 最慢部署”日报,并在多实例间用 Pod 锁(SLACK_DAILY_REPORT_LOCK_ID)防止重复发送,发完清空指标防止内存泄漏(slack_alerting.py)。spend_reports:send_weekly_spend_report(默认 7 天,格式如"7d")与send_monthly_spend_report按团队/标签汇总消费,用weekly_spend_report_sent_{起}_{止}之类的缓存键保证每期只发一次(slack_alerting.py)。- 用户消费:
user_spend_thresholds/user_spend_anomalies由send_user_spend_alerts每user_spend_check_interval驱动,阈值与异常判定逻辑在 user_spend_alerts.py,每个用户每个周期同样通过缓存键去重。
模型生命周期与审计事件
new_model_added在网关新增模型时推送模型信息与可直接复制的 OpenAI SDK 调用示例(slack_alerting.py);model_deprecation_warnings由独立后台循环驱动,先查“近一天是否已发过”(缓存键model_deprecation_alert_sent),再尝试获取 Pod 锁(SLACK_MODEL_DEPRECATION_LOCK_ID),确保多副本集群每天只发一条(slack_alerting.py);虚拟 Key、团队、内部用户的增删改事件通过send_virtual_key_event_slack统一输出操作者与参数摘要(slack_alerting.py)。
启动装配:ProxyConfig 如何接入告警
Gateway 侧的装配逻辑值得了解,因为它决定了告警何时“真正通电”。ProxyConfig在初始化时创建SlackAlerting实例(此时alerting=None,不启用任何通道,utils.py);当配置加载完成后调用update_values,若alerting含slack/ms_teams才把SlackAlerting注册进litellm.logging_callback_manager(add_litellm_callback处理失败事件,add_litellm_success_callback挂慢请求检查)——源码注释明确强调“告警关闭时绝不注册回调”(utils.py)。startup_event则负责按已启用的alert_types拉起后台任务:daily_reports启日报循环、llm_requests_hanging启挂起请求循环、model_deprecation_warnings启弃用检查循环(utils.py)。这也解释了为什么这些报表类告警在update_values中被再次注册回调:配置可在启动后热加载,注册时机必须兼容两种路径。
小结
LiteLLM Gateway 的 Slack 告警集成把“事件采集 → 去重降噪 → 批量投递 → 多通道分发”做成了完整管道:SlackAlerting.send_alert统一收口消息格式化与通道分发;batching_handler以 10 秒窗口 + 满额冲刷 + (url, alert_type) 合并保证高流量下不发洪水;budget_alert_types用策略模式把不同预算实体的告警差异收敛到工厂函数;SlackAlertingArgs则把所有可调阈值(TTL、窗口、倍数、间隔)显式化并支持环境变量覆盖。对运维者而言,关键调优点是alerting_threshold(慢请求灵敏度)、alert_to_webhook_url(频道分流)、alerting_args(去重窗口与宕机阈值)以及alert_type_config的 digest 模式;对扩展者而言,新增一类预算告警只需实现两个方法并注册进工厂字典。
核心文件索引:
- 模块说明:Readme.md
- 主实现:slack_alerting.py、batching_handler.py、budget_alert_types.py
- 类型与参数模型:types/integrations/slack_alerting.py
- 批量基类:custom_batch_logger.py
- Gateway 装配:proxy/utils.py
【免费下载链接】litellmThe fastest, litest AI Gateway. Rust core with Python SDK. Call 100+ LLM APIs in OpenAI (or native) format with cost tracking, guardrails, load balancing, and logging [Bedrock, Azure, OpenAI, Anthropic, OpenAI, VertexAI, vLLM, Nvidia NIM]项目地址: https://gitcode.com/GitHub_Trending/li/litellm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考