FreeKit 可观测性
FreeKitModules 将遥测采集与存储平台解耦:应用使用 OpenTelemetry 标准导出 Trace 和 Metrics,Serilog 通过独立 OTLP Sink 导出 Logs;生产部署再由 OpenTelemetry Collector 统一接收、限流、批处理并转发到 OpenObserve。CPU、Wall Time、分配与堆剖析由 Pyroscope 原生 profiler 直接发送,不经过 Collector。
当前采集边界
三个宿主都在启动早期调用 AddFreeKitObservability,并使用不同的默认服务名:
| 宿主 | 默认 service.name | 注册位置 | 生产配置 |
|---|---|---|---|
FreeKit.Host | freekit-host | src/Services/Host/FreeKit.Host/Program.cs | src/Services/Host/FreeKit.Host/appsettings.Production.json |
FreeKit.Job.Host | freekit-job | src/Services/Host/FreeKit.Job.Host/Program.cs | src/Services/Host/FreeKit.Job.Host/appsettings.Production.json |
FreeKit.MessageHandler.Host | freekit-message | src/Services/Host/FreeKit.MessageHandler.Host/Program.cs | src/Services/Host/FreeKit.MessageHandler.Host/appsettings.Production.json |
应用侧实现位于:
src/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/ObservabilityExtensions.cssrc/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/FreeSqlObservability.cssrc/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 地址时,必须同时检查:
OpenTelemetry:Otlp:Endpoint或OTEL_EXPORTER_OTLP_ENDPOINTSerilog.WriteTo中名为OpenTelemetry的 Sink endpoint
只覆盖 OTEL_EXPORTER_OTLP_ENDPOINT 不会自动改写 JSON 中的 Serilog Sink 地址。
Profiles
三个 Host Dockerfile 都内置 Grafana Pyroscope .NET 原生 profiler,但“镜像内置 profiler”不等于“当前部署一定在发送 Profile”。容器运行时还需要正确设置 PYROSCOPE_APPLICATION_NAME、PYROSCOPE_SERVER_ADDRESS 和采集开关。具体差异见 OpenObserve 完整部署。
启用与配置优先级
Trace/Metrics 满足以下任一条件即启用:
OpenTelemetry:Enabled为trueOTEL_EXPORTER_OTLP_ENDPOINT非空OpenTelemetry:Otlp:Endpoint非空
endpoint 和 protocol 优先使用标准环境变量,再回退到应用配置:
| 用途 | 高优先级 | 回退配置 |
|---|---|---|
| 服务名 | OTEL_SERVICE_NAME | OpenTelemetry:ServiceName,再回退 Host 默认名 |
| endpoint | OTEL_EXPORTER_OTLP_ENDPOINT | OpenTelemetry:Otlp:Endpoint |
| 协议 | OTEL_EXPORTER_OTLP_PROTOCOL | OpenTelemetry:Otlp:Protocol |
协议只接受:
grpchttp/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-job、freekit-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 或结构化日志中,并执行脱敏。
自定义业务埋点
可以使用 ActivitySource 和 System.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.cssrc/BuildingBlocks/IGeekFan.FreeKit.Web/Extensions/FreeSqlObservability.cssrc/Services/Host/FreeKit.Host/Program.cssrc/Services/Host/FreeKit.Job.Host/Program.cssrc/Services/Host/FreeKit.MessageHandler.Host/Program.csbuild/observability/docker-compose.ymlbuild/observability/otel-collector.yaml