Abhinav0905/Incidentlens

GitHub: Abhinav0905/Incidentlens

IncidentLens 从日志、架构元数据和部署事件中自动重建分布式系统故障,生成带有证据溯源的 3D 可视化回放和结构化分析报告。

Stars: 1 | Forks: 0

# IncidentLens 为分布式系统提供有据可查的故障复盘。 IncidentLens 会读取架构元数据、日志、指标和部署事件,然后重建故障期间发生的一切:哪个服务最先发生故障、在此之前发生了什么变更、故障是如何在依赖图中蔓延的,以及它对客户造成了什么影响。最终呈现的是一段电影般的 3D 故障回放——在您的架构上空推进的连续镜头,伴随着逐帧旁白——外加一份值班工程师可直接执行的简报。只需将其指向一个代码库及其日志文件,它就能在真实故障发生时自动完成这一切。 要在演示或黑客松上展示该项目?请使用这份 [三分钟运行手册](HACKATHON_RUNBOOK.md)。 ## 本项目的核心理念 **推测出的根本原因绝不会被当作事实呈现。** 生产环境故障往往错综复杂。日志不完整、时钟漂移、多个问题同时爆发。因此,IncidentLens 产生的每一个结论都附带状态和置信度得分,且每一项断言都可追溯到支持该断言的证据 ID: - `confirmed` — 遥测数据直接显示其发生过 - `inferred` — 证据指向这一结论,但应由人工进行验证 - `unknown` — 确实存在这种可能性,但现有遥测数据无法得出定论 分析过程还会列出它*无法*看到的内容:未上报遥测数据的服务、缺失的审计日志、空缺的变更历史。让系统知道自己“不知道什么”,正是本工具的核心意义所在。 ## 快速开始 可在 macOS、Linux 或 Windows(通过 WSL2)上运行。IncidentLens 核心和 Studio 支持 Python 3.10+;可选的 Genblaze 集成需要 Python 3.11+。 ``` git clone https://github.com/Abhinav0905/Incidentlens.git cd Incidentlens python -m venv .venv source .venv/bin/activate pip install -e ".[dev]" incidentlens serve ``` 打开 http://127.0.0.1:8000,选择一个场景,点击 **Reconstruct incident**。或者使用 Docker: ``` docker compose up --build ``` ## 产出内容 对于每次故障,引擎都会生成一份单一的 `IncidentAnalysis` 文档: - 一条从健康状态到故障,再到(如果观测到)恢复的时间线 - 带有状态、置信度和支持性证据 ID 的排序后假设 - 传播链:哪个服务导致了哪个服务降级,以及通过何种机制 - 客户影响(仅基于直接证据陈述) - 缺失的证据——响应人员应优先填补的空白 - 推荐的检查和修复措施,按优先级排序,并附有明确的风险说明 - 工程师简报和高管摘要 - 驱动 UI 中动画架构视图的回放脚本 UI 会像播放录像一样重现故障:随着时间线的推进,节点会从健康变为警告再到严重;当故障穿过依赖边时,传播路径会亮起;此外,字幕会将每一帧与其证据关联起来。 ## 内置场景 代码库附带了七个合成的故障场景。它们的存在是为了让您看到引擎 如何针对完全不同的故障进行推理——相同的代码,没有针对特定场景的分支。 ### 同一应用,四种故障 其中四个场景在*相同*的服务图和*相同*的 16 模块代码图上运行: `hary-platform`,一个 AI 助手。只需了解一次架构,然后观察每次其不同 部分的故障。其核心在于,分析过程并未针对特定故障进行调优—— 改变的仅仅是遥测数据。 | 场景 | 故障内容 | 引擎定位 | 深度 | | --- | --- | --- | --- | | `rate-limit-exhaustion` | 流量激增导致 Redis 连接池耗尽;限流器触发失败关闭(fail closed),每个请求在到达图之前就被以 `429` 拒绝 | `middleware.rate_limit` | 前门 | | `pii-guardrail-crash` | 异常的 unicode 序列导致 PII 脱敏崩溃。格式良好的请求继续工作,因此只有*部分*流量中断 | `hary.guardrails.pii._get_presidio` | 流水线中部 | | `gateway-auth-rejection` | 新配置的虚拟密钥在两种身份验证方案下均被拒绝(返回 `401`);断路器打开,助手面板变暗 | `hary.models.llm_factory.get_llm_for_tier` | LLM 客户端 | | `agentic-retry-exhaustion` | prompt-pack 的变更导致 Agent 误将 tool-call payload 视为可重试。它耗尽了重试预算,截断了部分答案进行补救,并返回 **HTTP 200** | `hary.graph.nodes.agent.AgentNode.__call__` | agentic 节点 | 最后一种情况很有意思:没有任何 5xx 错误,所有可用性仪表板 保持全绿。HTTP 成功率保持在 99.6%,但部分答案补救的比例从 0.3% 攀升至 44.8%,p95 延迟翻了三倍,点踩量从每小时 3 次增加到每小时 41 次。这种隐蔽的质量倒退,是日志尾部无法向您展示的故障。 `rate-limit-exhaustion` 被定位到一个模块而非函数,因为 `middleware.rate_limit` 在内置的代码图中没有对应的符号。这是刻意为之: 工具会退化为模块级别定位,而不是凭空捏造一个它无法支持的 stack frame。 ### 另外三种架构 为了证明分析过程未针对单一系统进行调优,我们还提供了:`cache-stampede`(冷缓存导致请求涌入索引层,直到数据库饱和,随后自行恢复——不涉及部署)、`checkout-secret-rotation`(部署重新指向数据库密钥,导致订单 worker 积压),以及 `queue-poison-message`(schema 变更产生了一条无法解码的记录,消费者陷入崩溃循环)。 您可以运行其中任何一个。由于遥测数据不同,输出结果也会有所不同——这就是 我们要展示的效果。 ## 实时观察您自己的系统 实时循环不需要代理,也不需要修改代码——一个日志文件就是全部的集成接口: ``` pip install -e ".[studio]" # plus ffmpeg incidentlens discover . # scan the repo -> architecture proposal + config incidentlens watch --config incidentlens.config.json ``` `discover` 从您的代码检出中提取服务图(包括 docker-compose、服务目录、配置交叉引用、在设置中发现的外部网关——这两个文件都是可编辑的建议方案)。它还会使用 `ast` 扫描每个 Python 服务的*内部*——LangGraph 节点连线、FastAPI 入口点、middleware 链、共享的 LLM/client 模块——以便随后视频可以深入到发生故障的服务内部,并追踪请求在各个内部阶段的流转。随后,`watch` 会持续监控配置的日志文件;当出现大量 error 级别的日志时,它会根据最近的遥测数据重建故障,并自动生成电影和简报。`incidentlens analyze --logs svc=incident.log` 则是在事后执行相同操作。详情:[docs/LIVE.md](docs/LIVE.md)。 在 [demo/model-id-typo/](demo/model-id-typo/) 中提供了一个可运行的端到端示例:`FAST_TIER_MODEL_ID` 中的单字符拼写错误(`vendor` 错写为 `modelhst`)导致每次 LLM 调用失败。有关该故障的任何信息都不是硬编码的——`incidentlens analyze --config demo/model-id-typo/incidentlens.config.json` 会读取原始日志,对其进行重建,将记录的故障归因于 `hary.models.llm_factory`,并使用静态调用图将 `get_llm_for_tier` 识别为候选故障点。将日志文件替换为您自己服务的真实日志,它的执行方式也是一样的。 ## Studio:将故障变成一部电影 `incidentlens[studio]` 会将故障渲染为带有旁白的 MP4——一个连续的 3D 镜头,而不是幻灯片。透视摄像机在架构中穿梭,聚焦于旁白正在讲述的内容;服务是地平网格上的方块,随着状态的变化而升起、变色和跳动;请求流量以粒子的形式沿着依赖边流动,当故障穿过这些边时,粒子会变热并反向倒流;冲击波从状态变化处向外扩散;热点区域会产生光晕效果。所有这些都是用纯 Python 渲染并通过 ffmpeg 以 1080p30 的帧率输出的——不需要浏览器,也不需要 GPU——并且它是确定性的:视觉效果绝不会展示分析中未曾发现的任何内容。旁白是唯一具有生成性质的部分,由 LLM 编写故事,再由文本转语音的嗓音朗读出来;对于原因的陈述,仅采用证据指向的方向,而非将其作为绝对事实。 **深入剖析。** 当发生故障的服务的内部流水线已知时(参见下文的扫描器),镜头不会仅仅停留在服务边界。在故障发生的节拍上,摄像机会逐阶段跟踪请求——已记录或推断出的遍历路径显示为青色,处于休眠状态的上下文显示为暗色,而已记录的故障阶段则显示为红色。当存在 `incidentlens.codegraph.json` 时,它随后会打开按包分组的完整模块蓝图,建立每个模块和边的联系,滑入发生故障的包中,并叠加故障情况:日志归因的模块显示为红色,结构依赖风险显示为琥珀色,仅有静态上下文的部分保持暗色。最后一级会打开聚焦的函数蓝图,其中方法嵌套在类中,类嵌套在模块中。它最终停留在标记为琥珀色的候选函数上,并明确将其标记为静态推断,而非确认的运行时 stack frame。如果没有代码库的代码图,先前那种紧凑的依赖关系和调用者/被调用者视图将作为后备方案。 ## 将代码视为网络 第三层深度。`incidentlens graph` 使用纯 `ast` 遍历代码库(不进行任何 import,不执行任何代码),并将每个服务的*代码*渲染为依赖网络——一个独立的 HTML 文件,无需 CDN: ``` incidentlens graph . --out code-graph.html ``` 扫描现在将两个图合二为一,且在一次遍历中构建完成: - **模块网络** — 每个内部模块都是一个节点;import 和*已解析的调用*(通过 import 别名,包含符号)是边。每个模块还带有其 **fan-in / fan-out**、其 **爆炸半径**(传递依赖于它的模块数量),以及它是否处于 import **环**中。 - **调用图** — `module.Class.method` / `module.function` 层。节点是函数、方法和类;它们之间的边是已解析的调用,本着 `pyan` 的精神尽力解析:`from x import f` 的来源、模块别名调用、`self.method()`、单次赋值的局部类型推断(`x = Thing(); x.foo()`)、构造函数调用,以及静态扫描通常会遗漏的**动态导入**(`importlib.import_module("a.b")`, `__import__`)。 在交互视图中点击任何节点,面板会直接回答凌晨两点(排查问题)时的疑问:**谁调用了它** 以及 **它调用了什么** —— `hary.guardrails.pii ← input_guardrail (scan)`,`hary.models.llm_factory.get_llm ← agent.AgentNode.__call__`。切换 **module ⁄ symbol** 以从文件深入到函数;节点按爆炸半径确定大小,处于环中的成员会被琥珀色虚线环绕,颜色则遵循其功能角色(endpoint · client · config · middleware · graph-node · logic)。支持搜索、缩放、拖拽;可在不同服务间切换。传入 `--analysis INC-….analysis.json`,故障就会落在地图上:遍历过的模块会被青色环标记,被关联错误日志指出的模块显示为红色,而静态选定的候选函数则被琥珀色虚线环绕。 ### Mermaid,层级分明且带有颜色标记 `--mermaid` 还会输出一张 [Mermaid](https://mermaid.js.org) 图表(`.mmd` 源文件外加一个一键查看的 HTML)——可以将其粘贴到 PR、运行手册、GitHub 或 Mermaid Chart 连接器中: ``` incidentlens graph . --mermaid --level module # package-grouped overview incidentlens graph . --mermaid --level symbol --focus hary.models.llm_factory ``` 在 `--level symbol` 级别下,图表是真正的层级结构——**方法嵌套在 class 子图中,class 嵌套在 module 子图中**——每个角色都带有 `classDef` 颜色,并且会标记出耦合集群。由于真实服务的完整调用图过于密集,难以整体阅读,symbol 级别会使用 `--focus`(某个模块或 `module.Class.method`)限定范围,仅显示其直接的调用者和被调用者。当 `discover` 已经写入 `incidentlens.codegraph.json` 时,`analyze` 和 `watch` 除了视频之外,还会输出交互式 HTML 和以故障为核心的 `.mmd` 文件。 ``` pip install -e ".[studio]" # plus ffmpeg incidentlens studio gateway-auth-rejection --out incident.mp4 ``` 配置了 `OPENAI_API_KEY` 后,默认的 `auto` 语音会使用 OpenAI 平稳的 神经 TTS;如果没有密钥,则会回退到本地环境。当发生故障的服务存在 调用图时,旁白会指明候选的**函数**、谁调用了它、 它的结构爆炸半径,以及它所处的任何环——而不会声称 这种静态归因是一个运行时的 stack frame。添加 `--intro-video PATH` 可以在开头加上一段简短的 Sora/Veo/Runway 片头,而无需将 技术图表交给生成式模型处理。有关语音控制、长篇 合成、Genblaze 溯源以及 Backblaze B2 发布的信息,请参见 docs/STUDIO.md](docs/STUDIO.md)。 ### Genblaze 媒体溯源 在 Python 3.11 或更高版本上安装附加的 Genblaze 集成: ``` pip install -e ".[studio,genblaze]" # plus ffmpeg export OPENAI_API_KEY=... export INCIDENTLENS_GENBLAZE_VOICE=coral incidentlens studio gateway-auth-rejection \ --voice genblaze \ --publish-genblaze \ --out incident.mp4 ``` 这会通过单个 Genblaze `OpenAITTSProvider` 管道处理每一段旁白节拍,将 prompt 可见性归类为私有,并且 将生成的 WAV 文件绑定到 SHA-256 声明。这种分类属于 溯源元数据,而非加密或脱敏:导出的标准 旁白清单仍然包含口述的旁白文本,因此请像保护任何 故障产物一样保护它。Genblaze 0.3.x 目前接受 `cor` 但不接受 较新的 `marin` 或 `cedar` 语音名称。对于 `marin` 和兼容的 自定义语音端点,直接使用 `--voice openai` 仍然是 默认途径。 `--publish-genblaze` 会保持 `incident.mp4` 在字节层面上完全不变,并在其旁边写入 `incident.genblaze.json`。如果使用 `--voice genblaze`,它还会写入一份 旁白清单。该旁白清单在标准上是可验证的,但其 节拍级别的 `file://` URL 指向的是渲染后会被删除的临时 WAV 文件;除非将这些音频文件单独 持久化保存,否则事后无法再次获取并进行验证。 要通过 Genblaze 的 B2 存储桶进行发布: ``` export B2_BUCKET=incidentlens-demo export B2_REGION=us-west-004 export B2_KEY_ID=... export B2_APP_KEY=... incidentlens studio gateway-auth-rejection \ --voice genblaze \ --upload-genblaze-b2 \ --out incident.mp4 ``` 这会使用层级化的 key 上传原始的 MP4 和标准清单, 然后将其嵌入到本地的 MP4 中。因此,嵌入后的本地副本 与存储在 B2 中记录了 SHA 的原始对象在字节上是不同的。 `B2_APPLICATION_KEY` 和标准的 `B2_ENDPOINT_URL` 仍被接受为 旧版别名。旧的直接 `--upload-b2` 命令被单独保留, 并且仍然会返回一个有时限的预签名 URL。 ## 引擎如何进行推理 默认引擎(`engines/deterministic.py`)是基于规则的,并且在源码中有完整的文档记录。没有机器学习,没有外部调用,对于相同的输入可重现输出。流水线如下: 1. **信号分类。** 每个遥测事件都会使用日志级别和指标行为,被标记为 baseline、change、failure、warning 或 recovery。 2. **指标异常检测。** 当指标序列上升至其自身序列内基线的 3 倍或以上(或下降至 1/3 或以下)时,即被视为异常;针对错误计数、≥95% 的饱和度、积压和极端延迟,会采用单点启发式算法。在出现异常后又恢复到接近基线水平的序列标志着系统已恢复。 3. **起源点识别。** 故障信号会在 90 秒的时间窗口内按服务进行合并;最早发生故障的服务即为起源点。 4. **变更关联度。** 根据起源故障对变更进行评分:基础分为 0.45,如果故障在 5 分钟内发生(15 分钟内为 +0.15)则加 +0.25,同服务加 +0.10,关键字匹配度加 +0.10(变更和故障中均出现 credential、capacity 或 config 等术语),上限为 0.95。如果分数低于 0.55,则该变更被报告为偶发事件,而非因果关系。 5. **传播映射。** 从起源点开始遍历依赖图。依赖于已降级服务的降级服务成为上游依赖步骤;被已降级服务所依赖的降级服务成为下游压力步骤。机制(超时、负载、积压)来自于对关联证据应用的关键字规则。 6. **假设组装。** 根本原因和传播状态为 `inferred`(传播得分上限为 0.88)。只有当面向用户的服务显示出直接的故障证据时,客户影响才被标记为 `confirmed`。遥测无法回答的问题会以置信度 0.0 输出为 `unknown`。 7. **来源验证。** 任何结论引用的每个证据 ID 都必须存在于证据集中,否则引擎将抛出异常。绝不出现悬空的引用。 阈值是模块顶部的常量,并附有解释每个选择的注释。不认同某个设定?非常欢迎提交 pull request。 ## API ``` GET /api/v1/health GET /api/v1/scenarios # list bundled scenarios GET /api/v1/scenarios/{name} # architecture + raw events for one scenario POST /api/v1/incidents/analyze # {"scenario": "checkout-secret-rotation"} ``` `analyze` 返回完整的 `IncidentAnalysis` JSON。未知场景返回 404。没有故障信号的遥测数据返回 422——在没有观察到故障的情况下,绝不凭空捏造故障。 ## 架构 ``` Telemetry sources (logs, metrics, deployments, architecture metadata) │ ▼ Connector interface ──► canonical event model │ ▼ Analysis engine (signals → anomalies → origin → change correlation → propagation → hypotheses → provenance check) │ ▼ IncidentAnalysis ──► replay UI · briefings · action plan · evidence record ``` ``` src/incidentlens/ ├── api.py FastAPI routes ├── cli.py `incidentlens serve` ├── domain/ models + errors ├── connectors/ telemetry integrations (synthetic included) ├── engines/ analysis engines (deterministic included) ├── services/ orchestration ├── data/scenarios/ bundled incidents (scenario.json, architecture.json, events.json) └── static/ replay UI ``` ## 编写连接器 连接器的作用是将遥测源适配到标准事件模型: ``` from incidentlens.connectors.base import TelemetryConnector from incidentlens.domain.models import ArchitectureGraph, TelemetryEvent class MyConnector(TelemetryConnector): def fetch_events(self) -> list[TelemetryEvent]: ... def fetch_architecture(self) -> ArchitectureGraph: ... ``` 期待:OpenTelemetry、Datadog、CloudWatch、Grafana Loki、Elastic、Kubernetes 事件、GitHub Actions 部署。一个新的合成场景也是非常棒的首次贡献——只需在 `data/scenarios//` 中放入三个 JSON 文件,完全不需要 Python。 ## 限制 当前版本的诚实清单: - **仅限合成数据。** 所有七个场景均为手工构建。真实的连接器是路线图上的首要任务。 - **未处理时钟偏移。** 事件排序信任源时间戳。真实系统在进行跨源排序获得信任之前,需要具备对偏移的容忍度。 - **启发式阈值。** 3 倍的异常比率和 5/15 分钟的变更窗口是合理的默认值,而非通过学习得出的数值。 - **单一起源假设。** 并发的独立故障按最早发作时间排序;目前尚未对真正的多起源故障进行建模。 - **无身份验证,单一租户。** 目前这只是一个本地分析工具,而不是托管服务。 ## 路线图 1. OpenTelemetry 连接器(输入真实遥测数据,进行相同的分析) 2. Studio 视频中状态间的动态过渡(目前是节拍之间的硬切换) 3. 事件排序中的时钟偏移容忍度 4. 回放导出(可共享的故障录像) 5. 根据分析文档生成复盘报告草稿 6. 基于策略的操作审批 ## 开发 ``` make install # editable install with dev deps make lint # ruff + mypy (strict) make test # pytest make run # serve on 127.0.0.1:8000 ``` CI 在每次 push 时都会运行同样的三项检查。 ## 贡献 · 安全 · 许可证 参见 [CONTRIBUTING.md](CONTRIBUTING.md) 和 [SECURITY.md](SECURITY.md)。采用 Apache-2.0 许可证。
标签:API集成, 分布式系统, 可观测性, 响应大小分析, 故障排查, 根因分析, 自动化payload嵌入, 请求拦截, 运维, 逆向工具