当前位置: 首页 > news >正文

AI应用的可观测性建设:从日志到链路追踪

AI应用的可观测性建设:从日志到链路追踪

前言

在大厂时,我们有专门的可观测性平台(日志、监控、链路追踪),我以为这是大公司的专利。创业后第一次遇到线上问题时,我们只有打印的日志,查了3个小时才定位到问题。

那次之后我意识到:可观测性不是奢侈品,而是必需品。一个没有可观测性的系统,就像一个没有仪表盘的飞机,出了问题都不知道在哪。

今天,分享我们是如何从零开始搭建 AI 应用的可观测性体系的。

一、可观测性的三大支柱

1.1 可观测性 vs 监控

维度传统监控可观测性
思维模式预设问题,针对检查未知问题,自由探索
关注点指标告警数据关联分析
调试方式告警 -> 排查数据 -> 探索 -> 根因
数据要求结构化指标多维度原始数据

1.2 三大支柱

可观测性 = 日志(Logs)+ 指标(Metrics)+ 链路追踪(Traces)
类型说明工具
日志离散的事件记录ELK, Loki
指标聚合的数值数据Prometheus, InfluxDB
链路追踪请求的完整路径Jaeger, Zipkin

二、日志体系建设

2.1 日志规范

import logging import json from datetime import datetime from enum import Enum class LogLevel(Enum): DEBUG = "DEBUG" INFO = "INFO" WARNING = "WARNING" ERROR = "ERROR" CRITICAL = "CRITICAL" class StructuredLogger: def __init__(self, service_name: str): self.service_name = service_name self.logger = logging.getLogger(service_name) self.logger.setLevel(logging.INFO) # JSON formatter handler = logging.StreamHandler() handler.setFormatter(self._create_formatter()) self.logger.addHandler(handler) def _create_formatter(self): """创建 JSON 格式化器""" def formatter(record): log_entry = { "timestamp": datetime.utcnow().isoformat(), "level": record.levelname, "service": self.service_name, "logger": record.name, "message": record.getMessage(), "module": record.module, "function": record.funcName, "line": record.lineno } # 添加异常信息 if record.exc_info: log_entry["exception"] = self.logger.exception( record.exc_info, exc_info=record.exc_info ) # 添加额外字段 if hasattr(record, "extra"): log_entry.update(record.extra) return json.dumps(log_entry) return formatter def log(self, level: LogLevel, message: str, **kwargs): """记录日志""" extra = {"extra": kwargs} if kwargs else {} getattr(self.logger, level.value.lower())(message, extra=extra) def info(self, message: str, **kwargs): self.log(LogLevel.INFO, message, **kwargs) def error(self, message: str, **kwargs): self.log(LogLevel.ERROR, message, **kwargs) def warning(self, message: str, **kwargs): self.log(LogLevel.WARNING, message, **kwargs)

2.2 AI 应用的日志最佳实践

class AILogger: def __init__(self, logger: StructuredLogger): self.logger = logger def log_model_request(self, request_id: str, model: str, prompt_length: int): """记录模型请求""" self.logger.info( "模型请求开始", request_id=request_id, model=model, prompt_length=prompt_length, event_type="model_request_start" ) def log_model_response(self, request_id: str, response_length: int, latency_ms: float, tokens_used: int): """记录模型响应""" self.logger.info( "模型请求完成", request_id=request_id, response_length=response_length, latency_ms=latency_ms, tokens_used=tokens_used, event_type="model_response_complete" ) def log_model_error(self, request_id: str, error: str, error_type: str): """记录模型错误""" self.logger.error( "模型请求失败", request_id=request_id, error=error, error_type=error_type, event_type="model_error" )

三、指标体系建设

3.1 指标采集

from prometheus_client import Counter, Histogram, Gauge, CollectorRegistry class AIMetrics: def __init__(self, registry: CollectorRegistry = None): self.registry = registry or CollectorRegistry() # 请求计数器 self.request_total = Counter( "ai_request_total", "Total number of AI requests", ["model", "status"], registry=self.registry ) # 请求延迟 self.request_duration = Histogram( "ai_request_duration_seconds", "AI request duration in seconds", ["model", "operation"], buckets=[0.1, 0.5, 1, 2, 5, 10, 30], registry=self.registry ) # Token 使用 self.tokens_used = Counter( "ai_tokens_used_total", "Total tokens used", ["model", "type"], # type: prompt/completion registry=self.registry ) # 当前请求数 self.active_requests = Gauge( "ai_active_requests", "Number of active requests", ["model"], registry=self.registry ) def record_request(self, model: str, status: str, duration: float, tokens: int): """记录请求指标""" self.request_total.labels(model=model, status=status).inc() self.request_duration.labels(model=model, operation="inference").observe(duration) self.tokens_used.labels(model=model, type="prompt").inc(tokens) def increment_active(self, model: str): """增加活跃请求数""" self.active_requests.labels(model=model).inc() def decrement_active(self, model: str): """减少活跃请求数""" self.active_requests.labels(model=model).dec()

