Skip to content

Commit f2d21a0

Browse files
stephentoubCopilotSteveSandersonMS
authored
feat: add OpenTelemetry support across all SDKs (#785)
* docs: add OpenTelemetry TelemetryConfig and trace context propagation documentation Add telemetry documentation across all SDK docs: - getting-started.md: New 'Telemetry & Observability' section with per-language examples, TelemetryConfig options table, file export example, and trace context propagation explanation - Per-SDK READMEs (Node.js, Python, Go, .NET): Add telemetry option to constructor/options lists and new Telemetry sections with language-specific examples and dependency notes - observability/opentelemetry.md: Add 'Built-in Telemetry Support' section at top with multi-language examples, options table, propagation details, and dependency matrix - docs/index.md: Update Observability description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add OpenTelemetry support across all SDKs Add TelemetryConfig to all four SDKs (Node, Python, Go, .NET) to configure OpenTelemetry instrumentation on the Copilot CLI process. This includes: - TelemetryConfig type with OTLP endpoint, file exporter, source name, and capture-content options, mapped to CLI environment variables - W3C Trace Context propagation (traceparent/tracestate) on session.create, session.resume, and session.send RPC calls - Trace context restoration in tool call handlers (v2 RPC and v3 broadcast) so user tool code executes within the correct distributed trace - Telemetry helper modules (telemetry.ts, telemetry.py, telemetry.go, Telemetry.cs) with unit tests - Updated generated types from latest schema Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(nodejs): remove @opentelemetry/api dependency Replace the optional @opentelemetry/api peer dependency with a user-provided callback approach: - Add TraceContext interface and TraceContextProvider type - Add onGetTraceContext callback to CopilotClientOptions - Pass traceparent/tracestate directly on ToolInvocation for inbound context - Remove @opentelemetry/api from peerDependencies and devDependencies - Rewrite telemetry.ts to a simple callback-based helper (~27 lines) - Update tests, README, and OpenTelemetry docs with wire-up examples Users who want distributed trace propagation provide a callback: const client = new CopilotClient({ onGetTraceContext: () => { const carrier = {}; propagation.inject(context.active(), carrier); return carrier; }, }); TelemetryConfig (CLI env vars) is unchanged and requires no dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
1 parent 05dd60e commit f2d21a0

File tree

95 files changed

+2345
-38
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+2345
-38
lines changed

docs/getting-started.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,119 @@ await using var session = await client.CreateSessionAsync(new()
13951395

13961396
---
13971397

1398+
## Telemetry & Observability
1399+
1400+
The Copilot SDK supports [OpenTelemetry](https://opentelemetry.io/) for distributed tracing. Provide a `telemetry` configuration to the client to enable trace export from the CLI process and automatic [W3C Trace Context](https://www.w3.org/TR/trace-context/) propagation between the SDK and CLI.
1401+
1402+
### Enabling Telemetry
1403+
1404+
Pass a `telemetry` (or `Telemetry`) config when creating the client. This is the opt-in — no separate "enabled" flag is needed.
1405+
1406+
<details open>
1407+
<summary><strong>Node.js / TypeScript</strong></summary>
1408+
1409+
<!-- docs-validate: skip -->
1410+
```typescript
1411+
import { CopilotClient } from "@github/copilot-sdk";
1412+
1413+
const client = new CopilotClient({
1414+
telemetry: {
1415+
otlpEndpoint: "http://localhost:4318",
1416+
},
1417+
});
1418+
```
1419+
1420+
Optional peer dependency: `@opentelemetry/api`
1421+
1422+
</details>
1423+
1424+
<details>
1425+
<summary><strong>Python</strong></summary>
1426+
1427+
<!-- docs-validate: skip -->
1428+
```python
1429+
from copilot import CopilotClient, SubprocessConfig
1430+
1431+
client = CopilotClient(SubprocessConfig(
1432+
telemetry={
1433+
"otlp_endpoint": "http://localhost:4318",
1434+
},
1435+
))
1436+
```
1437+
1438+
Install with telemetry extras: `pip install copilot-sdk[telemetry]` (provides `opentelemetry-api`)
1439+
1440+
</details>
1441+
1442+
<details>
1443+
<summary><strong>Go</strong></summary>
1444+
1445+
<!-- docs-validate: skip -->
1446+
```go
1447+
client, err := copilot.NewClient(copilot.ClientOptions{
1448+
Telemetry: &copilot.TelemetryConfig{
1449+
OTLPEndpoint: "http://localhost:4318",
1450+
},
1451+
})
1452+
```
1453+
1454+
Dependency: `go.opentelemetry.io/otel`
1455+
1456+
</details>
1457+
1458+
<details>
1459+
<summary><strong>.NET</strong></summary>
1460+
1461+
<!-- docs-validate: skip -->
1462+
```csharp
1463+
var client = new CopilotClient(new CopilotClientOptions
1464+
{
1465+
Telemetry = new TelemetryConfig
1466+
{
1467+
OtlpEndpoint = "http://localhost:4318",
1468+
},
1469+
});
1470+
```
1471+
1472+
No extra dependencies — uses built-in `System.Diagnostics.Activity`.
1473+
1474+
</details>
1475+
1476+
### TelemetryConfig Options
1477+
1478+
| Option | Node.js | Python | Go | .NET | Description |
1479+
|---|---|---|---|---|---|
1480+
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
1481+
| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
1482+
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
1483+
| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
1484+
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |
1485+
1486+
### File Export
1487+
1488+
To write traces to a local file instead of an OTLP endpoint:
1489+
1490+
<!-- docs-validate: skip -->
1491+
```typescript
1492+
const client = new CopilotClient({
1493+
telemetry: {
1494+
filePath: "./traces.jsonl",
1495+
exporterType: "file",
1496+
},
1497+
});
1498+
```
1499+
1500+
### Trace Context Propagation
1501+
1502+
Trace context is propagated automatically — no manual instrumentation is needed:
1503+
1504+
- **SDK → CLI**: `traceparent` and `tracestate` headers from the current span/activity are included in `session.create`, `session.resume`, and `session.send` RPC calls.
1505+
- **CLI → SDK**: When the CLI invokes tool handlers, the trace context from the CLI's span is propagated so your tool code runs under the correct parent span.
1506+
1507+
📖 **[OpenTelemetry Instrumentation Guide →](./observability/opentelemetry.md)** — detailed GenAI semantic conventions, event-to-attribute mapping, and complete examples.
1508+
1509+
---
1510+
13981511
## Learn More
13991512

14001513
- [Authentication Guide](./auth/index.md) - GitHub OAuth, environment variables, and BYOK
@@ -1406,6 +1519,7 @@ await using var session = await client.CreateSessionAsync(new()
14061519
- [Using MCP Servers](./features/mcp.md) - Integrate external tools via Model Context Protocol
14071520
- [GitHub MCP Server Documentation](https://github.com/github/github-mcp-server)
14081521
- [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Explore more MCP servers
1522+
- [OpenTelemetry Instrumentation](./observability/opentelemetry.md) - Add tracing to your SDK usage
14091523

14101524
---
14111525

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Detailed API reference for each session hook.
6767

6868
### [Observability](./observability/opentelemetry.md)
6969

70-
- [OpenTelemetry Instrumentation](./observability/opentelemetry.md)add tracing to your SDK usage
70+
- [OpenTelemetry Instrumentation](./observability/opentelemetry.md)built-in TelemetryConfig, trace context propagation, and application-level tracing
7171

7272
### [Integrations](./integrations/microsoft-agent-framework.md)
7373

docs/observability/opentelemetry.md

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,158 @@
11
# OpenTelemetry Instrumentation for Copilot SDK
22

3-
This guide shows how to add OpenTelemetry tracing to your Copilot SDK applications using GenAI semantic conventions.
3+
This guide shows how to add OpenTelemetry tracing to your Copilot SDK applications.
4+
5+
## Built-in Telemetry Support
6+
7+
The SDK has built-in support for configuring OpenTelemetry on the CLI process and propagating W3C Trace Context between the SDK and CLI. Provide a `TelemetryConfig` when creating the client to opt in:
8+
9+
<details open>
10+
<summary><strong>Node.js / TypeScript</strong></summary>
11+
12+
<!-- docs-validate: skip -->
13+
```typescript
14+
import { CopilotClient } from "@github/copilot-sdk";
15+
16+
const client = new CopilotClient({
17+
telemetry: {
18+
otlpEndpoint: "http://localhost:4318",
19+
},
20+
});
21+
```
22+
23+
</details>
24+
25+
<details>
26+
<summary><strong>Python</strong></summary>
27+
28+
<!-- docs-validate: skip -->
29+
```python
30+
from copilot import CopilotClient, SubprocessConfig
31+
32+
client = CopilotClient(SubprocessConfig(
33+
telemetry={
34+
"otlp_endpoint": "http://localhost:4318",
35+
},
36+
))
37+
```
38+
39+
</details>
40+
41+
<details>
42+
<summary><strong>Go</strong></summary>
43+
44+
<!-- docs-validate: skip -->
45+
```go
46+
client, err := copilot.NewClient(copilot.ClientOptions{
47+
Telemetry: &copilot.TelemetryConfig{
48+
OTLPEndpoint: "http://localhost:4318",
49+
},
50+
})
51+
```
52+
53+
</details>
54+
55+
<details>
56+
<summary><strong>.NET</strong></summary>
57+
58+
<!-- docs-validate: skip -->
59+
```csharp
60+
var client = new CopilotClient(new CopilotClientOptions
61+
{
62+
Telemetry = new TelemetryConfig
63+
{
64+
OtlpEndpoint = "http://localhost:4318",
65+
},
66+
});
67+
```
68+
69+
</details>
70+
71+
### TelemetryConfig Options
72+
73+
| Option | Node.js | Python | Go | .NET | Description |
74+
|---|---|---|---|---|---|
75+
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
76+
| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
77+
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
78+
| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
79+
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |
80+
81+
### Trace Context Propagation
82+
83+
> **Most users don't need this.** The `TelemetryConfig` above is all you need to collect traces from the CLI. The trace context propagation described in this section is an **advanced feature** for applications that create their own OpenTelemetry spans and want them to appear in the **same distributed trace** as the CLI's spans.
84+
85+
The SDK can propagate W3C Trace Context (`traceparent`/`tracestate`) on JSON-RPC payloads so that your application's spans and the CLI's spans are linked in one distributed trace. This is useful when, for example, you want to see a "handle tool call" span in your app nested inside the CLI's "execute tool" span, or show the SDK call as a child of your request-handling span.
86+
87+
#### SDK → CLI (outbound)
88+
89+
For **Node.js**, provide an `onGetTraceContext` callback on the client options. This is only needed if your application already uses `@opentelemetry/api` and you want to link your spans with the CLI's spans. The SDK calls this callback before `session.create`, `session.resume`, and `session.send` RPCs:
90+
91+
<!-- docs-validate: skip -->
92+
```typescript
93+
import { CopilotClient } from "@github/copilot-sdk";
94+
import { propagation, context } from "@opentelemetry/api";
95+
96+
const client = new CopilotClient({
97+
telemetry: { otlpEndpoint: "http://localhost:4318" },
98+
onGetTraceContext: () => {
99+
const carrier: Record<string, string> = {};
100+
propagation.inject(context.active(), carrier);
101+
return carrier; // { traceparent: "00-...", tracestate: "..." }
102+
},
103+
});
104+
```
105+
106+
For **Python**, **Go**, and **.NET**, trace context injection is automatic when the respective OpenTelemetry/Activity API is configured — no callback is needed.
107+
108+
#### CLI → SDK (inbound)
109+
110+
When the CLI invokes a tool handler, the `traceparent` and `tracestate` from the CLI's span are available in all languages:
111+
112+
- **Go**: The `ToolInvocation.TraceContext` field is a `context.Context` with the trace already restored — use it directly as the parent for your spans.
113+
- **Python**: Trace context is automatically restored around the handler via `trace_context()` — child spans are parented to the CLI's span automatically.
114+
- **.NET**: Trace context is automatically restored via `RestoreTraceContext()` — child `Activity` instances are parented to the CLI's span automatically.
115+
- **Node.js**: Since the SDK has no OpenTelemetry dependency, `traceparent` and `tracestate` are passed as raw strings on the `ToolInvocation` object. Restore the context manually if needed:
116+
117+
<!-- docs-validate: skip -->
118+
```typescript
119+
import { propagation, context, trace } from "@opentelemetry/api";
120+
121+
session.registerTool(myTool, async (args, invocation) => {
122+
// Restore the CLI's trace context as the active context
123+
const carrier = {
124+
traceparent: invocation.traceparent,
125+
tracestate: invocation.tracestate,
126+
};
127+
const parentCtx = propagation.extract(context.active(), carrier);
128+
129+
// Create a child span under the CLI's span
130+
const tracer = trace.getTracer("my-app");
131+
return context.with(parentCtx, () =>
132+
tracer.startActiveSpan("my-tool", async (span) => {
133+
try {
134+
const result = await doWork(args);
135+
return result;
136+
} finally {
137+
span.end();
138+
}
139+
})
140+
);
141+
});
142+
```
143+
144+
### Per-Language Dependencies
145+
146+
| Language | Dependency | Notes |
147+
|---|---|---|
148+
| Node.js || No dependency; provide `onGetTraceContext` callback for outbound propagation |
149+
| Python | `opentelemetry-api` | Install with `pip install copilot-sdk[telemetry]` |
150+
| Go | `go.opentelemetry.io/otel` | Required dependency |
151+
| .NET || Uses built-in `System.Diagnostics.Activity` |
152+
153+
## Application-Level Instrumentation
154+
155+
The rest of this guide shows how to add your own OpenTelemetry spans around SDK operations using GenAI semantic conventions. This is complementary to the built-in `TelemetryConfig` above — you can use both together.
4156

5157
## Overview
6158

dotnet/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ new CopilotClient(CopilotClientOptions? options = null)
7878
- `Logger` - `ILogger` instance for SDK logging
7979
- `GitHubToken` - GitHub token for authentication. When provided, takes priority over other auth methods.
8080
- `UseLoggedInUser` - Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `CliUrl`.
81+
- `Telemetry` - OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
8182

8283
#### Methods
8384

@@ -546,6 +547,32 @@ var session = await client.CreateSessionAsync(new SessionConfig
546547
});
547548
```
548549

550+
## Telemetry
551+
552+
The SDK supports OpenTelemetry for distributed tracing. Provide a `Telemetry` config to enable trace export and automatic W3C Trace Context propagation.
553+
554+
```csharp
555+
var client = new CopilotClient(new CopilotClientOptions
556+
{
557+
Telemetry = new TelemetryConfig
558+
{
559+
OtlpEndpoint = "http://localhost:4318",
560+
},
561+
});
562+
```
563+
564+
**TelemetryConfig properties:**
565+
566+
- `OtlpEndpoint` - OTLP HTTP endpoint URL
567+
- `FilePath` - File path for JSON-lines trace output
568+
- `ExporterType` - `"otlp-http"` or `"file"`
569+
- `SourceName` - Instrumentation scope name
570+
- `CaptureContent` - Whether to capture message content
571+
572+
Trace context (`traceparent`/`tracestate`) is automatically propagated between the SDK and CLI on `CreateSessionAsync`, `ResumeSessionAsync`, and `SendAsync` calls, and inbound when the CLI invokes tool handlers.
573+
574+
No extra dependencies — uses built-in `System.Diagnostics.Activity`.
575+
549576
## User Input Requests
550577

551578
Enable the agent to ask questions to the user using the `ask_user` tool by providing an `OnUserInputRequest` handler:

0 commit comments

Comments
 (0)