Edit

Enable telemetry and tracing for Voice Live

The Voice Live SDK includes built-in OpenTelemetry instrumentation that automatically traces connection, send, and receive operations. Use telemetry to monitor session health, diagnose latency issues, and correlate Voice Live operations with your application traces.

What gets traced

When you enable telemetry, the SDK automatically creates OpenTelemetry spans for:

Operation Span name prefix Description
WebSocket connect connect Connection establishment and lifecycle
Send events send Session updates, conversation items, response requests
Receive events recv Server events including responses, VAD, and errors

Prerequisites

Reference documentation | Package (PyPi) | Additional samples on GitHub

Additional prerequisites

  • azure-ai-voicelive package version 1.2.0 or later.

  • Install the telemetry dependencies:

    pip install opentelemetry-sdk azure-core-tracing-opentelemetry
    

    For Azure Monitor export, install instead:

    pip install azure-monitor-opentelemetry
    

Enable console tracing

Add the following code to your application before calling connect(). This is the smallest code change to start seeing Voice Live spans in your terminal.

from azure.core.settings import settings

# Step 1: Tell azure-core to use OpenTelemetry for tracing.
settings.tracing_implementation = "opentelemetry"

# Step 2: Configure a TracerProvider with a console exporter.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    SimpleSpanProcessor,
    ConsoleSpanExporter,
)

tracer_provider = TracerProvider()
tracer_provider.add_span_processor(
    SimpleSpanProcessor(ConsoleSpanExporter())
)
trace.set_tracer_provider(tracer_provider)

# Step 3: Enable the VoiceLive instrumentor.
from azure.ai.voicelive.telemetry import VoiceLiveInstrumentor

os.environ.setdefault(
    "AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING", "true"
)
VoiceLiveInstrumentor().instrument()

All connect, send, and recv operations now produce spans printed to stdout.

Export traces to Azure Monitor

To send traces to Application Insights instead of the console, replace the console setup with Azure Monitor configuration. Set the APPLICATIONINSIGHTS_CONNECTION_STRING environment variable, then add the following code.

from azure.core.settings import settings

# Step 1: Tell azure-core to use OpenTelemetry for tracing.
settings.tracing_implementation = "opentelemetry"

# Step 2: Configure Azure Monitor as the trace exporter.
from opentelemetry import trace
from azure.monitor.opentelemetry import configure_azure_monitor

application_insights_connection_string = os.environ[
    "APPLICATIONINSIGHTS_CONNECTION_STRING"
]
configure_azure_monitor(
    connection_string=application_insights_connection_string
)

# Step 3: Enable the VoiceLive instrumentor.
from azure.ai.voicelive.telemetry import VoiceLiveInstrumentor

os.environ.setdefault(
    "AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING", "true"
)
VoiceLiveInstrumentor().instrument()

View the results in the Tracing tab in your Azure AI Foundry project page or in Application Insights.

Add custom span attributes

To correlate Voice Live traces with your application context (session IDs, user IDs, or request identifiers), create a custom SpanProcessor.

class CustomAttributeSpanProcessor(SpanProcessor):
    """Add application-specific attributes to every span."""

    def on_start(self, span: Span, parent_context=None):
        # Add a session identifier to all spans.
        span.set_attribute(
            "app.session_id", "my-session-123"
        )

        # Tag send spans with extra context.
        if span.name and span.name.startswith("send"):
            span.set_attribute(
                "app.send.context", "user-interaction"
            )

        # Tag receive spans with a priority level.
        if span.name and span.name.startswith("recv"):
            span.set_attribute(
                "app.recv.priority", "normal"
            )

    def on_end(self, span: ReadableSpan):
        pass

Register the custom processor with the global tracer provider after your standard telemetry setup:

# Register the custom processor with the global provider.
provider = cast(TracerProvider, trace.get_tracer_provider())
provider.add_span_processor(CustomAttributeSpanProcessor())

Enable content recording