3.2 自定义指标

class BusinessMetrics: def __init__(self, registry: CollectorRegistry): # 用户指标 self.active_users = Gauge( "app_active_users", "Number of active users", ["period"], # hourly, daily registry=registry ) # 功能使用指标 self.feature_usage = Counter( "feature_usage_total", "Total feature usage", ["feature_name"], registry=registry ) # 业务转化指标 self.conversion_rate = Gauge( "conversion_rate", "Business conversion rate", ["stage"], # trial_to_paid, etc registry=registry )

四、链路追踪

4.1 分布式追踪基础

from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.jaeger.thrift import JaegerExporter class TracingSetup: def __init__(self, service_name: str): self.service_name = service_name self.setup_tracing() def setup_tracing(self): """配置链路追踪""" # 创建 tracer provider provider = TracerProvider() # 添加 Jaeger exporter jaeger_exporter = JaegerExporter( agent_host_name="localhost", agent_port=6831, ) # 添加 batch span processor provider.add_span_processor( BatchSpanProcessor(jaeger_exporter) ) # 设置全局 tracer provider trace.set_tracer_provider(provider) # 获取 tracer self.tracer = trace.get_tracer(self.service_name) def create_span(self, name: str, attributes: dict = None): """创建 span""" return self.tracer.start_as_current_span( name, attributes=attributes or {} )

4.2 AI 应用的链路追踪

class AIDistributedTracing: def __init__(self, tracing: TracingSetup): self.tracing = tracing self.tracer = tracing.tracer def trace_ai_request(self, user_id: str, prompt: str, model: str): """追踪 AI 请求""" with self.tracer.start_as_current_span( "ai.request", attributes={ "user_id": user_id, "model": model, "prompt_length": len(prompt) } ) as span: try: # 模拟 AI 调用 response = self._call_model(prompt, model) # 记录响应信息 span.set_attribute("response_length", len(response)) span.set_attribute("status", "success") return response except Exception as e: span.set_attribute("status", "error") span.set_attribute("error.message", str(e)) raise def _call_model(self, prompt: str, model: str) -> str: """调用模型(实际应用中替换为真实调用)""" import time time.sleep(0.1) # 模拟调用延迟 return f"Response to: {prompt[:50]}..."

五、可观测性集成

5.1 统一日志上下文

from contextvars import ContextVar # 上下文变量 request_id: ContextVar[str] = ContextVar('request_id', default='') user_id: ContextVar[str] = ContextVar('user_id', default='') class UnifiedLogger: def __init__(self): self.logger = StructuredLogger("app") def _get_context(self) -> dict: """获取上下文信息""" return { "request_id": request_id.get(), "user_id": user_id.get() } def info(self, message: str, **kwargs): context = self._get_context() context.update(kwargs) self.logger.info(message, **context)

5.2 告警配置

# alertmanager.yml groups: - name: ai_alerts rules: - alert: HighErrorRate expr: rate(ai_request_total{status="error"}[5m]) > 0.1 for: 5m labels: severity: critical annotations: summary: "AI 请求错误率过高" description: "当前错误率: ${{ $value }}" - alert: HighLatency expr: histogram_quantile(0.95, ai_request_duration_seconds) > 10 for: 5m labels: severity: warning annotations: summary: "AI 请求延迟过高" description: "P95 延迟: ${{ $value }}s" - alert: APIKeyNearlyExhausted expr: api_usage_percentage > 90 for: 1h labels: severity: warning annotations: summary: "API 额度即将耗尽"

六、实战案例:问题排查

6.1 问题场景

用户反馈:"AI 客服响应很慢,有时还会失败"

6.2 排查流程

# 1. 查看错误率指标 # prometheus query: rate(ai_request_total{status="error"}[5m]) # 2. 查看延迟分布 # prometheus query: histogram_quantile(0.95, ai_request_duration_seconds) # 3. 查看具体错误日志 # loki query: {service="ai-service"} |= "error" | json # 4. 查看链路追踪 # jaeger query: service=ai-service operation=/api/chat

