Skip to content

Azure-Samples/functions-quickstart-typescript-azd-otel

description page_type products urlFragment languages
This end-to-end TypeScript sample demonstrates distributed tracing with OpenTelemetry across multiple Azure Functions in a Flex Consumption plan app with Service Bus integration and virtual network security.
sample
azure-functions
azure
functions-quickstart-typescript-azd-otel
typescript
bicep
azdeveloper

Azure Functions TypeScript Service Bus Trigger with OpenTelemetry Distributed Tracing using Azure Developer CLI

This template repository contains a Service Bus trigger reference sample for functions written in TypeScript and deployed to Azure using the Azure Developer CLI (azd). The sample demonstrates distributed tracing using OpenTelemetry across multiple Azure Functions and includes managed identity and virtual network integration for secure deployment by default. This sample demonstrates these key features:

  • Distributed tracing with OpenTelemetry. The sample shows how to trace requests across multiple Azure Functions using OpenTelemetry integration, providing end-to-end visibility into function execution flows.
  • Virtual network integration. The Service Bus that this Flex Consumption app reads events from is secured behind a private endpoint. The function app can read events from it because it is configured with VNet integration. All connections to Service Bus and to the storage account associated with the Flex Consumption app also use managed identity connections instead of connection strings.

This project is designed to run on your local computer. You can also use GitHub Codespaces if available.

This sample demonstrates distributed tracing across multiple Azure Functions with OpenTelemetry integration. The app includes three functions that work together: an HTTP-triggered function that calls a second HTTP function, which then sends a message to Service Bus that triggers a third function. This creates a complete end-to-end tracing scenario that you can observe in Application Insights.

Important

This sample creates several resources. Make sure to delete the resource group after testing to minimize charges!

Prerequisites

Initialize the local project

You can initialize a project from this azd template in one of these ways:

  • Use this azd init command from an empty local (root) folder:

    azd init --template functions-quickstart-typescript-azd-otel

    Supply an environment name, such as flexquickstart when prompted. In azd, the environment is used to maintain a unique deployment context for your app.

  • Clone the GitHub template repository locally using the git clone command:

    git clone https://github.com/Azure-Samples/functions-quickstart-typescript-azd-otel.git
    cd functions-quickstart-typescript-azd-otel

    You can also clone the repository from your own fork in GitHub.

Prepare your local environment

  1. Navigate to the src app folder and create a file in that folder named local.settings.json that contains this JSON data:

    {
        "IsEncrypted": false,
        "Values": {
            "AzureWebJobsStorage": "UseDevelopmentStorage=true",
            "FUNCTIONS_WORKER_RUNTIME": "node",
            "APPLICATIONINSIGHTS_CONNECTION_STRING": "",
            "ServiceBusConnection": "",
            "ServiceBusQueueName": "testqueue"
        }
    }

    [!NOTE] The ServiceBusConnection will be empty for local development. You'll need an actual Service Bus connection for full testing, which will be provided after deployment to Azure.

  2. Install the required npm packages:

    cd src
    npm install
  3. Build the TypeScript code:

    npm run build

Run your app from the terminal

  1. From the src folder, run this command to start the Functions host locally:

    npm start

    [!NOTE] The Service Bus trigger function will start but won't process messages until connected to an actual Service Bus queue. However, you can test the HTTP functions locally.

  2. The function will start and display the available functions. You should see output similar to:

    Functions:
        first_http_function: [GET,POST] http://localhost:7071/api/first_http_function
        second_http_function: [GET,POST] http://localhost:7071/api/second_http_function
        servicebus_queue_trigger: serviceBusQueueTrigger
    
  3. You can test the HTTP functions locally by calling the endpoint, though the Service Bus functionality requires deployment to Azure for full testing.

  4. When you're done, press Ctrl+C in the terminal window to stop the func host process.

