Edit

Share via


Use OpenTelemetry with Azure Functions

This article shows you how to configure your function app to export log and trace data in an OpenTelemetry format. Azure Functions generates telemetry data on your function executions from both the Functions host process and the language-specific worker process in which your function code runs. By default, this telemetry data is sent to Application Insights by using the Application Insights SDK. However, you can choose to export this data by using OpenTelemetry semantics. While you can still use an OpenTelemetry format to send your data to Application Insights, you can now also export the same data to any other OpenTelemetry-compliant endpoint.

You can obtain these benefits by enabling OpenTelemetry in your function app:

  • Correlates data across traces and logs being generated both at the host and in your application code.
  • Enables consistent, standards-based generation of exportable telemetry data.
  • Integrates with other providers that can consume OpenTelemetry-compliant data.

Keep these considerations in mind when using this article:

  • Try the OpenTelemetry tutorial, which is designed to help you get started quickly with OpenTelemetry and Azure Functions. This article uses the Azure Developer CLI (azd) to create and deploy a function app that uses OpenTelemetry integration for distributed tracing.

  • Because this article is targeted at your development language of choice, remember to choose the correct language at the top of the article.

  • OpenTelemetry is enabled at the function app level, both in host configuration (host.json) and in your code project. Functions also provides a client optimized experience for exporting OpenTelemetry data from your function code that's running in a language-specific worker process.

Enable OpenTelemetry in the Functions host

When you enable OpenTelemetry output in the function app's host.json file, your host exports OpenTelemetry output regardless of the language stack used by your app.

To enable OpenTelemetry output from the Functions host, update the host.json file in your code project to add a "telemetryMode": "OpenTelemetry" element to the root collection. With OpenTelemetry enabled, your host.json file might look like this:

{
    "version": "2.0",
    "telemetryMode": "OpenTelemetry",
    ...
}

Configure application settings

When you enable OpenTelemetry in the host.json file, the app's environment variables determine the endpoints for sending data based on which OpenTelemetry-supported application settings are available.

Create specific application settings in your function app based on the OpenTelemetry output destination. When you provide connection settings for both Application Insights and an OpenTelemetry protocol (OTLP) exporter, OpenTelemetry data is sent to both endpoints.

APPLICATIONINSIGHTS_CONNECTION_STRING: the connection string for an Application Insights workspace. When this setting exists, OpenTelemetry data is sent to that workspace. Use the same setting to connect to Application Insights without OpenTelemetry enabled. If your app doesn't already have this setting, you might need to Enable Application Insights integration.

JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY: set to true so that the Functions host allows the Java worker process to stream OpenTelemetry logs directly, which prevents duplicate host-level entries.

PYTHON_APPLICATIONINSIGHTS_ENABLE_TELEMETRY: set to true so that the Functions host allows the Python worker process to stream OpenTelemetry logs directly, which prevents duplicate host-level entries.

Enable OpenTelemetry in your app

After you configure the Functions host to use OpenTelemetry, update your application code to output OpenTelemetry data. When you enable OpenTelemetry in both the host and your application code, you get better correlation between traces and logs that the Functions host process and your language worker process emit.

How you instrument your application to use OpenTelemetry depends on your target OpenTelemetry endpoint:

