跳到主要内容

FreeKit 可观测性

FreeKitModules 将遥测采集与存储平台解耦:应用使用 OpenTelemetry 标准导出 Trace 和 Metrics,Serilog 通过独立 OTLP Sink 导出 Logs;生产部署再由 OpenTelemetry Collector 统一接收、限流、批处理并转发到 OpenObserve。CPU、Wall Time、分配与堆剖析由 Pyroscope 原生 profiler 直接发送,不经过 Collector。

当前采集边界

三个宿主都在启动早期调用 AddFreeKitObservability,并使用不同的默认服务名:

宿主默认 service.name注册位置生产配置
FreeKit.Hostfreekit-hostsrc/Services/Host/FreeKit.Host/Program.cssrc/Services/Host/FreeKit.Host/appsettings.Production.json
FreeKit.Job.Hostfreekit-jobsrc/Services/Host/FreeKit.Job.Host/Program.cssrc/Services/Host/FreeKit.Job.Host/appsettings.Production.json
FreeKit.MessageHandler.Hostfreekit-messagesrc/Services/Host/FreeKit.MessageHandler.Host/Program.cssrc/Services/Host/FreeKit.MessageHandler.Host/appsettings.Production.json

应用侧实现位于:

  • src/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/ObservabilityExtensions.cs
  • src/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/FreeSqlObservability.cs
  • src/BuildingBlocks/IGeekFan.FreeKit.Web/IGeekFan.FreeKit.Web.csproj

SDK 还会把入口程序集版本写入 service.version,把 Environment.MachineName 写入 service.instance.id,并以当前 ASP.NET Core 环境名写入 deployment.environment.name,可用于区分版本、实例与环境。

Trace

当前自动注册的 Trace 来源包括:

  • ASP.NET Core 入站请求;记录异常,过滤 /health 路径
  • HttpClient 出站请求;记录异常
  • FreeSql 命令;由 FreeSql AOP 创建 Activity,不是 SqlClientInstrumentation

FreeSql Span 使用 FreeKit.FreeSql ActivitySource,并记录数据库类型、操作、数据库名、服务端地址、耗时和异常。只有显式开启 OpenTelemetry:FreeSql:IncludeSql 时才会附加 db.query.text

Metrics

当前自动注册:

  • ASP.NET Core 请求指标
  • HttpClient 请求指标
  • .NET Runtime、GC、线程池等运行时指标

build/observability/otel-collector.yaml 还会在应用外采集宿主机、MySQL 和 Redis 指标。应用 SDK 与 Collector 的基础设施采集是两层能力,不应混为一谈。

Logs

Logs 不由 AddFreeKitObservability 注册。三个宿主的生产配置分别通过 Serilog.Sinks.OpenTelemetry 将结构化日志发送到 Collector,并设置各自的 service.name

这意味着 Trace/Metrics endpoint 与 Serilog endpoint 是两份配置。切换 Collector 地址时,必须同时检查:

  1. OpenTelemetry:Otlp:EndpointOTEL_EXPORTER_OTLP_ENDPOINT
  2. Serilog.WriteTo 中名为 OpenTelemetry 的 Sink endpoint

只覆盖 OTEL_EXPORTER_OTLP_ENDPOINT 不会自动改写 JSON 中的 Serilog Sink 地址。

Profiles

三个 Host Dockerfile 都内置 Grafana Pyroscope .NET 原生 profiler,但“镜像内置 profiler”不等于“当前部署一定在发送 Profile”。容器运行时还需要正确设置 PYROSCOPE_APPLICATION_NAMEPYROSCOPE_SERVER_ADDRESS 和采集开关。具体差异见 OpenObserve 完整部署

启用与配置优先级

Trace/Metrics 满足以下任一条件即启用:

  • OpenTelemetry:Enabledtrue
  • OTEL_EXPORTER_OTLP_ENDPOINT 非空
  • OpenTelemetry:Otlp:Endpoint 非空

endpoint 和 protocol 优先使用标准环境变量,再回退到应用配置:

用途高优先级回退配置
服务名OTEL_SERVICE_NAMEOpenTelemetry:ServiceName,再回退 Host 默认名
endpointOTEL_EXPORTER_OTLP_ENDPOINTOpenTelemetry:Otlp:Endpoint
协议OTEL_EXPORTER_OTLP_PROTOCOLOpenTelemetry:Otlp:Protocol

协议只接受:

  • grpc
  • http/protobuf

容器与 Collector 同在 Docker 网络时,推荐 OTLP/gRPC:

