diff --git a/docs/decisions/0003-agent-opentelemetry-instrumentation.md b/docs/decisions/0003-agent-opentelemetry-instrumentation.md index 863387b5cb..e7c95c749c 100644 --- a/docs/decisions/0003-agent-opentelemetry-instrumentation.md +++ b/docs/decisions/0003-agent-opentelemetry-instrumentation.md @@ -124,21 +124,42 @@ The implementation is validated through: ### Usage Example +The name passed to `TracerProviderBuilder.AddSource` must match the source name the agent emits under, otherwise the +provider silently receives no agent spans. When no source name is supplied to `UseOpenTelemetry`, the agent emits under +`OpenTelemetryAgent.DefaultSourceName`, so that is the value to register: + ```csharp // Create TracerProvider using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource(AgentOpenTelemetryConsts.DefaultSourceName) + .AddSource(OpenTelemetryAgent.DefaultSourceName) .AddConsoleExporter() .Build(); // Create and wrap agent with telemetry var baseAgent = new ChatClientAgent(chatClient, options); -using var telemetryAgent = baseAgent.WithOpenTelemetry(); +using var telemetryAgent = baseAgent.AsBuilder() + .UseOpenTelemetry() + .Build(); // Use agent normally - telemetry is captured automatically var response = await telemetryAgent.RunAsync(messages); ``` +To emit under a custom source name, pass the same value to both calls: + +```csharp +const string SourceName = "MyCompany.MyAgent"; + +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(SourceName) + .AddConsoleExporter() + .Build(); + +using var telemetryAgent = baseAgent.AsBuilder() + .UseOpenTelemetry(sourceName: SourceName) + .Build(); +``` + ### Relationship to Microsoft.Extensions.AI This implementation follows the exact patterns established by Microsoft.Extensions.AI's OpenTelemetry instrumentation, ensuring consistency across the AI ecosystem and leveraging proven patterns for telemetry integration. diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs index 90c0ec59eb..fa221b81a0 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs @@ -41,6 +41,18 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable // inner agent not directly but rather via OpenTelemetryChatClient, which wraps a ForwardingChatClient that in turn // calls back into the inner agent. + /// + /// Gets the default name used by when no source + /// name is supplied to the constructor. + /// + /// + /// Pass this value to the tracing pipeline (for example, TracerProviderBuilder.AddSource) to subscribe to + /// the spans emitted by agents that use the default source name, instead of hardcoding the literal name. This is + /// a property rather than a constant so that the value is read at run time: a consumer that upgrades the package + /// picks up the current source name without recompiling. + /// + public static string DefaultSourceName => OpenTelemetryConsts.DefaultSourceName; + /// The providing the bulk of the telemetry. private readonly OpenTelemetryChatClient _otelClient; /// The provider name extracted from . @@ -62,8 +74,9 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// Initializes a new instance of the class. /// The underlying to be augmented with telemetry capabilities. /// - /// An optional source name that will be used to identify telemetry data from this agent. - /// If not provided, a default source name will be used for telemetry identification. + /// An optional source name used to identify telemetry data from this agent. + /// When specified, register the same value with TracerProviderBuilder.AddSource so the tracing pipeline + /// subscribes to these spans. When omitted, is used. /// /// is . /// @@ -80,8 +93,9 @@ public OpenTelemetryAgent(AIAgent innerAgent, string? sourceName = null) /// Initializes a new instance of the class. /// The underlying to be augmented with telemetry capabilities. /// - /// An optional source name that will be used to identify telemetry data from this agent. - /// If not provided, a default source name will be used for telemetry identification. + /// An optional source name used to identify telemetry data from this agent. + /// When specified, register the same value with TracerProviderBuilder.AddSource so the tracing pipeline + /// subscribes to these spans. When omitted, is used. /// /// /// When and the inner agent is a , the underlying diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgentBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgentBuilderExtensions.cs index 8f83a8dda1..0d27ef8d17 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgentBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgentBuilderExtensions.cs @@ -15,8 +15,9 @@ public static class OpenTelemetryAgentBuilderExtensions /// /// The to which OpenTelemetry support will be added. /// - /// An optional source name that will be used to identify telemetry data from this agent. - /// If not specified, a default source name will be used. + /// An optional source name used to identify telemetry data from this agent. + /// When specified, register the same value with TracerProviderBuilder.AddSource so the tracing pipeline + /// subscribes to these spans. When omitted, is used. /// /// /// An optional callback that provides additional configuration of the instance. diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs index 79e3418b86..e6a1c67077 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs @@ -873,6 +873,17 @@ public async Task AutoWireChatClient_UserFactoryAddsOwnOTel_CoexistsWithBelowFic Assert.Equal(2, activities.Count(a => string.Equals(a.GetTagItem("gen_ai.operation.name") as string, "chat", StringComparison.Ordinal))); } + [Fact] + public void DefaultSourceName_ReturnsDocumentedSourceName() + { + // Callers pass this to TracerProviderBuilder.AddSource, so it must stay in sync with the source name the + // agent emits spans under, which Ctor_NullOrWhitespaceSourceName_AutoWiredChatClientUsesDefaultSource_Async + // pins to the same literal. Comparing against the literal here guards a rename of the internal constant. + + // Arrange & Act & Assert + Assert.Equal("Experimental.Microsoft.Agents.AI", OpenTelemetryAgent.DefaultSourceName); + } + [Theory] [InlineData(null)] [InlineData("")]