The @dynatrace/opentelemetry-azure-functions module provides APIs for tracing Node.js on Azure Functions.
Ensure that you have followed the initial configuration steps described in Set up OpenTelemetry monitoring for Azure Functions on Consumption Plan before using the packages below.
To set up OpenTelemetry Node.js integration on Azure Functions, run the following command.
npm install --save @dynatrace/opentelemetry-azure-functions
Azure Functions can be developed by using either of two different programming models, v3 and v4. To accommodate differences between the two models, Dynatrace provides two different ways of trace export:
wrapHandler API) to generate and export traces.For details, see below.
To export traces to Dynatrace from Azure Functions developed with programming model v3
Select one of the two ways below to initialize tracing.
NodeTracerProvider—more lightweight than NodeSDKNodeSDK—typically used if you're interested in additional OpenTelemetry signals such as metricsIt is possible to bundle several Azure Functions into a single Azure Function app. It's therefore important to initialize tracing only once per Azure Function app instead of once per function. The simplest way to do this is to put a tracing setup code into a shared file as described in the Azure Functions JavaScript developer guide and require it at the top of all functions.
The tracing setup code should be implemented to set up tracing only once before any other third-party module is required.
import { Resource } from "@opentelemetry/resources";import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";import { DtSpanExporter, DtSpanProcessor, DtTextMapPropagator, DtSampler } from "@dynatrace/opentelemetry-azure-functions";// tracing setupconst exporter = new DtSpanExporter();const processor = new DtSpanProcessor(exporter);const provider = new NodeTracerProvider({resource: new Resource({"my.resource.attribute": "My Resource"}),sampler: new DtSampler(),// for @opentelemetry/sdk-trace-node versions lower than 1.29.0 use `provider.addSpanProcessor(processor)` insteadspanProcessors: [processor]// ...other configurations});provider.register({propagator: new DtTextMapPropagator(),// ...other configurations});
import { Resource } from "@opentelemetry/resources";import { NodeSDK } from "@opentelemetry/sdk-node";import { DtSpanExporter, DtSpanProcessor, DtTextMapPropagator, DtSampler } from "@dynatrace/opentelemetry-azure-functions";const sdk = new NodeSDK({resource: new Resource({"my.resource.attribute": "My Resource"}),sampler: new DtSampler(),spanProcessor: new DtSpanProcessor(new DtSpanExporter()),textMapPropagator: new DtTextMapPropagator(),// ...other configurations});sdk.start().then(() => {// Resources have been detected and SDK is started});
Wrap your function handler as below and export the wrapped handler.
import type { AzureFunction, Context, HttpRequest } from "@azure/functions"// Import the wrapHandler function.import { wrapHandler } from "@dynatrace/opentelemetry-azure-functions";const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {// The created span is set as active by the OpenTelemetry ContextManager herecontext.log("HTTP trigger function processed a request.");const name = (req.query.name || (req.body && req.body.name));const responseMessage = name? "Hello, " + name + ". This HTTP triggered function executed successfully.": "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";context.res = {status: 200,body: responseMessage};};// Export the wrapped handler function.export default wrapHandler(httpTrigger);
There are two ways to export traces to Dynatrace from Azure Functions developed with programming model v4.
initDynatrace API.Regardless of the instrumentation approach you choose, always implement the tracing setup code to set up tracing only once before any other third-party module is required.
initDynatrace APIThe initDynatrace API registers Azure Function hooks required for tracing and optionally registers required tracing components.
You can do this either with or without the OpenTelemetry setup:
Pass true as the first argument to the initDynatrace to set up tracing and return the registered NodeTracerProvider. Resource attributes for the provider can be passed as the second optional argument.
import { initDynatrace } from "@dynatrace/opentelemetry-azure-functions";// initialize instrumentation with tracing setupconst provider = initDynatrace(true, {"my.resource.attribute": "My Resource"});// azure functions registration goes here
Call initDynatrace without parameters to register required Azure Function hooks only and set up tracing manually. This is convenient if customizations are required in the tracing setup.
import { initDynatrace } from "@dynatrace/opentelemetry-azure-functions";import { Resource } from "@opentelemetry/resources";import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";import { DtSpanExporter, DtSpanProcessor, DtTextMapPropagator, DtSampler } from "@dynatrace/opentelemetry-azure-functions";// tracing setupconst exporter = new DtSpanExporter();const processor = new DtSpanProcessor(exporter);const provider = new NodeTracerProvider({resource: new Resource({"my.resource.attribute": "My Resource"}),sampler: new DtSampler(),// for @opentelemetry/sdk-trace-node versions lower than 1.29.0 use `provider.addSpanProcessor(processor)` insteadspanProcessors: [processor]// ...other configurations});provider.register({propagator: new DtTextMapPropagator(),// ...other configurations});// initialize instrumentationinitDynatrace();// azure functions registration goes here
Note that the tracing setup code is the same as for programming model v3 and the example with NodeSDK (from the model v3 above) would work here as well. To make it more convenient, there is the configureDynatrace API, which does the same as the above.
import { configureDynatrace, initDynatrace } from "@dynatrace/opentelemetry-azure-functions";// tracing setupconst provider = configureDynatrace({"my.resource.attribute": "My Resource"});// initialize instrumentationinitDynatrace();// azure functions registration goes here
In cases where you need to register additional Azure Functions hooks, the initDynatrace API might not be suitable.
Because Azure Function hooks are executed in the same order they are registered, it's important to:
Hook execution times are included in the total function execution time. If the order of the registered hooks is incorrect, the function execution time reported by our instrumentation won't be accurate either.
To find out more about Azure Function hooks, see the Azure Functions Node.js developer guide.
To order hooks as needed, you can use the registerTraceStartHook and registerTraceEndHook APIs as shown below.
import { app, PreInvocationContext, PostInvocationContext } from "@azure/functions";import { configureDynatrace, registerTraceStartHook, registerTraceEndHook } from "@dynatrace/opentelemetry-azure-functions";// setup tracing with configureDynatrace or manuallyconst provider = configureDynatrace();// register Dynatrace Trace Start hookregisterTraceStartHook();// register other user's pre-invocation hooksapp.hook.preInvocation(async (context: PreInvocationContext) => {// hook code});// register other user's post-invocation hooksapp.hook.postInvocation(async (context: PostInvocationContext) => {// hook code});// register Dynatrace Trace End hookregisterTraceEndHook();// azure functions registration goes here
| OneAgent version | OpenTelemetry API | OpenTelemetry SDK |
|---|---|---|
| 1.243 - 1.255 | 1.x.y | 1.0.x |
| 1.257 | 1.x.y | 1.0.x - 1.7.x |
| 1.259+ | 1.x.y | 1.0.x - 1.8.x |
| 1.261+ | 1.x.y | 1.0.x - 1.9.x |
| 1.265+ | 1.x.y | 1.0.x - 1.10.x |
| 1.273+ | 1.x.y | 1.0.x - 1.15.x |
| 1.279+ | 1.x.y | 1.0.x - 1.17.x |
| 1.283+ | 1.x.y | 1.0.x - 1.18.x |
| 1.285+ | 1.x.y | 1.0.x - 1.20.x |
| 1.289+ | 1.x.y | 1.0.x - 1.22.x |
| 1.293+ | 1.x.y | 1.0.x - 1.24.x |
| 1.297+ | 1.x.y | 1.0.x - 1.25.x |
| 1.303+ | 1.x.y | 1.0.x - 1.26.x |
| 1.307+ | 1.x.y | 1.0.x - 1.29.x |
| 1.313+ | 1.x.y | 1.0.x - 1.30.x |
Supported Azure Functions runtime:
Supported Azure Functions programming model:
Only async function handlers are supported.
async and await.wrapHandler returns any non-async function unwrapped, so the function itself will work but no span will be created.Signaling function completion using the deprecated context.done() or context.res.send() calls is not supported. Either use a $return binding and return the result from the function handler, or use a named out binding and set context.binding.<name>. For HTTP triggers, setting context.res is also supported.