log_schema.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """结构化日志模型 + SLS 索引配置
  2. 设计目标:
  3. 1. 统一日志字段,消除当前 free-form dict 导致的查询困难
  4. 2. 按查询模式设计 SLS 键值索引,避免全表扫描
  5. 3. 支持 trace_id 串联完整调用链
  6. ====== SLS 索引配置说明 ======
  7. 全文索引(分词查询):
  8. 字段 分词符 包含中文
  9. ───────────────────────────────────────────
  10. message ,;=|'"\t\r\n ✓
  11. error_message ,;=|'"\t\r\n ✓
  12. 键值索引(精确匹配 / 范围 / 聚合):
  13. 字段 类型 别名 说明
  14. ────────────────────────────────────────────────────
  15. level text log_level 日志级别,精确匹配 + 统计
  16. category text log_category 日志分类,精确匹配
  17. event text 事件名,精确匹配 + 前缀查询
  18. trace_id text 分布式追踪 ID,精确匹配
  19. task_name text 任务名,精确匹配 + 前缀查询
  20. agent_name text Agent 名,精确匹配
  21. tool_name text Tool 名,精确匹配
  22. duration_ms long 耗时(毫秒),范围查询 + 统计
  23. status text 执行状态,精确匹配
  24. error_type text 异常类型,精确匹配 + 聚合
  25. extra json 扩展字段,子字段可索引
  26. 统计优化:
  27. duration_ms 开启统计(min / max / avg / sum / distinct_count)
  28. level 开启统计(distinct_count)
  29. 控制台创建 SQL 建索引(参考,实际通过控制台或 API 创建):
  30. -- 全文索引
  31. {"Line": {"Token": [",;=|'\"\t\r\n"]}, "Keys": {"message": {"Token": [",;=|'\"\t\r\n"], "CaseSensitive": false, "Chn": true}, "error_message": {"Token": [",;=|'\"\t\r\n"], "CaseSensitive": false, "Chn": true}}}
  32. -- 键值索引
  33. Key=level Type=text
  34. Key=category Type=text
  35. Key=event Type=text
  36. Key=trace_id Type=text
  37. Key=task_name Type=text
  38. Key=agent_name Type=text
  39. Key=tool_name Type=text
  40. Key=duration_ms Type=long Stats=true
  41. Key=status Type=text
  42. Key=error_type Type=text
  43. Key=extra Type=json
  44. ====== 典型查询场景 ======
  45. 1. 追踪某次任务全链路:
  46. trace_id:"Task-20260630143000-abc123" | SELECT * ORDER BY __time__
  47. 2. 统计各 Agent 执行耗时 P50/P99:
  48. agent_name:* | SELECT agent_name, approx_percentile(duration_ms, 0.5) AS p50, approx_percentile(duration_ms, 0.99) AS p99 GROUP BY agent_name
  49. 3. 告警:最近 5 分钟 ERROR 数量:
  50. level:ERROR AND __time__ > now() - 300
  51. 4. 某任务最近 1 小时成功率:
  52. task_name:"content_generation" AND __time__ > now() - 3600 | SELECT status, count(*) GROUP BY status
  53. 5. 按异常类型聚合排障:
  54. level:ERROR AND __time__ > now() - 3600 | SELECT error_type, count(*) AS cnt GROUP BY error_type ORDER BY cnt DESC
  55. 6. 慢任务检测(耗时 > 阈值):
  56. category:task AND duration_ms > 300000 | SELECT task_name, trace_id, duration_ms ORDER BY duration_ms DESC
  57. """
  58. from __future__ import annotations
  59. from enum import Enum
  60. from typing import Any, Dict, Optional
  61. from pydantic import BaseModel, Field
  62. # ==================== 枚举 ====================
  63. class LogLevel(str, Enum):
  64. """日志级别"""
  65. DEBUG = "DEBUG"
  66. INFO = "INFO"
  67. WARN = "WARN"
  68. ERROR = "ERROR"
  69. FATAL = "FATAL"
  70. class LogCategory(str, Enum):
  71. """日志分类 —— 按系统边界划分,用于快速过滤"""
  72. SYSTEM = "system" # 启动、关闭、心跳
  73. TASK = "task" # 任务调度生命周期
  74. AGENT = "agent" # Agent 执行(规划、推理、反思)
  75. TOOL = "tool" # Tool 调用(HTTP、DB 查询等)
  76. HTTP = "http" # 入站 HTTP 请求
  77. DB = "db" # 数据库操作
  78. SECURITY = "security" # 安全相关(认证、鉴权、告警)
  79. class LogStatus(str, Enum):
  80. """执行状态"""
  81. SUCCESS = "success"
  82. FAILED = "failed"
  83. TIMEOUT = "timeout"
  84. CANCELLED = "cancelled"
  85. PENDING = "pending"
  86. # ==================== 结构化日志模型 ====================
  87. class LogRecord(BaseModel):
  88. """统一日志记录 —— 所有日志推送都必须使用此模型
  89. 使用方式:
  90. record = LogRecord(
  91. level=LogLevel.INFO,
  92. category=LogCategory.TASK,
  93. event="task_started",
  94. message="内容生成任务开始执行",
  95. trace_id="Task-20260630143000-abc123",
  96. task_name="content_generation",
  97. extra={"topic": "AI 安全", "word_count": 5000},
  98. )
  99. await log_service.log(record)
  100. """
  101. # ── 核心字段 ──
  102. level: LogLevel = Field(default=LogLevel.INFO, description="日志级别")
  103. category: LogCategory = Field(default=LogCategory.SYSTEM, description="日志分类")
  104. event: str = Field(default="", description="事件名,如 task_started / agent_think / tool_error")
  105. message: str = Field(default="", description="人类可读描述,用于全文检索")
  106. # ── 追踪字段 ──
  107. trace_id: str = Field(default="", description="分布式追踪 ID,串联完整调用链")
  108. span_id: str = Field(default="", description="当前 span ID,可选")
  109. # ── 业务标识 ──
  110. task_name: str = Field(default="", description="任务名,如 content_generation")
  111. agent_name: str = Field(default="", description="Agent 名,如 ContentWriter")
  112. tool_name: str = Field(default="", description="Tool 名,如 WebSearchTool")
  113. # ── 性能字段 ──
  114. duration_ms: int = Field(default=0, description="执行耗时(毫秒),用于 P50/P99 分析")
  115. # ── 状态与异常 ──
  116. status: LogStatus = Field(default=LogStatus.SUCCESS, description="执行状态")
  117. error_type: str = Field(default="", description="异常类名,如 TimeoutError")
  118. error_message: str = Field(default="", description="异常消息全文,用于全文检索")
  119. # ── 扩展字段 ──
  120. extra: Dict[str, Any] = Field(default_factory=dict, description="业务自定义 KV,JSON 索引可查询子字段")
  121. def to_sls_items(self) -> list[tuple[str, str]]:
  122. """转为阿里云 SLS LogItem 的 contents 格式 [(key, value), ...]
  123. SLS API 要求所有值都是 string,此方法负责序列化。
  124. """
  125. return [
  126. ("level", self.level.value),
  127. ("category", self.category.value),
  128. ("event", self.event),
  129. ("message", self.message),
  130. ("trace_id", self.trace_id),
  131. ("span_id", self.span_id),
  132. ("task_name", self.task_name),
  133. ("agent_name", self.agent_name),
  134. ("tool_name", self.tool_name),
  135. ("duration_ms", str(self.duration_ms)),
  136. ("status", self.status.value),
  137. ("error_type", self.error_type),
  138. ("error_message", self.error_message),
  139. # extra 存 JSON 字符串,SLS json 类型索引可解析
  140. ("extra", json.dumps(self.extra, ensure_ascii=False) if self.extra else "{}"),
  141. ]
  142. # ==================== SLS 索引配置(文档 / 参考) ====================
  143. SLS_INDEX_CONFIG = {
  144. "logstore": "long_articles_agentic_aupply",
  145. "ttl_days": 30,
  146. "full_text_index": {
  147. "case_sensitive": False,
  148. "include_chinese": True,
  149. "delimiter": ",;=|'\"\t\r\n ",
  150. "fields": ["message", "error_message"],
  151. },
  152. "key_value_index": [
  153. {"key": "level", "type": "text", "alias": "log_level", "stats": True},
  154. {"key": "category", "type": "text", "alias": "log_category"},
  155. {"key": "event", "type": "text"},
  156. {"key": "trace_id", "type": "text"},
  157. {"key": "task_name", "type": "text"},
  158. {"key": "agent_name", "type": "text"},
  159. {"key": "tool_name", "type": "text"},
  160. {"key": "duration_ms", "type": "long", "stats": True},
  161. {"key": "status", "type": "text"},
  162. {"key": "error_type", "type": "text"},
  163. {"key": "extra", "type": "json"},
  164. ],
  165. }
  166. # ── 惰性 json import(避免循环) ──
  167. import json # noqa: E402