Examples in this article assume your app uses IHostApplicationBuilder, which is available in version 2.x and later version of Microsoft.Azure.Functions.Worker. For more information, see Version 2.x in the C# isolated worker model guide.

  1. Run these commands to install the required assemblies in your app:

    dotnet add package Microsoft.Azure.Functions.Worker.OpenTelemetry
    dotnet add package OpenTelemetry.Extensions.Hosting 
    dotnet add package Azure.Monitor.OpenTelemetry.Exporter  
    
  2. In your Program.cs project file, add this using statement:

    using Azure.Monitor.OpenTelemetry.Exporter; 
    
  3. Configure OpenTelemetry based on whether your project startup uses IHostBuilder or IHostApplicationBuilder. The latter was introduced in v2.x of the .NET isolated worker model extension.

    In program.cs, add this line of code after ConfigureFunctionsWebApplication:

    builder.Services.AddOpenTelemetry()
        .UseFunctionsWorkerDefaults()
        .UseAzureMonitorExporter();
    

    You can export to both OpenTelemetry endpoints from the same app.

  1. Add the required libraries to your app. The way you add libraries depends on whether you deploy using Maven or Kotlin and if you want to also send data to Application Insights.

    <dependency>
      <groupId>com.microsoft.azure.functions</groupId>
      <artifactId>azure-functions-java-opentelemetry</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-monitor-opentelemetry-autoconfigure</artifactId>
      <version>1.2.0</version>
    </dependency>
    
  2. (Optional) Add this code to create custom spans:

    import com.microsoft.azure.functions.opentelemetry.FunctionsOpenTelemetry;
    import io.opentelemetry.api.trace.Span;
    import io.opentelemetry.api.trace.SpanKind;
    import io.opentelemetry.context.Scope;
    
    Span span = FunctionsOpenTelemetry.startSpan(
            "com.contoso.PaymentFunction",  // tracer name
            "validateCharge",               // span name
            null,                           // parent = current context
            SpanKind.INTERNAL);
    
    try (Scope ignored = span.makeCurrent()) {
        // business logic here
    } finally {
        span.end();
    }
    
  1. Install these npm packages in your project:

    npm install @opentelemetry/api 
    npm install @opentelemetry/auto-instrumentations-node 
    npm install @azure/monitor-opentelemetry-exporter 
    npm install @azure/functions-opentelemetry-instrumentation
    
  1. Create a code file in your project, copy and paste the following code in this new file, and save the file as src/index.js:

    const { AzureFunctionsInstrumentation } = require('@azure/functions-opentelemetry-instrumentation');
    const { AzureMonitorLogExporter, AzureMonitorTraceExporter } = require('@azure/monitor-opentelemetry-exporter');
    const { getNodeAutoInstrumentations, getResourceDetectors } = require('@opentelemetry/auto-instrumentations-node');
    const { registerInstrumentations } = require('@opentelemetry/instrumentation');
    const { detectResourcesSync } = require('@opentelemetry/resources');
    const { LoggerProvider, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs');
    const { NodeTracerProvider, SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-node');
    
    const resource = detectResourcesSync({ detectors: getResourceDetectors() });
    
    const tracerProvider = new NodeTracerProvider({ resource });
    tracerProvider.addSpanProcessor(new SimpleSpanProcessor(new AzureMonitorTraceExporter()));
    tracerProvider.register();
    
    const loggerProvider = new LoggerProvider({ resource });
    loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(new AzureMonitorLogExporter()));
    
    registerInstrumentations({
        tracerProvider,
        loggerProvider,
        instrumentations: [getNodeAutoInstrumentations(), new AzureFunctionsInstrumentation()],
    });
    
  2. Update the main field in your package.json file to include the new src/index.js file. For example:

    "main": "src/{index.js,functions/*.js}"
    
  1. Create a code file in your project, copy and paste the following code in this new file, and save the file as src/index.ts:

    import { AzureFunctionsInstrumentation } from '@azure/functions-opentelemetry-instrumentation';
    import { AzureMonitorLogExporter, AzureMonitorTraceExporter } from '@azure/monitor-opentelemetry-exporter';
    import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node';
    import { registerInstrumentations } from '@opentelemetry/instrumentation';
    import { detectResourcesSync } from '@opentelemetry/resources';
    import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
    import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
    
    const resource = detectResourcesSync({ detectors: getResourceDetectors() });
    
    const tracerProvider = new NodeTracerProvider({ resource });
    tracerProvider.addSpanProcessor(new SimpleSpanProcessor(new AzureMonitorTraceExporter()));
    tracerProvider.register();
    
    const loggerProvider = new LoggerProvider({ resource });
    loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(new AzureMonitorLogExporter()));
    
    registerInstrumentations({
        tracerProvider,
        loggerProvider,
        instrumentations: [getNodeAutoInstrumentations(), new AzureFunctionsInstrumentation()],
    });
    
  2. Update the main field in your package.json file to include the output of this new src/index.ts file, which might look like this:

    "main": "dist/src/{index.js,functions/*.js}"
    

Important

OpenTelemetry output to Application Insights from the language worker isn't currently supported for PowerShell apps. You might instead want to use an OTLP exporter endpoint. When you configure your host for OpenTelemetry output to Application Insights, the logs generated by the PowerShell worker process are still forwarded, but distributed tracing isn't supported at this time.

These instructions only apply for an OTLP exporter:

  1. Add an application setting named OTEL_FUNCTIONS_WORKER_ENABLED with value of True.

  2. Create an app-level Modules folder in the root of your app and run the following command:

    Save-Module -Name AzureFunctions.PowerShell.OpenTelemetry.SDK
    

    This command installs the required AzureFunctions.PowerShell.OpenTelemetry.SDK module directly in your app. You can't use the requirements.psd1 file to automatically install this dependency because managed dependencies isn't currently supported in the Flex Consumption plan preview.

  3. Add this code to your profile.ps1 file:

    Import-Module AzureFunctions.PowerShell.OpenTelemetry.SDK -Force -ErrorAction Stop 
    Initialize-FunctionsOpenTelemetry 
    
  1. Make sure these libraries are in your requirements.txt file, whether from uncommenting or adding yourself:

    azure-monitor-opentelemetry
    
  2. Add this code to your function_app.py main entry point file:

    If you already added PYTHON_APPLICATIONINSIGHTS_ENABLE_TELEMETRY=true in your application settings, you can skip this step. To manually enable Application Insights collection without automatic instrumentation, add this code to your app:

    from azure.monitor.opentelemetry import configure_azure_monitor 
    configure_azure_monitor() 
    
  3. Review Azure monitor Distro usage documentation for options on how to further configure the SDK.