{
"OpenTelemetry": {
"Enabled": true,
"ServiceName": "freekit-host",
"FreeSql": {
"Enabled": true,
"IncludeSql": false
},
"Otlp": {
"Endpoint": "http://otel-collector:4317",
"Protocol": "grpc"
}
}
}

等价的 Trace/Metrics 环境变量:

OpenTelemetry__Enabled=true
OTEL_SERVICE_NAME=freekit-host
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc

Job 和 Message 应分别将 OTEL_SERVICE_NAME 设为 freekit-jobfreekit-message。生产配置文件当前已经启用 OpenTelemetry,并指向 http://otel-collector:4317;Collector 暂时不可用通常不会阻止业务 Host 启动,但遥测会发送失败或被丢弃,应监控应用与 Collector 日志。

:::warning Docker 网络不是自动共享的

build/observability/docker-compose.yml 使用 external network freekit-net。单容器部署脚本会把 Host 加入这个网络,但 build/compose/freekit_pro_modules/docker-compose.yml 当前使用自己的 Compose 默认网络。并行启动两套 Compose 前,必须显式让需要导出遥测的应用服务加入 freekit-net,或改用它们能够访问的 Collector 地址。

:::

FreeSql SQL 与敏感数据

当前三个 appsettings.Production.json 都将 OpenTelemetry:FreeSql:IncludeSql 设为 true,因此生产 Span 可能携带完整 SQL 文本。FreeSql 的命令生成方式可能让业务值出现在 SQL 中,必须结合数据类型与访问范围评估风险。

推荐原则:

  • 默认使用 IncludeSql: false,确有排障需要时限时开启
  • 不在 Span 或日志中记录密码、Token、Cookie、身份证、手机号、密钥和完整请求体
  • 对 OpenObserve 的 Trace/Log 查询设置最小权限和合理保留期
  • 变更生产采集范围时同步检查脱敏、存储成本与合规要求

指标基数控制

Metrics 标签适合低基数维度,例如环境、服务、区域、接口模板、渠道和成功/失败。以下值不要作为指标标签:

  • 用户 ID、订单 ID、文章 ID
  • Trace ID、Request ID
  • 带随机参数的完整 URL
  • 完整异常消息或 SQL

高基数字段会快速放大时序数量。需要定位单次请求时,把相关值放到受控的 Trace 或结构化日志中,并执行脱敏。

自定义业务埋点

可以使用 ActivitySourceSystem.Diagnostics.Metrics.Meter 定义业务 Span 与指标,但创建 Source/Meter 并不会自动让当前 SDK 监听它。新增业务埋点时,还需要在 ObservabilityExtensions.cs 中:

  • 对 Trace 调用 .AddSource("你的 Source 名称")
  • 对 Metrics 调用 .AddMeter("你的 Meter 名称")

示例:

using System.Diagnostics;
using System.Diagnostics.Metrics;

internal static class CmsTelemetry
{
public const string Name = "FreeKit.CmsKit";
public static readonly ActivitySource Activities = new(Name);
public static readonly Meter Meter = new(Name);
public static readonly Counter<long> PublishedArticles =
Meter.CreateCounter<long>("cms.articles.published");
}
using var activity = CmsTelemetry.Activities.StartActivity("PublishArticle");
activity?.SetTag("cms.channel", channelCode); // 低敏、低基数

await articleManager.PublishAsync(articleId);
CmsTelemetry.PublishedArticles.Add(1, new("cms.channel", channelCode));

不要把 articleId 放进 Counter 标签。若确有单请求定位需求,可以在确认访问控制和保留期后,将其作为 Trace 属性而不是 Metrics 标签。

选择部署方式

场景文档
使用仓库当前 OpenObserve、Collector、MySQL/Redis Exporter 与 Pyroscope 完整栈OpenObserve 完整部署
先确认三个 Host 是否成功发送 OTLP,暂不部署查询和存储平台独立 Collector 调试验证

OpenTelemetry Collector 本身不是长期存储,也不提供业务查询界面。验证完成后应接入 OpenObserve 或其他兼容后端,并关闭高噪声的 debug exporter。

相关源码

  • src/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/ObservabilityExtensions.cs
  • src/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/FreeSqlObservability.cs
  • src/Services/Host/FreeKit.Host/Program.cs
  • src/Services/Host/FreeKit.Job.Host/Program.cs
  • src/Services/Host/FreeKit.MessageHandler.Host/Program.cs
  • build/observability/docker-compose.yml
  • build/observability/otel-collector.yaml