6.3 根因分析

通过分析发现:

  • 错误集中在某个时间段
  • 延迟突增与模型 API 响应时间相关
  • 链路追踪显示是模型 API 超时

七、最佳实践

7.1 日志最佳实践

  • 结构化日志:使用 JSON 格式,便于查询分析
  • 统一日志格式:所有服务使用相同的格式
  • 添加上下文:包含 request_id、user_id 等
  • 合理的日志级别:不要滥用 ERROR

7.2 指标最佳实践

  • 使用 RED 方法:Rate(速率)、Errors(错误)、Duration(延迟)
  • USE 方法:Utilization(利用率)、Saturation(饱和度)、Errors(错误)
  • 选择合适的聚合:根据业务选择百分位

7.3 链路追踪最佳实践

  • 添加关键属性:user_id、model、operation
  • 合理划分 span:不要过细也不要过粗
  • 采样策略:高流量时采样而非全量

八、总结

可观测性是保障 AI 应用稳定运行的基础。关键在于:

  1. 三大支柱结合:日志、指标、链路追踪缺一不可
  2. 结构化数据:便于查询和分析
  3. 上下文贯穿:让数据能够关联
  4. 持续改进:根据问题不断优化

记住:没有可观测性,就没有可靠性。让我们一起打造可观测的 AI 应用!

http://www.jsqmd.com/news/838846/

相关文章:

  • AI智能体技能库:模块化设计、标准化实现与LangChain集成实战
  • 保姆级教程:用Vue3+webrtc-streamer搞定海康/大华监控的Web实时播放(附完整代码)
  • Universal-IFR-Extractor终极指南:三步解密EFI/UEFI固件内部表单
  • 别再到处找破解版了!手把手教你用AnyLogic 8.8.4社区版/学生版合法免费建模
  • 一份给成都业主的中央空调服务商挑选指南 - 速递信息
  • MultiFunPlayer完整指南:3分钟学会设备与媒体完美同步,打造沉浸式娱乐体验
  • 3步快速安装Android应用的终极指南:告别模拟器时代
  • Lumerical FDTD 仿真进阶:手把手教你用矩形监视器“拼”出圆形监视器(附完整脚本)
  • 2026南昌乱账整理TOP5一文看懂 | 5维评测+老账还原+多年凌乱账+收费拆解 - 资讯焦点
  • 高效跨平台图片预览解决方案:Windows HEIC缩略图插件深度解析
  • 手机免费一键去水印App如何选?2026热门去水印工具排行对比指南 - 爱上科技热点
  • RV1126 NPU部署ResNet50全流程:从PyTorch训练到嵌入式板端推理
  • Minimax算法在技能学习中的应用:构建抗风险技术成长路径
  • 如何在Windows上快速构建完整的词法分析与语法解析工具链
  • 2026年5月水处理荧光法溶氧监测仪国产口碑品牌盘点 - 仪表品牌榜
  • 爱彼离岸型走时忽快忽慢?北京专业维修师傅手记:从3120到4302机芯,调校差别的背后真相 - 亨得利官方维修中心
  • magnetW磁力聚合搜索工具:一站式资源发现神器
  • 2026年南昌资深账务合规机构深度榜单 | 5维评测+服务体系+价格对比+15年深耕 - 资讯焦点
  • 工业级PCB缺陷检测革命:1500对图像数据集如何解决传统质量检测难题
  • 2026年贵阳地摊货源、百货批发、不锈钢厨具怎么选?思洪多元深度评测指南 - 精选优质企业推荐官
  • 考研失利后转战海外:2026年深圳硕士留学申请指南与机构选择建议 - 品牌2025
  • 别再死记硬背NAT命令了!用eNSP模拟真实企业网,手把手带你搞懂静态NAT、地址池和Easy-IP
  • 中小企业如何通过Taotoken的Token Plan套餐控制AI集成成本
  • 智能宏插件终极指南:告别手动操作,实现游戏技能全自动化
  • 2026美国高端留学中介推荐:美国本科高端定制与藤校申请中介精选 - 品牌2025
  • ai20260518 - 小镇
  • 第99篇:Vibe Coding时代:企业级 AI Coding 平台交付模板,解决从 Demo 到落地没有标准方案的问题
  • 学生专业护眼台灯怎么选?独语A8以0.95重新定义舒适光 - 资讯焦点
  • 多路由器组网实战:让打印机在复杂网络下轻松共享
  • ESP32蓝牙音频革命:从零打造你的无线音乐系统