Content recording captures full message payloads (send and receive) in span events as gen_ai.event.content attributes. This is useful for debugging but can capture personal data.

Caution

Content recording may capture personal data. Only enable in development or controlled environments.

# Option 1: Enable via environment variable.
# os.environ[
#     "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"
# ] = "true"

# Option 2: Enable programmatically.
VoiceLiveInstrumentor().instrument(
    enable_content_recording=True
)

Reference documentation | Package (NuGet) | Additional samples on GitHub

Additional prerequisites

  • Azure.AI.VoiceLive package version 1.1.0 or later.

  • .NET 10.0 or later.

  • Install the telemetry dependencies:

    dotnet add package OpenTelemetry
    dotnet add package OpenTelemetry.Exporter.Console
    

    For Azure Monitor export, install instead:

    dotnet add package Azure.Monitor.OpenTelemetry.Exporter
    

Enable console tracing

Register an OpenTelemetry tracer provider that listens to the Azure.AI.VoiceLive activity source before you construct the VoiceLiveClient. The SDK emits spans automatically when a provider is present.

using Azure.AI.VoiceLive;
using Azure.Identity;
using OpenTelemetry;
using OpenTelemetry.Trace;

// Register an OpenTelemetry provider before constructing the VoiceLive client.
using TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("Azure.AI.VoiceLive")
    .AddConsoleExporter()
    .Build();

string endpoint = Environment.GetEnvironmentVariable("AZURE_VOICELIVE_ENDPOINT")!;
VoiceLiveClient client = new(new Uri(endpoint), new DefaultAzureCredential());

// All connect, send, and receive operations now produce spans on the console.
VoiceLiveSession session = await client.StartSessionAsync("gpt-realtime");

All connect, send, and receive operations now produce spans printed to stdout.

Reference: OpenTelemetry .NET | VoiceLiveClient

Export traces to Azure Monitor

To send traces to Application Insights instead of the console, replace the console exporter with the Azure Monitor exporter. Set the APPLICATIONINSIGHTS_CONNECTION_STRING environment variable, then add the following code.

using Azure.AI.VoiceLive;
using Azure.Identity;
using Azure.Monitor.OpenTelemetry.Exporter;
using OpenTelemetry;
using OpenTelemetry.Trace;

string connectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING")!;

using TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("Azure.AI.VoiceLive")
    .AddAzureMonitorTraceExporter(options => options.ConnectionString = connectionString)
    .Build();

string endpoint = Environment.GetEnvironmentVariable("AZURE_VOICELIVE_ENDPOINT")!;
VoiceLiveClient client = new(new Uri(endpoint), new DefaultAzureCredential());
VoiceLiveSession session = await client.StartSessionAsync("gpt-realtime");

View the results in the Tracing tab in your Foundry project page or in Application Insights.

Reference: Azure Monitor OpenTelemetry exporter for .NET

Add custom span attributes

To correlate Voice Live traces with your application context (session IDs, user IDs, or request identifiers), implement a processor that derives from BaseProcessor<Activity>.

using System.Diagnostics;
using OpenTelemetry;

internal sealed class CustomAttributesProcessor : BaseProcessor<Activity>
{
    private readonly string _sessionId;
    private readonly string _userId;

    public CustomAttributesProcessor(string sessionId, string userId)
    {
        _sessionId = sessionId;
        _userId = userId;
    }

    public override void OnStart(Activity activity)
    {
        activity.SetTag("app.session_id", _sessionId);
        activity.SetTag("app.user_id", _userId);
    }
}

Register the custom processor with the tracer provider builder after adding the Azure.AI.VoiceLive source:

using TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("Azure.AI.VoiceLive")
    .AddProcessor(new CustomAttributesProcessor(sessionId: "sess-123", userId: "user-abc"))
    .AddConsoleExporter()
    .Build();

Enable content recording

Content recording captures full message payloads (send and receive) on span events. This is useful for debugging but can capture personal data.

Caution

