How to Set Up OpenTelemetry Observability and Tracing for the Microsoft Agent Framework

The Microsoft Agent Framework provides a thin wrapper around OpenTelemetry that enables distributed tracing, logging, and metrics through environment variables or programmatic configuration using configure_otel_providers().

The Microsoft Agent Framework ships with built-in observability capabilities that make distributed tracing trivial for agent-based applications. By leveraging the agent_framework.observability module, developers can instrument their agents to emit OpenTelemetry data without boilerplate code. This implementation resides primarily in python/packages/core/agent_framework/observability.py and supports OTLP, Azure Monitor, and console exporters out of the box.

Architecture of the Observability Layer

The framework’s telemetry system is designed as a low-overhead, opt-in wrapper around standard OpenTelemetry SDKs. When ENABLE_INSTRUMENTATION is disabled, the framework skips tracing code paths entirely, ensuring zero performance impact in production builds.

Global Settings Management

The ObservabilitySettings class (lines 632–682 in observability.py) parses environment variables and .env files to store global flags that drive instrumentation. Key variables include:

  • ENABLE_INSTRUMENTATION – Master switch to activate telemetry collection
  • ENABLE_SENSITIVE_DATA – Allows logging of prompts and responses (disabled by default for privacy)
  • ENABLE_CONSOLE_EXPORTERS – Dumps spans and logs to stdout for local debugging
  • VS_CODE_EXTENSION_PORT – Enables integration with the VS Code OpenTelemetry extension
  • OTEL_SERVICE_NAME and OTEL_EXPORTER_OTLP_ENDPOINT – Standard OpenTelemetry variables

The Bootstrap Function

configure_otel_providers() (lines 979–1020) serves as the one-stop-shop initialization routine. It performs four distinct operations:

  1. Reads standard OTEL environment variables
  2. Initializes exporters (OTLP, Azure Monitor, console, or custom)
  3. Builds the global TracerProvider, MeterProvider, and LogRecordProcessor
  4. Invokes enable_instrumentation() to activate the framework’s telemetry layers

Call this function exactly once at application startup, before constructing any Agent or client objects.

Runtime Control

enable_instrumentation() (lines 953–975) toggles the global OBSERVABILITY_SETTINGS without creating exporters. This is useful when you have already configured OpenTelemetry via environment variables or third-party exporters and only need to activate the framework’s internal telemetry hooks.

Distributed Tracing for MCP

When an OpenTelemetry span is active, the framework automatically injects W3C traceparent and tracestate headers into the params._meta field of every MCP (Model Context Protocol) call. This enables end-to-end distributed tracing across agent boundaries and MCP servers without manual header management.

Four Configuration Patterns

The repository’s samples under python/samples/02-agents/observability/ demonstrate four distinct setup approaches.

For most deployments, set environment variables and call configure_otel_providers() with no arguments:


# main.py

from agent_framework.observability import configure_otel_providers

# Set in .env or CI environment:

# ENABLE_INSTRUMENTATION=true

# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# OTEL_SERVICE_NAME=my_agent_app

# ENABLE_SENSITIVE_DATA=true  # Optional

# ENABLE_CONSOLE_EXPORTERS=true  # Optional

configure_otel_providers()

This pattern automatically reads the standard OTEL variables and initializes the appropriate exporters.

Programmatic Exporter Configuration

For scenarios requiring explicit control over endpoints, compression, or authentication, pass instantiated exporter objects:


# custom_exporters.py

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

from agent_framework.observability import configure_otel_providers

exporters = [
    OTLPSpanExporter(endpoint="http://otel-collector:4317", compression="gzip"),
    OTLPLogExporter(endpoint="http://otel-collector:4317"),
    OTLPMetricExporter(endpoint="http://otel-collector:4317"),
]

configure_otel_providers(
    exporters=exporters,
    enable_sensitive_data=True
)

See the sample file configure_otel_providers_with_parameters.py for the complete working implementation.

Azure Monitor Integration

For Microsoft-centric observability stacks, integrate with Azure Monitor while still using the framework’s resource attributes:


# azure_monitor.py

from azure.monitor.opentelemetry import configure_azure_monitor
from agent_framework.observability import create_resource, enable_instrumentation

# Azure Monitor handles its own provider setup

configure_azure_monitor(
    connection_string="InstrumentationKey=YOUR_KEY",
    resource=create_resource(),  # Inherits OTEL_SERVICE_NAME and version

    enable_live_metrics=True,
)

# Activate framework telemetry layers if not using env vars

enable_instrumentation(enable_sensitive_data=False)

The create_resource() helper pulls service metadata from the same environment variables used by ObservabilitySettings, ensuring consistent attributes across custom and framework-generated spans.

Zero-Code Auto-Instrumentation

For DevOps pipelines or legacy codebases where modifying source is undesirable, use the OpenTelemetry CLI:


# Install the CLI tool

pip install opentelemetry-instrumentation

# Run with automatic instrumentation

opentelemetry-instrument python -m my_agent_app

The CLI initializes the global TracerProvider before your application starts. The Agent Framework’s get_tracer() function automatically detects this global provider and begins emitting spans. This pattern requires no code changes and is demonstrated in advanced_zero_code.py.

Step-by-Step Implementation Workflow

Regardless of which configuration pattern you choose, follow this sequence to ensure proper initialization:

  1. Install dependencies – Include opentelemetry-api, opentelemetry-sdk, and at least one exporter (OTLP, Azure Monitor, or console) in your requirements.
  2. Configure environment – Set OTEL_SERVICE_NAME and either OTEL_EXPORTER_OTLP_ENDPOINT or Azure Monitor connection strings.
  3. Bootstrap observability – Call configure_otel_providers() once at application startup, before creating any Agent instances.
  4. Activate instrumentation – This happens automatically via configure_otel_providers(), or manually via enable_instrumentation() if you configured OpenTelemetry separately.
  5. Execute agents – The framework automatically emits spans for Chat, Embedding, and MCP calls. For custom spans, call get_tracer() and use the standard OpenTelemetry start_as_current_span() API.

Summary

  • The Agent Framework’s observability layer is defined in python/packages/core/agent_framework/observability.py and provides zero-overhead telemetry when disabled.
  • configure_otel_providers() handles initialization of exporters, providers, and processors in a single call (lines 979–1020).
  • ObservabilitySettings manages configuration through ENABLE_INSTRUMENTATION, ENABLE_SENSITIVE_DATA, and standard OTEL environment variables.
  • Distributed tracing across MCP servers works automatically via W3C trace context injection into params._meta.
  • Four configuration patterns are supported: environment variables, programmatic exporters, Azure Monitor, and zero-code CLI instrumentation.

Frequently Asked Questions

How do I completely disable telemetry in production?

Set ENABLE_INSTRUMENTATION=false or simply do not call configure_otel_providers(). According to the source code in observability.py, when this flag is false, the framework skips all tracing code paths entirely, resulting in zero runtime overhead.

Can I export traces to both OTLP and the console simultaneously?

Yes. Pass multiple exporters to configure_otel_providers() or set ENABLE_CONSOLE_EXPORTERS=true alongside your OTLP endpoint. The function accepts a list of exporter objects and will configure a BatchSpanProcessor for each, writing to both destinations concurrently.

Does the framework log sensitive data like prompts and responses?

Only if explicitly enabled. The ENABLE_SENSITIVE_DATA environment variable (or the enable_sensitive_data=True parameter) controls whether input/output content is attached to spans. By default, this is disabled to prevent PII leakage in production trace data.

How does trace propagation work with external MCP servers?

When a span is active, the framework automatically adds traceparent and tracestate headers to the params._meta dictionary of every MCP request. External servers that support W3C trace context can extract these headers to continue the trace, enabling end-to-end visibility across agent boundaries without manual header manipulation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →