step08 · Observability (OpenTelemetry)

How to turn every agent run into an OpenTelemetry span with the SDK's otelprovider middleware.

Part 18 of 91 Microsoft Agent Framework Go — Every Lesson

What this lesson demonstrates

Same Joker agent as step01, but the middleware in the Config is now otelprovider.NewMiddleware(...) — the framework’s built-in OpenTelemetry instrumentation. It opens a span around each run, tags it with gen_ai.* attributes (agent name, provider, operation), records any error onto the span, and closes it when the run finishes. Point it at any OTel backend and you get distributed traces for free.

The core code

Observability is just another middleware slotted into Config.Middlewares, and it emits to whatever TracerProvider is globally registered:

rec := newSpanRecorder()
otellib.SetTracerProvider(rec)

a := newJoker(demo.Endpoint(), demo.Model(), cred,
    otelprovider.NewMiddleware(otelprovider.MiddlewareConfig{}))

// each run — collected or streamed — produces one invoke_agent span
resp, err := a.RunText(ctx, "Tell me a joke about a pirate.").Collect()

What to notice

How it maps to Azure AI Foundry

The gen_ai.* attributes the middleware sets follow OpenTelemetry’s semantic conventions for generative-AI, so traces from a Foundry-backed agent line up with the rest of your observability stack. In production you register the OTel SDK’s TracerProvider pointed at Jaeger, an OTLP collector, or a console exporter — and the middleware code is unchanged. This port swaps that exporter for a tiny in-process spanRecorder (built only from the core OTel API pinned in the tutorial’s go.mod), so the lesson stays offline; the concept — one middleware turns runs into spans — is identical. The offline tests drive the OTel API exactly as the middleware does and assert a finished span was captured with its name, attributes, and error status intact.

Run it

go run ./tutorial/02-agents/agents/step08_observability

You’ll see two jokes then a ===== captured spans ===== block. The program needs Foundry; the span-capture tests run offline, and the live call is gated behind AF_LIVE=1.


Next: step09 · Dependency Injection

Sources & References

The Go SDK this lesson exercises
OpenTelemetry observability for agent runs