Content recording may capture personal data. Only enable in development or controlled environments.

Set the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable to true before starting your application. No code changes are required.

export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

When enabled, the SDK attaches event payloads as gen_ai.event.content attributes on the corresponding spans.

Reference documentation | Package (Maven) | Additional samples on GitHub

Additional prerequisites

  • azure-ai-voicelive package version 1.0.0 or later.

  • Java Development Kit (JDK) version 8 or later.

  • Add the OpenTelemetry dependencies to your pom.xml:

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
        <version>1.45.0</version>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-logging</artifactId>
        <version>1.45.0</version>
    </dependency>
    

    For Azure Monitor export, add:

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-monitor-opentelemetry-exporter</artifactId>
        <version>1.0.0-beta.31</version>
    </dependency>
    

Enable console tracing

Register a global OpenTelemetry instance with a span exporter before constructing the VoiceLiveAsyncClient. The SDK defaults to GlobalOpenTelemetry.getOrNoop(), so tracing is picked up automatically once a global instance exists.

import com.azure.ai.voicelive.VoiceLiveAsyncClient;
import com.azure.ai.voicelive.VoiceLiveClientBuilder;
import com.azure.identity.DefaultAzureCredentialBuilder;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;

// 1. Register a global OpenTelemetry instance BEFORE building any client.
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
    .build();

OpenTelemetrySdk.builder()
    .setTracerProvider(tracerProvider)
    .buildAndRegisterGlobal();

// 2. Build the client — it picks up GlobalOpenTelemetry automatically.
String endpoint = System.getenv("AZURE_VOICELIVE_ENDPOINT");
VoiceLiveAsyncClient client = new VoiceLiveClientBuilder()
    .endpoint(endpoint)
    .credential(new DefaultAzureCredentialBuilder().build())
    .buildAsyncClient();

All connect, send, and receive operations now produce spans printed to stdout.

Tip

If you attach the OpenTelemetry Java agent (-javaagent:opentelemetry-javaagent.jar), the global instance is registered automatically and no code changes are required.

Reference: OpenTelemetry Java | VoiceLiveClientBuilder

Export traces to Azure Monitor

To send traces to Application Insights instead of the console, replace the logging exporter with the Azure Monitor exporter. Set the APPLICATIONINSIGHTS_CONNECTION_STRING environment variable, then add the following code.

import com.azure.ai.voicelive.VoiceLiveAsyncClient;
import com.azure.ai.voicelive.VoiceLiveClientBuilder;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.monitor.opentelemetry.exporter.AzureMonitorExporterBuilder;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;

String connectionString = System.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING");

SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .addSpanProcessor(BatchSpanProcessor.builder(
        new AzureMonitorExporterBuilder()
            .connectionString(connectionString)
            .buildTraceExporter())
        .build())
    .build();

OpenTelemetrySdk.builder()
    .setTracerProvider(tracerProvider)
    .buildAndRegisterGlobal();

String endpoint = System.getenv("AZURE_VOICELIVE_ENDPOINT");
VoiceLiveAsyncClient client = new VoiceLiveClientBuilder()
    .endpoint(endpoint)
    .credential(new DefaultAzureCredentialBuilder().build())
    .buildAsyncClient();

View the results in the Tracing tab in your Foundry project page or in Application Insights.

Reference: Azure Monitor OpenTelemetry exporter for Java

Add custom span attributes

To correlate Voice Live traces with your application context (session IDs, user IDs, or request identifiers), implement a custom SpanProcessor that adds attributes when each span starts.

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;

final class CustomAttributesProcessor implements SpanProcessor {
    private final String sessionId;
    private final String userId;

    CustomAttributesProcessor(String sessionId, String userId) {
        this.sessionId = sessionId;
        this.userId = userId;
    }

    @Override
    public void onStart(Context parentContext, ReadWriteSpan span) {
        span.setAttribute(AttributeKey.stringKey("app.session_id"), sessionId);
        span.setAttribute(AttributeKey.stringKey("app.user_id"), userId);
    }

    @Override
    public boolean isStartRequired() { return true; }

    @Override
    public void onEnd(ReadableSpan span) { }

    @Override
    public boolean isEndRequired() { return false; }
}

Register the custom processor with the tracer provider before registering the global OpenTelemetry instance:

SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .addSpanProcessor(new CustomAttributesProcessor("sess-123", "user-abc"))
    .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
    .build();

OpenTelemetrySdk.builder()
    .setTracerProvider(tracerProvider)
    .buildAndRegisterGlobal();

Reference: OpenTelemetry Java

Enable content recording

Content recording captures full message payloads (send and receive) on span events. This is useful for debugging but can capture personal data.

Caution

Content recording may capture personal data. Only enable in development or controlled environments.

Set the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable to true before starting your application. No code changes are required.

export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

When enabled, the SDK attaches event payloads as gen_ai.event.content attributes on the corresponding spans.

Reference documentation | Package (npm) | Additional samples on GitHub

Additional prerequisites

  • @azure/ai-voicelive package version 1.0.0 or later.

  • Node.js version 18 or later.

  • Install the telemetry dependencies:

    npm install @opentelemetry/api @opentelemetry/sdk-trace-node @azure/core-tracing
    

    For Azure Monitor export, install instead:

    npm install @azure/monitor-opentelemetry-exporter
    

    For browser-based applications, use @opentelemetry/sdk-trace-web instead of @opentelemetry/sdk-trace-node.

Enable console tracing

First register an OpenTelemetry provider, then bridge @azure/core-tracing into OpenTelemetry via useInstrumenter() so the SDK emits spans. Add this code before constructing the VoiceLiveClient.

import {
  NodeTracerProvider,
  SimpleSpanProcessor,
  ConsoleSpanExporter,
} from "@opentelemetry/sdk-trace-node";
import { useInstrumenter } from "@azure/core-tracing";
import { trace, context } from "@opentelemetry/api";
import { VoiceLiveClient } from "@azure/ai-voicelive";
import { DefaultAzureCredential } from "@azure/identity";

// 1. Configure OpenTelemetry with a console exporter.
const provider = new NodeTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
});
provider.register();

// 2. Bridge @azure/core-tracing into OpenTelemetry.
useInstrumenter({
  startSpan(name, spanOptions) {
    const ctx = spanOptions.tracingContext ?? context.active();
    const tracer = trace.getTracer(
      spanOptions.packageName ?? "@azure/ai-voicelive",
      spanOptions.packageVersion,
    );
    const span = tracer.startSpan(name, { attributes: spanOptions.spanAttributes, kind: 0 }, ctx);
    return {
      span: {
        end() { span.end(); },
        setStatus(s) {
          if (s.status === "error") span.setStatus({ code: 2, message: String(s.error ?? "") });
        },
        setAttribute(k, v) { span.setAttribute(k, v); },
        isRecording() { return span.isRecording(); },
        recordException(e) { span.recordException(e); },
      },
      tracingContext: trace.setSpan(ctx, span),
    };
  },
  withContext(ctx, fn, ...args) { return context.with(ctx, fn, undefined, ...args); },
  parseTraceparentHeader() { return undefined; },
  createRequestHeaders() { return {}; },
});

// 3. Use VoiceLive as normal — spans are emitted automatically.
const client = new VoiceLiveClient(
  process.env.AZURE_VOICELIVE_ENDPOINT,
  new DefaultAzureCredential(),
);
const session = client.createSession("gpt-realtime");
await session.connect();

All connect, send, and receive operations now produce spans printed to stdout.

Note

The samples in this article use useInstrumenter() for ESM compatibility. If your app is CommonJS, you can use the standard createAzureSdkInstrumentation() from @azure/opentelemetry-instrumentation-azure-sdk instead.

Reference: OpenTelemetry JavaScript SDK | VoiceLiveClient

Export traces to Azure Monitor

To send traces to Application Insights instead of the console, replace the console exporter with AzureMonitorTraceExporter. Set the APPLICATIONINSIGHTS_CONNECTION_STRING environment variable, then add the following code. The useInstrumenter() bridge from the previous section is still required.

import { NodeTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-node";
import { AzureMonitorTraceExporter } from "@azure/monitor-opentelemetry-exporter";

const exporter = new AzureMonitorTraceExporter({
  connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING,
});
const provider = new NodeTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(exporter)],
});
provider.register();

// Register the same useInstrumenter() bridge shown in the console tracing section.

View the results in the Tracing tab in your Foundry project page or in Application Insights.

Reference: Azure Monitor OpenTelemetry exporter for JavaScript

Add custom span attributes

To correlate Voice Live traces with your application context (session IDs, user IDs, or request identifiers), implement a custom SpanProcessor that adds attributes when each span starts.

class CustomAttributesProcessor {
  constructor(sessionId, userId) {
    this._sessionId = sessionId;
    this._userId = userId;
  }

  onStart(span) {
    span.setAttribute("app.session_id", this._sessionId);
    span.setAttribute("app.user_id", this._userId);
  }

  onEnd() { }
  async shutdown() { }
  async forceFlush() { }
}

Register the custom processor on the tracer provider before calling provider.register():

const provider = new NodeTracerProvider({
  spanProcessors: [
    new CustomAttributesProcessor("sess-123", "user-abc"),
    new SimpleSpanProcessor(new ConsoleSpanExporter()),
  ],
});
provider.register();

Reference: OpenTelemetry JavaScript SDK

Enable browser tracing

For browser-based applications, use WebTracerProvider instead of NodeTracerProvider. The same useInstrumenter() bridge applies. Spans can be exported to an in-page element, the browser console, or any OpenTelemetry-compatible backend.

import { WebTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-web";
import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
import { useInstrumenter } from "@azure/core-tracing";
import { trace, context } from "@opentelemetry/api";
import { VoiceLiveClient } from "@azure/ai-voicelive";

const provider = new WebTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
});
provider.register();

// Register the same useInstrumenter() bridge shown in the console tracing section.

// Browsers don't support DefaultAzureCredential. Use AzureKeyCredential instead.
const credential = { key: import.meta.env.VITE_VOICELIVE_API_KEY };
const client = new VoiceLiveClient(import.meta.env.VITE_VOICELIVE_ENDPOINT, credential);
const session = client.createSession("gpt-realtime");
await session.connect();

Note

Browsers don't support DefaultAzureCredential. Use AzureKeyCredential or a bearer-token flow instead.

Enable content recording

Content recording captures full message payloads (send and receive) on span events. This is useful for debugging but can capture personal data.

Caution

Content recording may capture personal data. Only enable in development or controlled environments.

Set the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable to true before starting your application. No code changes are required.

export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true

When enabled, the SDK attaches event payloads as gen_ai.event.content attributes on the corresponding spans.

Production best practices

  • Batch export: Use BatchSpanProcessor instead of SimpleSpanProcessor in production to reduce overhead.
  • Sampling: Configure a sampling strategy to control trace volume at scale.
  • Sensitive data: Don't enable content recording in production. Message payloads can contain personal data.
  • Correlation: Use custom span attributes to add session or user identifiers so you can filter traces in your observability backend.

Troubleshooting

Symptom Cause Fix
No spans appear Missing AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING env var Set AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true
No spans appear VoiceLiveInstrumentor().instrument() not called Call instrument() before connect()
Spans missing in Azure Monitor Missing or invalid connection string Verify APPLICATIONINSIGHTS_CONNECTION_STRING is set correctly
Spans appear in console but not in Azure Monitor Using ConsoleSpanExporter instead of Azure Monitor Switch to configure_azure_monitor()
Custom attributes missing Processor registered after spans are created Register the custom processor before calling connect()