2. 可观测性三大支柱:日志、指标、链路追踪在多租户场景下的基础概念

在多租户系统中,可观测性的三大支柱——日志(Logging)、指标(Metrics)和链路追踪(Tracing)——不再是独立的工具,而是必须与租户隔离策略深度绑定的数据模型。每个支柱都需要在采集、存储、查询和展示层面嵌入租户上下文,否则跨租户的数据泄露或故障边界模糊将不可避免。

2.1 三大支柱的核心定义与租户隔离挑战

下表从数据特征、隔离粒度、常见陷阱三个维度对比三大支柱在多租户环境下的基础差异:

支柱 数据特征 租户隔离粒度 常见隔离陷阱
日志 离散、非结构化或半结构化文本,记录事件详情 每条日志记录必须携带 tenant_id 标签 日志聚合时丢失租户字段;全文索引跨租户泄露
指标 时序数值,聚合后反映系统状态 指标标签(Label)中注入租户维度 全局聚合指标掩盖单租户异常;高基数标签导致存储爆炸
链路追踪 请求级调用链,包含 Span 上下文 Span 中注入 tenant_id 上下文,传播至下游服务 异步消息丢失租户上下文;跨服务边界未透传租户标识
关键原则: 在多租户可观测性中,租户标识必须作为一等公民(First-Class Citizen)嵌入数据模型,而非事后通过正则或模糊匹配提取。

2.2 日志(Logging):结构化与租户标签

多租户日志的核心要求是结构化。非结构化日志无法可靠地按租户过滤,而结构化日志通过键值对(Key-Value)或 JSON 格式,将 tenant_id 作为必填字段。

// 结构化日志示例(JSON 格式)
{
  "timestamp": "2025-03-21T10:15:30.123Z",
  "level": "ERROR",
  "tenant_id": "tenant-abc-123",
  "service": "payment-service",
  "trace_id": "trace-xyz-789",
  "message": "Payment gateway timeout",
  "error_code": "PAY_TIMEOUT",
  "duration_ms": 5002
}

日志隔离的三大基础策略:

  • 采集层隔离: 每个租户的日志写入独立索引(如 Elasticsearch 的 logs-{tenant_id}-yyyy-MM-dd),避免跨租户查询性能干扰。
  • 存储层隔离: 使用租户级保留策略(Retention Policy),高 SLA 租户日志保留 90 天,普通租户保留 30 天。
  • 查询层隔离: 查询接口强制校验用户权限,仅返回其所属租户的日志记录。
避坑提示: 避免在日志中打印租户敏感数据(如 API Key、个人身份信息)。即使日志按租户隔离,存储介质的安全等级仍需统一。

2.3 指标(Metrics):标签基数与租户维度

指标在多租户场景下最大的挑战是标签基数(Cardinality)。每个租户作为一个标签值加入指标,当租户数量达到数千甚至数万时,时序数据库的索引和存储压力会急剧上升。

推荐的指标隔离模型:

  • 租户级指标: 使用 tenant_id 标签,仅记录关键业务指标(如请求量、错误率、P99 延迟),避免记录细粒度调试指标。
  • 全局聚合指标: 去除 tenant_id 标签,用于基础设施监控(如 CPU、内存、网络吞吐),不暴露租户维度。
  • 租户分组指标: 按租户等级(Tier)或业务线聚合,降低标签基数。
# Prometheus 指标示例(带租户标签)
# 租户级请求量
http_requests_total{tenant_id="tenant-abc-123", status="200"} 1024
http_requests_total{tenant_id="tenant-def-456", status="200"} 512

# 全局聚合(无租户标签)
node_cpu_seconds_total{mode="user"} 3600
高基数警告: 不要将 user_id、request_id 等极高基数字段作为指标标签。这会导致时序数据库性能崩溃。用户级数据应通过日志或追踪处理。

2.4 链路追踪(Tracing):上下文传播与租户透传

链路追踪在多租户系统中的核心任务是跨服务边界透传租户上下文。一个请求可能经过 API 网关、认证服务、业务服务、数据服务等多个微服务,每个服务都必须从上游 Span 中提取 tenant_id 并注入到下游 Span。

租户上下文传播的标准做法:

  • W3C Trace Context 扩展: 在 tracestate 字段中携带 tenant_id,而非自定义 HTTP Header。
  • 异步消息透传: 在消息队列(如 Kafka、RabbitMQ)的消息头中注入租户上下文,消费者端自动恢复。
  • Span 标签注入: 每个 Span 必须包含 tenant_id 标签,用于后续按租户过滤追踪数据。
// OpenTelemetry Span 示例(Java)
Span span = tracer.spanBuilder("processPayment")
    .setAttribute("tenant_id", "tenant-abc-123")
    .setAttribute("service.name", "payment-service")
    .startSpan();
故障排查价值: 当租户 A 报告请求超时,通过 tenant_id 过滤追踪数据,可以快速定位是租户 A 专属的数据库连接池耗尽,还是全局性的网络抖动。

2.5 三大支柱的协同关系:多租户故障排查视图

下图展示了日志、指标、链路追踪在多租户故障排查中的协同工作流:

三大支柱协同排查多租户故障 三大支柱协同排查多租户故障 租户 A 报告故障 日志 (Logging) 按 tenant_id 过滤错误日志 指标 (Metrics) 查看租户 A 的 P99 延迟 链路追踪 (Tracing) 追踪租户 A 请求调用链 关联分析:日志错误码 + 指标异常 + 追踪耗时 定位:租户 A 专属数据库连接池耗尽 三大支柱数据通过 tenant_id 关联,形成完整的租户级故障视图

2.6 三大支柱的租户隔离矩阵

在实际架构中,三大支柱的隔离策略需要组合使用。下表总结了不同场景下的推荐隔离方案:

场景 日志策略 指标策略 链路追踪策略
金融级租户(高隔离) 独立日志索引 + 独立存储集群 独立指标实例 + 租户级 Dashboard 独立追踪后端 + 租户级采样率
标准 SaaS 租户 共享集群 + 按 tenant_id 路由索引 共享实例 + 租户标签 + 降采样 共享后端 + 租户上下文过滤
免费/试用租户 共享索引 + 短保留期(7天) 聚合指标 + 无租户级标签 头部采样 + 不保留全量追踪
架构建议: 不要追求所有租户使用同一隔离级别。根据租户 SLA 和付费等级,动态调整三大支柱的隔离深度,在成本与可观测性之间取得平衡。