Skip to content

Commit 3732a29

Browse files
committed
Propagate trace context to the extension process
1 parent a0b4908 commit 3732a29

File tree

3 files changed

+50
-6
lines changed

3 files changed

+50
-6
lines changed

cli/azd/cmd/extensions.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ func (a *extensionAction) Run(ctx context.Context) (*actions.ActionResult, error
176176
fmt.Sprintf("AZD_ACCESS_TOKEN=%s", jwtToken),
177177
)
178178

179+
// Propagate trace context to the extension process
180+
if traceEnv := tracing.Environ(ctx); len(traceEnv) > 0 {
181+
allEnv = append(allEnv, traceEnv...)
182+
}
183+
179184
options := &extensions.InvokeOptions{
180185
Args: a.args,
181186
Env: allEnv,

cli/azd/docs/extension-framework.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,42 @@ The build process automatically creates binaries for multiple platforms and arch
334334
> [!NOTE]
335335
> Build times may vary depending on your hardware and extension complexity.
336336
337+
### Distributed Tracing
338+
339+
`azd` uses OpenTelemetry and W3C Trace Context for distributed tracing. To ensure your extension's operations are correctly correlated with the parent `azd` process, you must hydrate the context in your extension's entry point.
340+
341+
**Update `main.go`:**
342+
343+
```go
344+
import (
345+
"context"
346+
"os"
347+
"github.com/azure/azure-dev/cli/azd/pkg/azdext"
348+
)
349+
350+
func main() {
351+
ctx := context.Background()
352+
353+
// Hydrate context with traceparent from environment if present
354+
// This ensures the extension process participates in the active trace
355+
if traceparent := os.Getenv("TRACEPARENT"); traceparent != "" {
356+
ctx = azdext.ContextFromTraceParent(ctx, traceparent)
357+
}
358+
359+
rootCmd := cmd.NewRootCommand()
360+
361+
if err := rootCmd.ExecuteContext(ctx); err != nil {
362+
// Handle error
363+
}
364+
}
365+
```
366+
367+
The `ExtensionHost` automatically handles trace propagation for all gRPC communication:
368+
1. **Outgoing**: Traces are injected into messages sent back to `azd` core.
369+
2. **Incoming**: Traces from `azd` core are extracted and injected into the `context.Context` passed to your handlers.
370+
371+
This ensures that calls to Azure SDKs (which support `TRACEPARENT`) within your handlers will automatically be correlated.
372+
337373
### Developer Extension
338374

339375
The easiest way to get started building extensions is to install the `azd` Developer extension.

cli/azd/pkg/azdext/trace_context.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import (
1111

1212
const traceparentKey = "traceparent"
1313

14-
// GetTraceParentFromContext extracts the current span context and formats it as a W3C traceparent string.
15-
// Returns empty string if no valid span context is found.
16-
func GetTraceParentFromContext(ctx context.Context) string {
17-
carrier := propagation.MapCarrier{}
18-
propagation.TraceContext{}.Inject(ctx, carrier)
19-
return carrier.Get(traceparentKey)
14+
// ContextFromTraceParent creates a new context with the span context extracted from the traceparent string.
15+
func ContextFromTraceParent(ctx context.Context, traceparent string) context.Context {
16+
if traceparent == "" {
17+
return ctx
18+
}
19+
carrier := propagation.MapCarrier{
20+
traceparentKey: traceparent,
21+
}
22+
return propagation.TraceContext{}.Extract(ctx, carrier)
2023
}

0 commit comments

Comments
 (0)