Run your app using Visual Studio Code

  1. Open the project root folder in Visual Studio Code.
  2. Open the src folder in the terminal within VS Code.
  3. Press Run/Debug (F5) to run in the debugger.
  4. The Azure Functions extension will automatically detect your function and start the local runtime.
  5. The function will start and be ready to receive Service Bus messages (though local testing requires an actual Service Bus connection).

Source Code

The function app is defined in TypeScript with the main entry point at src/index.ts which configures OpenTelemetry instrumentation, and contains three functions that demonstrate distributed tracing across a complete request flow:

1. First HTTP Function

Located in src/functions/first_http_function.ts:

export async function firstHttpFunction(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log("TypeScript HTTP trigger function (first) processed a request.");

  // Log OpenTelemetry tracing information
  context.log(`Header traceparent- "${request.headers.get("traceparent")}"`);
  context.log(`Context traceparent- "${context.traceContext.traceParent}"`);
  context.log(`ActiveSpan traceId- "${otelAPI.trace.getActiveSpan()}"`);
  context.log(`ActiveSpan spanId- "${otelAPI.trace.getActiveSpan()}"`);

  try {
    // Call the second function
    const baseUrl = request.url.split("/api/")[0];
    const secondFunctionUrl = `${baseUrl}/api/second_http_function`;

    const response = await axios.get(secondFunctionUrl);
    const secondFunctionResult = response.data;

    const result = {
      message: "Hello from the first function!",
      second_function_response: secondFunctionResult,
    };

    context.log("Successfully called second function");

    return {
      status: 200,
      body: JSON.stringify(result),
      headers: {
        "Content-Type": "application/json",
      },
    };
  } catch (error) {
    context.log("Error occurred:", error);
    return {
      status: 500,
      body: JSON.stringify({
        error: "Failed to process request",
        message: error.message,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    };
  }
}

2. Second HTTP Function

Located in src/functions/second_http_function.ts:

export async function secondHttpFunction(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log("TypeScript HTTP trigger function (second) processed a request.");

  // Log OpenTelemetry tracing information
  context.log(`Header traceparent- "${request.headers.get("traceparent")}"`);
  context.log(`Context traceparent- "${context.traceContext.traceParent}"`);
  context.log(`ActiveSpan traceId- "${otelAPI.trace.getActiveSpan()}"`);
  context.log(`ActiveSpan spanId- "${otelAPI.trace.getActiveSpan()}"`);

  const message = "This is the second function responding.";

  // Send a message to the Service Bus queue
  const queueMessage =
    "Message from second HTTP function to trigger ServiceBus queue processing";
  
  // Set the Service Bus output binding
  context.extraOutputs.set(serviceBusOutput, queueMessage);
  
  context.log("Sent message to ServiceBus queue:", queueMessage);

  return {
    status: 200,
    body: message,
  };
}

3. Service Bus Queue Trigger

Located in src/functions/servicebus_queue_trigger.ts:

export async function serviceBusQueueTrigger(
  message: unknown,
  context: InvocationContext
): Promise<void> {
  context.log(
    "TypeScript ServiceBus Queue trigger start processing a message:",
    message
  );

  // Log OpenTelemetry tracing information
  context.log(`Context traceparent- "${context.traceContext.traceParent}"`);
  context.log(`ActiveSpan traceId- "${otelAPI.trace.getActiveSpan()}"`);
  context.log(`ActiveSpan spanId- "${otelAPI.trace.getActiveSpan()}"`);

  // Simulate processing time
  await new Promise((resolve) => setTimeout(resolve, 5000));

  context.log("TypeScript ServiceBus Queue trigger end processing a message");
}

Distributed Tracing Flow

This architecture creates a complete distributed tracing scenario:

  1. First HTTP function receives an HTTP request and calls the second HTTP function
  2. Second HTTP function responds and sends a message to Service Bus
  3. Service Bus trigger processes the message with a 5-second delay to simulate processing work

Key aspects of the implementation:

  • OpenTelemetry integration: The src/index.ts file configures OpenTelemetry with Azure Monitor exporters for traces and logs
  • Function chaining: The first function calls the second using HTTP requests with axios
  • Service Bus integration: The second function outputs to Service Bus using output bindings, which triggers the third function
  • Managed identity: All Service Bus connections use managed identity instead of connection strings
  • Processing simulation: The 5-second delay in the Service Bus trigger simulates message processing work

The OpenTelemetry configuration in src/index.ts sets up tracing and logging:

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 { detectResources } from '@opentelemetry/resources';
import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';

// Detect resources automatically (service name, version, etc.)
const resource = detectResources({ detectors: getResourceDetectors() });

// Create and configure NodeTracerProvider with Azure Monitor exporter
const tracerProvider = new NodeTracerProvider({ 
  resource, 
  spanProcessors: [new SimpleSpanProcessor(new AzureMonitorTraceExporter())] 
});
tracerProvider.register();

// Create and configure LoggerProvider with Azure Monitor exporter
const loggerProvider = new LoggerProvider({
  resource,
  processors: [new SimpleLogRecordProcessor(new AzureMonitorLogExporter())],
});

// Register instrumentations including auto-instrumentations for Node.js
registerInstrumentations({
    tracerProvider,
    loggerProvider,
    instrumentations: [
      getNodeAutoInstrumentations(), 
      new AzureFunctionsInstrumentation()
    ],
});

The function configuration in src/host.json enables OpenTelemetry and configures Service Bus settings:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "extensions": {
    "serviceBus": {
      "maxConcurrentCalls": 10
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}

Key configuration aspects:

  • OpenTelemetry: "telemetryMode": "OpenTelemetry" enables distributed tracing across function calls
  • Service Bus concurrency: maxConcurrentCalls: 10 allows multiple messages to be processed concurrently
  • Dependencies: The src/package.json includes @azure/functions-opentelemetry-instrumentation, @azure/monitor-opentelemetry-exporter, and axios packages for OpenTelemetry tracing and HTTP calls

Deploy to Azure

Run this command to provision the function app, with any required Azure resources, and deploy your code:

azd up

You're prompted to supply these required deployment parameters:

Parameter Description
Environment name An environment that's used to maintain a unique deployment context for your app. You won't be prompted if you created the local project using azd init.
Azure subscription Subscription in which your resources are created.
Azure location Azure region in which to create the resource group that contains the new Azure resources. Only regions that currently support the Flex Consumption plan are shown.

After deployment completes successfully, azd provides you with the URL endpoints and resource information for your new function app.

Test the solution

  1. Once deployment is complete, you can test the distributed tracing functionality by calling the first_http_function:

  2. Call the first HTTP function: Use the function URL provided after deployment to trigger the complete distributed tracing flow:

    https://your-function-app.azurewebsites.net/api/first_http_function
    
  3. View distributed tracing in Application Insights:

    • Navigate to your Application Insights resource in the Azure Portal
    • Open the "Application map" to see the distributed trace across all three functions
    • Check the "Transaction search" to find your request and see the complete trace timeline
    • The trace will show: HTTP request → first_http_function → second_http_function → Service Bus message → servicebus_queue_trigger

The Application Insights telemetry will show the complete distributed trace:

  • The HTTP request to first_http_function
  • The internal HTTP call to second_http_function
  • The Service Bus message being sent
  • The servicebus_queue_trigger processing the message through the VNet-secured Service Bus

This demonstrates end-to-end distributed tracing across multiple Azure Functions with OpenTelemetry integration.

Redeploy your code

You can run the azd up command as many times as you need to both provision your Azure resources and deploy code updates to your function app.

Note

Deployed code files are always overwritten by the latest deployment package.

Clean up resources

When you're done working with your function app and related resources, you can use this command to delete the function app and its related resources from Azure and avoid incurring any further costs:

azd down

Resources

For more information on Azure Functions, Service Bus, OpenTelemetry, and VNet integration, see the following resources:

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •