Wire up A365 observability instrumentation for Agent Framework sample#308
Open
biswapm wants to merge 4 commits into
Open
Wire up A365 observability instrumentation for Agent Framework sample#308biswapm wants to merge 4 commits into
biswapm wants to merge 4 commits into
Conversation
Adds the minimum code needed for the Microsoft.OpenTelemetry distro
Agent365 exporter to actually emit traces that show up in the
Microsoft Admin Center Advanced Hunting view.
Without these changes, the exporter saw gen_ai spans but dropped them
all because they had no tenant/agent identity attributes, and even when
identity was present no InvokeAgent parent event was emitted, so MAC
had nothing to render.
Changes in Agent/MyAgent.cs:
- Inject IExporterTokenCache<AgenticTokenStruct> so the exporter can
obtain an OBO token per (agent, tenant) tuple.
- At the start of OnMessageAsync, set tenant + agent baggage via
BaggageBuilder so the distro's ActivityProcessor copies the IDs
onto every child span (gen_ai/InferenceCall/ExecuteToolBySDK).
Tenant comes from Activity.Conversation/Recipient.TenantId, agent
from Activity.GetAgenticInstanceId() (for agentic requests).
- Call _agentTokenCache.RegisterObservability(agentId, tenantId, ...)
so the exporter has a token resolver for this identity, eliminating
"No token obtained" warnings for the primary identity.
- Wrap the LLM RunStreamingAsync call in InvokeAgentScope.Start(...)
with AgentDetails + CallerDetails (from Activity.From). This is
what emits the InvokeAgent event that MAC needs as the parent
record for the trace UI; RecordInputMessages / RecordOutputMessages
capture the user prompt and assistant reply on the scope.
- Set ChatClientAgentOptions.Id = chatAgentId and .Name = configured
name so the AI SDK's auto-instrumentation tags gen_ai spans with
the real agent instance ID instead of a fresh N-format GUID per
turn (which previously produced orphan identity groups the exporter
couldn't authenticate).
Pattern mirrors A365OtelWrapper.InvokeObservedAgentOperation from the
official Microsoft.OpenTelemetry.Agent365.Demo example.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR wires Agent365 observability into the .NET Agent Framework sample so A365 exporter spans can be associated with agent/tenant identity and emit an InvokeAgent parent event.
Changes:
- Adds observability imports, token-cache injection, baggage setup, and token registration in
MyAgent. - Wraps streaming LLM execution in
InvokeAgentScopeand records input/output messages. - Sets
ChatClientAgentOptions.IdandNameto align AI SDK instrumentation tags with configured agent identity.
Agent 365 sample validation: copyright header is present and no Kairo references were found in the changed source file. Two blocking issues were filed around non-agentic identity resolution and deriving a URI from display text.
…Name Build the InvokeAgentScope endpoint Uri from the AgentBlueprintId (a GUID, always URI-safe) under the RFC 2606 reserved `.invalid` TLD, instead of slugifying the display AgentName. AgentName is free-form configuration text and may contain characters invalid in a hostname (apostrophes, parentheses, slashes, etc.), which would throw UriFormatException during message handling before the LLM call.
…observability when unavailable
Previously, OnMessageAsync fell back to Guid.Empty for non-agentic
requests (Playground / WebChat with the OBO auth handler). That meant
the baggage, token-cache registration, and ChatClientAgentOptions.Id
were all set to a synthetic identity the Agent365 exporter could not
authenticate — exactly the orphan "No token obtained" group we set out
to fix.
Changes in Agent/MyAgent.cs:
- Resolve agent id via the OBO token (UserAuthorization.GetTurnTokenAsync +
Utility.ResolveAgentIdentity) for non-agentic requests, mirroring
A365OtelWrapper.ResolveTenantAndAgentId in the official distro demo.
- Compute hasObservabilityIdentity = both agent id AND tenant id are
real (non-empty); skip BaggageBuilder, RegisterObservability and
InvokeAgentScope when false. This stops polluting traces with
Guid.Empty-grouped spans the exporter would drop anyway.
- Make chatAgentId parameter to GetClientAgent nullable; only set
ChatClientAgentOptions.Id when a real agent id was resolved.
- Forward cancellationToken to UserAuthorization.GetTurnTokenAsync.
- Tighten Activity null-state with `?.` to satisfy nullable analysis.
…settings template
Adds two changes to the appsettings.json template:
1. `EnableAgent365Exporter: true` at the root. The SDK's
Microsoft.Agents.A365.Observability.Runtime.Builder defaults this
to false when the key is absent — without setting it to true, all
the observability code added in this PR is wired up but no traces
are POSTed to the Agent365 backend. Required for the sample to
actually export traces out of the box.
2. Debug-level logging for the four observability-relevant categories:
- OpenTelemetry — SDK pipeline diagnostics
- Microsoft.OpenTelemetry — distro startup and instrumentation
- Microsoft.Agents.A365.Observability — exporter activity: span
partitioning, token resolution, HTTP export status codes.
The key signal for verifying traces reach the backend.
- Microsoft.Agents.A365.Runtime — auth identity resolution,
tool dispatch, and related runtime diagnostics.
These make the sample observability-ready out of the box — running
it shows exporter batches, identity groups, and HTTP 200 responses
in the console, so developers can immediately confirm whether
traces are flowing.
Skipped: System.Net.Http.HttpClient.Default at Trace level. That setting
logs bearer tokens and response bodies in plain text and is not
appropriate as a template default — devs can opt in when diagnosing
specific HTTP issues.
gwharris7
approved these changes
May 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Microsoft.OpenTelemetrydistro Agent365 exporter to actually emit traces that show up in the Microsoft Admin Center Advanced Hunting view for the .NET Agent Framework sample agent.gen_aispans but dropped them all (missing tenant/agent identity), and even when identity was present noInvokeAgentparent event was emitted, so MAC had nothing to render.Microsoft.OpenTelemetry— no direct package refs toMicrosoft.Agents.A365.Observability.*(per distro guidance to avoid CS0433 collisions).Changes in
Agent/MyAgent.csIExporterTokenCache<AgenticTokenStruct>so the exporter can obtain an OBO token per(agent, tenant)tuple.OnMessageAsync, set tenant + agent baggage viaBaggageBuilderso the distro'sActivityProcessorcopies the IDs onto every child span (gen_ai / InferenceCall / ExecuteToolBySDK). Tenant fromActivity.Conversation/Recipient.TenantId, agent fromActivity.GetAgenticInstanceId()(for agentic requests)._agentTokenCache.RegisterObservability(agentId, tenantId, ...)so the exporter has a token resolver for this identity — eliminates "No token obtained" warnings for the primary identity.RunStreamingAsynccall inInvokeAgentScope.Start(...)withAgentDetails+CallerDetails(fromActivity.From). This emits theInvokeAgentevent that MAC needs as the parent record for the trace UI;RecordInputMessages/RecordOutputMessagescapture the user prompt and assistant reply on the scope.ChatClientAgentOptions.Id = chatAgentIdand.Name = configured nameso the AI SDK's auto-instrumentation tags gen_ai spans with the real agent instance ID instead of a fresh N-format GUID per turn (which previously produced orphan identity groups the exporter couldn't authenticate).Pattern mirrors
A365OtelWrapper.InvokeObservedAgentOperationfrom the officialMicrosoft.OpenTelemetry.Agent365.Demoexample in the distro repo.Test plan
dotnet build dotnet/agent-framework/sample-agent/AgentFrameworkSampleAgent.csprojsucceedsa365 setup allSending chunk ... to .../observability/tenants/.../tracesandHTTP 200 exporting spans(with ax-ms-correlation-id)Partitioned into N identity groupsshows the primary agent instance IDNo token obtainedwarning is gone for the primary identityInvokeAgentrows appear alongside the existingInferenceCall/ExecuteToolBySDKevents.