microsoft/maf-tracing
Enable tracing and logging for Microsoft Agent Framework (MAF) workflows. Configures OpenTelemetry export to Azure Application Insights and/or a generic OTLP endpoint using environment variables. Adds required packages to requirements.txt. WHEN: enable tracing, add tracing, enable logging, add logging, configure telemetry, Application Insights for MAF, OTLP export, observe workflow, monitor agent workflow, trace agent framework, instrument MAF, add observability, trace workflow executions, debug workflow.
npx skills add https://github.com/microsoft/promptflow --skill maf-tracing
> Configure OpenTelemetry-based tracing for Microsoft Agent Framework workflows, exporting to Azure Application Insights and/or a generic OTLP endpoint.
Activate this skill when the user wants to:
agent-framework projectMAF automatically emits OpenTelemetry spans for every executor invocation, agent call, and LLM request. No instrumentation changes are needed inside Executor classes. You only need to:
configure_otel_providers() — activates MAF's built-in instrumentationThis must happen once at application startup, before any workflow.run() calls.
requirements.txt, .env, and entry-point script (e.g., main.py, app.py, run_*.py).tracer.start_span() calls inside @handler methods unless the user explicitly asks for custom spans.configure_otel_providers(), and both must happen BEFORE any workflow.run().requirements.txt — Append only the packages the user needs (see Packages section). Do not duplicate existing entries.os.environ or .env..env.example — Provide a template showing which environment variables are required.logging module to export via OpenTelemetry using opentelemetry-sdk log handler.| Variable | Required For | Description |
|----------|-------------|-------------|
| APPLICATIONINSIGHTS_CONNECTION_STRING | Application Insights | Connection string from Azure Portal → App Insights → Overview |
| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP export | Base URL of the OTLP collector (e.g., http://localhost:4318) |
| OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | OTLP export (traces only) | Overrides the base endpoint for trace signals only |
| OTEL_EXPORTER_OTLP_PROTOCOL | OTLP export | Protocol: http/protobuf (default) or grpc |
| OTEL_SERVICE_NAME | Optional | Service name shown in trace backends (defaults to Python process name) |
> OTEL_EXPORTER_OTLP_TRACES_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT for traces.
| Package | Version | When Needed |
|---------|---------|-------------|
| agent-framework | >=1.0.1 | Always (provides configure_otel_providers) |
| azure-monitor-opentelemetry | >=1.6.4 | Application Insights export |
| opentelemetry-exporter-otlp-proto-http | >=1.25.0 | OTLP/HTTP export |
| opentelemetry-exporter-otlp-proto-grpc | >=1.25.0 | OTLP/gRPC export (only if OTEL_EXPORTER_OTLP_PROTOCOL=grpc) |
| opentelemetry-sdk | >=1.25.0 | Custom spans or Python logging integration |
| python-dotenv | any | Loading .env files |
Use when the user wants to send traces to Azure Application Insights.
Required env var: APPLICATIONINSIGHTS_CONNECTION_STRING
Required packages:
azure-monitor-opentelemetry>=1.6.4
Setup code (add at the top of the entry-point script, before workflow.run()):
import os
from dotenv import load_dotenv
from azure.monitor.opentelemetry import configure_azure_monitor
from agent_framework.observability import configure_otel_providers
load_dotenv()
# Step 1: Configure Azure Monitor exporter (traces, metrics, logs → App Insights)
configure_azure_monitor(
connection_string=os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
)
# Step 2: Enable MAF's built-in instrumentation (executor, agent, LLM spans)
configure_otel_providers()
Use when the user wants to send traces to a generic OTLP-compatible backend (Jaeger, Grafana Tempo, Aspire Dashboard, etc.).
Required env var: OTEL_EXPORTER_OTLP_ENDPOINT
Required packages:
opentelemetry-sdk>=1.25.0
opentelemetry-exporter-otlp-proto-http>=1.25.0
Setup code:
import os
from dotenv import load_dotenv
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from agent_framework.observability import configure_otel_providers
load_dotenv()
# Step 1: Set up the OTLP exporter with a TracerProvider
resource = Resource.create({
"service.name": os.environ.get("OTEL_SERVICE_NAME", "maf-workflow"),
})
tracer_provider = TracerProvider(resource=resource)
otlp_exporter = OTLPSpanExporter(
endpoint=os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT"),
)
tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
trace.set_tracer_provider(tracer_provider)
# Step 2: Enable MAF's built-in instrumentation
configure_otel_providers()
Use when the user wants dual export — Application Insights for Azure-native monitoring plus an OTLP backend for local/third-party observability.
Required env vars: APPLICATIONINSIGHTS_CONNECTION_STRING, OTEL_EXPORTER_OTLP_ENDPOINT
Required packages:
azure-monitor-opentelemetry>=1.6.4
opentelemetry-sdk>=1.25.0
opentelemetry-exporter-otlp-proto-http>=1.25.0
Setup code:
import os
from dotenv import load_dotenv
from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from agent_framework.observability import configure_otel_providers
load_dotenv()
# Step 1a: Configure Azure Monitor (sets up its own TracerProvider internally)
configure_azure_monitor(
connection_string=os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
)
# Step 1b: Add OTLP exporter to the existing TracerProvider
otlp_endpoint = (
os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
)
if otlp_endpoint:
otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
tracer_provider: TracerProvider = trace.get_tracer_provider()
tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
# Step 2: Enable MAF's built-in instrumentation
configure_otel_providers()
Use when the user also wants Python logging calls to be exported alongside traces.
Additional packages (on top of Pattern A, B, or C):
opentelemetry-sdk>=1.25.0
Setup code (add after the exporter setup, before configure_otel_providers()):
import logging
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry._logs import set_logger_provider
# If using Application Insights, configure_azure_monitor() already handles log export.
# If using OTLP only, set up the OTLP log exporter:
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter(
endpoint=os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT"),
))
)
set_logger_provider(logger_provider)
# Bridge Python logging to OpenTelemetry
from opentelemetry.instrumentation.logging import LoggingInstrumentor
LoggingInstrumentor().instrument(set_logging_format=True)
> Note: When using azure-monitor-opentelemetry (Pattern A/C), configure_azure_monitor() already captures Python logs by default. The above is only needed for OTLP-only setups.
Ask the user or infer from context:
If the user says "tracing" without specifying a destination, default to Pattern C (both).
requirements.txtAppend the required packages. Do not duplicate existing entries. Example additions for Pattern C:
azure-monitor-opentelemetry>=1.6.4
opentelemetry-sdk>=1.25.0
opentelemetry-exporter-otlp-proto-http>=1.25.0
.env.exampleAdd the environment variables relevant to the chosen pattern:
# === Tracing & Observability ===
# Application Insights (Azure Portal → App Insights → Overview → Connection String)
APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=xxx;IngestionEndpoint=https://xxx.in.applicationinsights.azure.com/
# OTLP endpoint (e.g., Jaeger, Grafana Tempo, Aspire Dashboard)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# Optional: override service name in trace backends
OTEL_SERVICE_NAME=my-maf-workflow
Insert the setup code from the appropriate pattern at the top of the entry-point script (after imports, before any workflow.run() call). The setup must execute once at module load / application startup.
Placement rules:
asyncio.run(main()), place setup code inside main() before workflow.run().load_dotenv().http://localhost:16686).configure_azure_monitor() and/or OTLP exporter setup must happen BEFORE configure_otel_providers(). If reversed, MAF spans won't be exported.configure_otel_providers() must run BEFORE workflow.run() — Otherwise, executor-level spans are not generated.@handler method will create duplicate exporters and corrupt traces.configure_azure_monitor() creates its own TracerProvider — When combining with OTLP (Pattern C), add the OTLP exporter to the existing provider via trace.get_tracer_provider() rather than creating a new TracerProvider.configure_otel_providers() — Without this call, you'll see Application Insights or OTLP infrastructure telemetry but no MAF-specific spans (executor transitions, agent calls, LLM requests).APPLICATIONINSIGHTS_CONNECTION_STRING starts with InstrumentationKey= followed by a GUID. Do not confuse it with the Instrumentation Key alone.OTEL_EXPORTER_OTLP_ENDPOINT should be the base URL (e.g., http://localhost:4318). The SDK appends /v1/traces automatically. Do not include /v1/traces in the env var.http/protobuf (port 4318). If the collector uses gRPC (port 4317), set OTEL_EXPORTER_OTLP_PROTOCOL=grpc and install opentelemetry-exporter-otlp-proto-grpc instead."""Entry point for a MAF workflow with full tracing setup."""
import asyncio
import os
from dotenv import load_dotenv
load_dotenv()
def setup_tracing():
"""Configure telemetry exporters and MAF instrumentation. Call once at startup."""
from agent_framework.observability import configure_otel_providers
# Application Insights
appinsights_conn = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")
if appinsights_conn:
from azure.monitor.opentelemetry import configure_azure_monitor
configure_azure_monitor(connection_string=appinsights_conn)
# OTLP endpoint (optional, additive)
otlp_endpoint = (
os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
)
if otlp_endpoint:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
tracer_provider = trace.get_tracer_provider()
# If Azure Monitor already set a TracerProvider, reuse it; otherwise create one
if not isinstance(tracer_provider, TracerProvider):
resource = Resource.create({
"service.name": os.environ.get("OTEL_SERVICE_NAME", "maf-workflow"),
})
tracer_provider = TracerProvider(resource=resource)
trace.set_tracer_provider(tracer_provider)
tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
# Enable MAF's built-in spans (must be last)
configure_otel_providers()
async def main():
setup_tracing()
# Import and run your workflow here
from workflow import create_workflow
workflow = create_workflow()
result = await workflow.run("Hello, world!")
print(result.get_outputs())
if __name__ == "__main__":
asyncio.run(main())
Take microsoft/maf-tracing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.