Considerations for OpenTelemetry

When you export your data by using OpenTelemetry, keep these considerations in mind.

  • The Azure portal supports Recent function invocation traces only if the telemetry is sent to Azure Monitor.

  • When you configure the host to use OpenTelemetry, the Azure portal doesn't support log streaming.

  • If you set telemetryMode to OpenTelemetry, the configuration in the logging.applicationInsights section of host.json doesn't apply.

  • Custom spans automatically include all resource attributes and use the exporters configured in your app.

  • When your app runs outside Azure, including during local development, the resource detector sets the service.name attribute to java-function-app by default.

  • Use these Java Virtual Machine (JVM) flags to silence telemetry when running locally during unit tests:

    • -Dotel.traces.exporter=none
    • -Dotel.metrics.exporter=none
    • -Dotel.logs.exporter=none
  • You don't need to manually register middleware; the Java worker autodiscovers OpenTelemetryInvocationMiddleware.

Troubleshooting

When you export your data by using OpenTelemetry, keep these common issues and solutions in mind.

Log filtering

To correctly configure log filtering in your function app, you need to understand the difference between the host process and the worker process.

The host process is the Azure Functions runtime that manages triggers, scaling, and emits system-level telemetry such as initialization logs, request traces, and runtime health information.

The worker process is language specific, executes your function code, and produces application logs and telemetry independently.

Important

Filters defined in host.json apply only to logs generated by the host process. You must use language-specific OpenTelemetry settings to filter logs from the worker process.

Example: Filter host logs for all providers in host.json

Use this approach to set a global log level across all providers managed by the host:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "logging": {
    "logLevel": {
      "default": "Warning"
    }
  }
}

Example: Filter logs only for the OpenTelemetry logger provider

Use this approach to target only the OpenTelemetry logger provider while leaving other providers (such as console or file logging) unaffected:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "logging": {
    "OpenTelemetry": {
      "logLevel": {
        "default": "Warning"
      }
    }
  }
}

Console logging

The Functions host automatically captures anything written to stdout or stderr and forwards it to the telemetry pipeline. If you also use a ConsoleExporter or write directly to console in your code, duplicate logs can occur in your telemetry data.

Note

To avoid duplicate telemetry entries, don't add ConsoleExporter or write to console in production code.

Microsoft Entra authentication

When you use Microsoft Entra authentication with OpenTelemetry, you must configure authentication separately for both the host process and the worker process.

To configure authentication for the host process, see Require Microsoft Entra authentication.

To configure authentication for the worker process, see Enable Microsoft Entra authentication.

Resource attributes support

Resource attributes support in Azure Monitor is currently in preview. To enable this feature, set the OTEL_DOTNET_AZURE_MONITOR_ENABLE_RESOURCE_METRICS environment variable to true. This setting ingests resource attributes into the custom metrics table.

Duplicate request telemetry

The host process automatically emits request telemetry. If the worker process is also instrumented with request tracking libraries (for example, AspNetCoreInstrumentation in .NET), the same request is reported twice.

Note

Since the Azure Monitor Distro typically includes AspNetCoreInstrumentation in .NET and similar instrumentation in other languages, avoid using the Azure Monitor distro in the worker process to prevent duplicate telemetry.

Logging scopes not included

By default, the worker process doesn't include scopes in its logs. To enable scopes, you must configure this setting explicitly in the worker. The following example shows how to enable scopes in .NET Isolated:

builder.Logging.AddOpenTelemetry(b => b.IncludeScopes = true);

Missing request telemetry

Triggers such as HTTP, Service Bus, and Event Hubs depend on context propagation for distributed tracing. With parent-based sampling as the default behavior, request telemetry isn't generated when the incoming request or message isn't sampled.

Duplicate OperationId

In Azure Functions, the OperationId used for correlating telemetry comes directly from the traceparent value in the incoming request or message. If multiple calls reuse the same traceparent value, they all get the same OperationId.

Configure OpenTelemetry with environment variables

You can configure OpenTelemetry behavior by using its standard environment variables. These variables provide a consistent way to control behavior across different languages and runtimes. You can adjust sampling strategies, exporter settings, and resource attributes. For more information about supported environment variables, see the OpenTelemetry documentation.

Use diagnostics to troubleshoot monitoring issues

Azure Functions diagnostics in the Azure portal is a useful resource for detecting and diagnosing potential monitoring-related issues.

To access diagnostics in your app:

  1. In the Azure portal, go to your function app resource.

  2. In the left pane, select Diagnose and solve problems and search for the Function App missing telemetry Application Insights or OpenTelemetry workflow.

  3. Select this workflow, choose your ingestion method, and select Next.

  4. Review the guidelines and any recommendations provided by the troubleshooter.

Next steps

Learn more about OpenTelemetry and monitoring Azure Functions: