1. 分布式调试的痛点:日志太多,链路太长
如果你写过 .NET Aspire 项目,大概率经历过这种场景:本地 F5 启动 AppHost,Dashboard 一打开,资源列表里五六个服务全绿,但某个 API 偶尔返回 500。点进 Structured logs,几百条日志刷屏,Error 和 Warning 混在一起;再切到 Traces,一条跨 OrderService、PaymentService、Redis 的调用链展开有二十多个 span,哪个环节慢、哪个环节抛异常,全靠肉眼一行行对时间戳。
我试过最笨的办法:把 TraceId 复制出来,在日志页搜索框里粘贴,再一条条看。问题是 Aspire Dashboard 的日志和追踪是两个独立视图,来回跳转特别费神。更麻烦的是,很多异常不是本地代码直接抛的,而是下游服务超时、gRPC 状态码非 OK、HttpClient 连接被拒绝,这些信息散落在不同 span 的 attributes 里,人工关联成本极高。
.NET Aspire 9.3 之后,Dashboard 右上角多了一个 GitHub Copilot 按钮,资源、结构化日志、追踪、Span 的右键菜单里也出现了「Ask GitHub Copilot」。它做的事情很直接:把当前视图里的 OpenTelemetry 数据(日志、trace、span、resource 状态)作为上下文喂给 Copilot,让 AI 帮你做归纳、定位和解释。对本地调试来说,这相当于给 Dashboard 配了一个随时能问的 SRE 助手。
这篇文章聚焦本地调试场景,交付三样东西:可复制的 AppHost 配置片段(确保遥测数据完整上报到 Dashboard)、Copilot 提示词模板(针对日志分析和追踪分析分别给模板)、以及验证遥测是否被正确采集的步骤。适合正在用 .NET Aspire 做微服务本地开发、已经被分布式日志折磨过的 .NET 开发者。
2. 前置准备:TaoToken 与 Copilot 在 Aspire 里的分工
先说清楚一件事:Aspire Dashboard 里的 GitHub Copilot 集成,走的是你 IDE 登录的 GitHub 账号和 Copilot 订阅,它负责「读 Dashboard 里的遥测数据并给出分析建议」。而如果你在项目里用 AI 辅助写代码、生成 AppHost 配置、或者跑 Agent 做批量重构,这部分模型调用可以走 TaoToken 的 API,两者不冲突。
TaoToken 在这里的角色是统一的模型接入层:官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以了解能力范围,API 入口是 https://taotoken.net/api(不加 UTM)。如果你要在 AppHost 或配套脚本里调用模型做日志摘要、生成提示词,用它的 API Key 就行。
需要提前准备的东西:
- .NET Aspire 9.3 或更高版本的 AppHost 项目
- VS Code + C# Dev Kit 1.19.63+,或 Visual Studio 17.14+
- IDE 里已登录 GitHub 账号,且该账号有 Copilot 订阅(免费计划也行,有每月聊天次数限制)
- 一个能正常跑的 Aspire 项目,至少包含两个以上资源(比如 API + Worker + Redis)
注意:Dashboard 里的 Copilot 只在从 IDE 运行 Aspire 项目时可用,直接
dotnet run启动 AppHost 不会出现 Copilot 按钮。这是设计如此,不是配置问题。
如果你还没有 API Key,可以去控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。长期做编码和 Agent 任务的话,Coding Plan 页面是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。
3. 可复制配置:让 AppHost 把遥测完整送到 Dashboard
Copilot 能不能给出准确建议,前提是 Dashboard 里得有足够完整的 OpenTelemetry 数据。Aspire 默认已经开了 OTLP 导出,但有些细节不配好,日志和 trace 会缺字段。下面这段 AppHost 配置可以直接抄。
3.1 AppHost Program.cs 基础配置
var builder = DistributedApplication.CreateBuilder(args); // Redis 作为缓存资源,方便演示跨服务调用链 var cache = builder.AddRedis("cache"); var apiService = builder.AddProject<Projects.AspireCopilotDemo_ApiService>("apiservice") .WithReference(cache) .WithHttpHealthCheck("/health"); var workerService = builder.AddProject<Projects.AspireCopilotDemo_Worker>("workerservice") .WithReference(cache) .WithReference(apiService); builder.AddProject<Projects.AspireCopilotDemo_Web>("webfrontend") .WithExternalHttpEndpoints() .WithReference(apiService) .WithReference(workerService); builder.Build().Run();这段是标准写法,重点是WithReference会把连接字符串和服务发现信息注入到下游项目,同时 Aspire 会自动为每个项目配置 OTLP exporter,把日志、trace、metrics 发到 Dashboard。
3.2 确保日志和追踪字段完整
在 ApiService 和 Worker 的Program.cs里,建议显式配置 OpenTelemetry,把异常堆栈和 HTTP/gRPC 的 span 属性带上:
builder.Services.AddOpenTelemetry() .WithTracing(tracing => { tracing.AddAspNetCoreInstrumentation(options => { options.RecordException = true; options.EnrichWithHttpRequestMessage = (activity, request) => { activity.SetTag("http.request.body.size", request.Content?.Headers.ContentLength); }; }); tracing.AddHttpClientInstrumentation(options => { options.RecordException = true; }); tracing.AddGrpcClientInstrumentation(); tracing.AddRedisInstrumentation(); }) .WithLogging(logging => { logging.AddOtlpExporter(); });RecordException = true很关键,否则 span 里只有状态码,没有异常类型和堆栈,Copilot 分析时只能看到「500」而看不到「为什么 500」。
3.3 结构化日志里带上 TraceId
在业务代码里打日志时,用ILogger的结构化写法,Aspire 会自动把 TraceId 和 SpanId 关联进去:
public async Task<OrderResult> CreateOrderAsync(OrderRequest request, CancellationToken ct) { using var activity = Activity.Current?.Source.StartActivity("CreateOrder"); activity?.SetTag("order.id", request.OrderId); _logger.LogInformation("开始创建订单 {OrderId},金额 {Amount}", request.OrderId, request.Amount); try { var result = await _paymentClient.ChargeAsync(request, ct); _logger.LogInformation("订单 {OrderId} 支付成功,交易号 {TransactionId}", request.OrderId, result.TransactionId); return result; } catch (Exception ex) { _logger.LogError(ex, "订单 {OrderId} 创建失败", request.OrderId); throw; } }这样在 Dashboard 的 Structured logs 里,每条日志都能点开看到对应的 TraceId,Copilot 也能顺着这个关联去分析整条链路。
4. 验证请求:确认遥测数据被 Dashboard 正确采集
配置写完,启动项目,按下面步骤验证数据是否到位。这一步不做,后面 Copilot 分析出来的结论可能是残缺的。
4.1 启动并检查资源状态
从 IDE 按 F5 启动 AppHost,Dashboard 自动打开。资源列表里应该看到 apiservice、workerservice、webfrontend、cache 全部变成 Running 状态。如果某个资源一直 Starting,先看它的日志,通常是端口冲突或依赖没起来。
4.2 触发一次跨服务调用
用浏览器或 curl 打一下 webfrontend 的接口,让它调用 apiservice,apiservice 再访问 Redis:
curl -X POST http://localhost:5000/api/orders \ -H "Content-Type: application/json" \ -d '{"orderId":"ORD-1001","amount":99.9}'4.3 在 Traces 页面确认链路完整
切到 Dashboard 的 Traces 页面,应该能看到一条 trace,展开后包含:
- webfrontend 的 HTTP server span
- apiservice 的 HTTP client span 和 server span
- Redis 的 command span
- 如果 worker 被触发,还有 worker 的消费 span
每个 span 的 Duration、Status、Attributes 都要有值。如果某个 span 缺失,回到第 3.2 节检查对应的 Instrumentation 有没有加。
4.4 在 Structured logs 页面确认 TraceId 关联
切到 Structured logs,找到刚才那条「开始创建订单」的日志,点开详情,应该能看到 TraceId 和 SpanId 字段。复制 TraceId,回到 Traces 页面搜索,能定位到同一条链路。这个关联是 Copilot 做跨视图分析的基础。
4.5 确认 Copilot 按钮出现
Dashboard 右上角应该出现 GitHub Copilot 图标。如果没有,检查:IDE 是否登录了 GitHub 账号、账号是否有 Copilot 订阅、C# Dev Kit 版本是否达标。资源、日志、追踪的右键菜单里也应该有「Ask GitHub Copilot」选项。
5. Copilot 提示词模板与常见错排查
数据验证通过后,就可以用 Copilot 做实际分析了。下面给两类提示词模板,以及我踩过的几个坑。
5.1 日志分析提示词模板
在 Structured logs 页面选中一批 Error 日志,右键「Ask GitHub Copilot」,然后输入:
请分析当前选中的错误日志,按以下结构输出: 1. 错误类型归纳(相同根因的合并) 2. 每条错误的可能触发条件 3. 涉及的服务和 TraceId 4. 建议的排查顺序(从最可能的根因开始) 不要逐条复述日志原文,只给归纳和判断。这个模板的好处是强制 Copilot 做归纳而不是复读。实测下来,几百条日志它能压成三到五条根因,比人工翻快很多。
5.2 追踪分析提示词模板
在 Trace 详情页点「解析跟踪」,或者右键「Ask GitHub Copilot」,输入:
请分析这条分布式追踪: 1. 关键路径上耗时最长的三个 span,给出耗时占比 2. 是否有失败或异常的 span,指出异常类型和所在服务 3. 是否存在串行调用可以并行化的机会 4. 如果这是性能问题,最可能的瓶颈在哪 用表格列出 span 名称、服务、耗时、状态。5.3 常见错排查
Copilot 按钮不出现:最常见原因是没从 IDE 启动,或者 C# Dev Kit 版本低于 1.19.63。VS Code 里检查扩展版本,升级后重启。
Copilot 分析结果说「没有足够上下文」:通常是遥测数据缺字段。回到第 4 节,确认 span 的 Attributes 和日志的 TraceId 都有值。特别是RecordException没开的话,异常 span 里只有状态码,Copilot 没法判断根因。
日志里 TraceId 为空:检查是否用了结构化日志写法。字符串拼接的日志(比如LogInformation("order " + id))不会自动关联 TraceId,必须用占位符写法LogInformation("order {OrderId}", id)。
Traces 页面只有 server span 没有 client span:HttpClientInstrumentation 没加,或者 HttpClient 是直接new出来的而不是通过 DI 注入的。Aspire 里统一用AddHttpClient注册。
Copilot 给出的建议和实际代码对不上:Dashboard 里的 Copilot 只能看到遥测数据,看不到你的源码。如果分析涉及具体代码逻辑,需要你手动把相关代码片段贴进对话,或者用 IDE 里的 Copilot Chat 结合代码上下文问。
5.4 把 AI 分析接入日常调试流程
如果你想把日志摘要、trace 分析做成自动化脚本,比如每次 CI 跑完集成测试后自动生成一份诊断报告,可以用 TaoToken 的 API 来调模型。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,API 入口 https://taotoken.net/api 。脚本里把 Dashboard 导出的 OTLP 数据或日志文件作为上下文传给模型,就能批量生成分析结果。长期跑这类任务的话,Coding Plan 比按次调用更划算,页面在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。
6. 把 Copilot 变成你的 Aspire 调试搭档
Aspire Dashboard 集成 Copilot 之后,本地调试的流程可以变成这样:先按第 3 节配好遥测,按第 4 节验证数据完整,遇到问题时不再手动翻日志,而是选中相关日志或 trace,用第 5 节的模板让 Copilot 做归纳和定位。它不能替代你对业务代码的理解,但能把你从「几百条日志里找异常」这种体力活里解放出来。
几个实用技巧:提示词里明确要求「按根因归纳」而不是「逐条列出」,输出质量会高很多;分析 trace 时先让它算耗时占比,再问瓶颈,比直接问「哪里慢」更准;如果 Copilot 的结论你觉得不对,把对应的 span attributes 或日志字段贴回去追问,通常能纠正。
如果你在项目里同时用 AI 辅助编码和调试分析,可以把模型调用统一走 TaoToken,API Key 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 管理,模型对话在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 体验。Claude Code 相关的接入可以参考 https://taotoken.net/claude-code?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。