Självstudie: Övervaka Azure Functions med OpenTelemetry-distribuerad spårning

Den här artikeln visar OpenTelemetry-stöd i Azure Function, som möjliggör distribuerad spårning över flera funktionsanrop med hjälp av integrerat Application Insights- och OpenTelemetry-stöd. För att hjälpa dig att komma igång används en AZURE Developer CLI-mall (azd) för att skapa ditt kodprojekt samt Azure-distributionen där du kan köra din app.

I den här handledningen kommer du att använda azd-verktyget för att:

  • Initiera ett OpenTelemetry-aktiverat projekt från en mall.
  • Granska koden som aktiverar OpenTelemetry-integrering.
  • Kör och verifiera din OpenTelemetry-aktiverade app lokalt.
  • Skapa en funktionsapp och relaterade resurser i Azure.
  • Distribuera kodprojektet till funktionsappen i Azure.
  • Verifiera distribuerad spårning i Application Insights.

De nödvändiga Azure-resurser som skapas av den här mallen följer nuvarande bästa praxis för säkra och skalbara distributioner av funktionsappar i Azure. Samma azd kommando distribuerar även kodprojektet till din nya funktionsapp i Azure.

Som standard följer Flex Consumption-planen en faktureringsmodell för att betala för vad du använder , vilket innebär att slutförandet av den här snabbstarten medför en liten kostnad på några USDcent eller mindre på ditt Azure-konto.

Viktigt!

Den här artikeln stöder för närvarande endast C#, Python och TypeScript. Slutför snabbstarten genom att välja något av de språk som stöds överst i artikeln.

Förutsättningar

Initiera projektet

azd init Använd kommandot för att skapa ett lokalt Azure Functions-kodprojekt från en mall som innehåller distribuerad spårning med OpenTelemetry.

  1. Kör det här azd init kommandot i en tom mapp i den lokala terminalen eller kommandotolken:

    azd init --template functions-quickstart-python-azd-otel -e flexquickstart-otel
    

    Det här kommandot hämtar projektfilerna från malllagringsplatsen och initierar projektet i den aktuella mappen. Flaggan -e anger ett namn för den aktuella miljön. I azdbehåller miljön en unik distributionskontext för din app och du kan definiera mer än en. Miljönamnet visas också i namnet på den resursgrupp som du skapar i Azure.

  1. Kör det här azd init kommandot i en tom mapp i den lokala terminalen eller kommandotolken:

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

    Det här kommandot hämtar projektfilerna från malllagringsplatsen och initierar projektet i den aktuella mappen. Flaggan -e anger ett namn för den aktuella miljön. I azdbehåller miljön en unik distributionskontext för din app och du kan definiera mer än en. Miljönamnet visas också i namnet på den resursgrupp som du skapar i Azure.

  1. Kör det här azd init kommandot i en tom mapp i den lokala terminalen eller kommandotolken:

    azd init --template functions-quickstart-dotnet-azd-otel -e flexquickstart-otel
    

    Det här kommandot hämtar projektfilerna från malllagringsplatsen och initierar projektet i den aktuella mappen. Flaggan -e anger ett namn för den aktuella miljön. I azdbehåller miljön en unik distributionskontext för din app och du kan definiera mer än en. Miljönamnet visas också i namnet på den resursgrupp som du skapar i Azure.

Granska koden

Mallen skapar ett fullständigt distribuerat spårningsscenario med tre funktioner som fungerar tillsammans. Nu ska vi gå igenom viktiga OpenTelemetry-relaterade aspekter:

OpenTelemetry-konfiguration

Filen src/otel-sample/host.json aktiverar OpenTelemetry för Functions-värden:

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

Nyckelinställningen "telemetryMode": "OpenTelemetry" möjliggör distribuerad spårning mellan funktionsanrop.

Filen src/OTelSample/host.json aktiverar OpenTelemetry för Functions-värdenoden:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "logging": {
    "OpenTelemetry": {
      "logLevel": {
        "Host.General": "Warning"
      }
    }
  }
}

Nyckelinställningen "telemetryMode": "OpenTelemetry" möjliggör distribuerad spårning mellan funktionsanrop.

Beroenden för OpenTelemetry

Filen src/otel-sample/requirements.txt innehåller nödvändiga paket för OpenTelemetry-integrering:

azure-functions
azure-monitor-opentelemetry
requests

Paketet azure-monitor-opentelemetry tillhandahåller OpenTelemetry-integrering med Application Insights.

Filen src/otel-sample/package.json innehåller nödvändiga paket för OpenTelemetry-integrering:

{
  "dependencies": {
    "@azure/functions": "^4.0.0",
    "@azure/functions-opentelemetry-instrumentation": "^0.1.0",
    "@azure/monitor-opentelemetry-exporter": "^1.0.0",
    "axios": "^1.6.0"
  }
}

Paketen @azure/functions-opentelemetry-instrumentation och @azure/monitor-opentelemetry-exporter tillhandahåller OpenTelemetry-integreringen med Application Insights.

Filen .csproj innehåller nödvändiga paket för OpenTelemetry-integrering:

<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.OpenTelemetry" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.10.0" />

Dessa paket tillhandahåller OpenTelemetry-integrering med Application Insights och HTTP-instrumentation för distribuerad spårning.

Funktionsimplementering

Funktionerna i src/otel-sample/function_app.py demonstrerar ett distribuerat spårningsflöde:

Första HTTP-funktionen

@app.function_name("first_http_function")
@app.route(route="first_http_function", auth_level=func.AuthLevel.ANONYMOUS)
def first_http_function(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function (first) processed a request.')

    # Call the second function
    base_url = f"{req.url.split('/api/')[0]}/api"
    second_function_url = f"{base_url}/second_http_function"

    response = requests.get(second_function_url)
    second_function_result = response.text

    result = {
        "message": "Hello from the first function!",
        "second_function_response": second_function_result
    }

    return func.HttpResponse(
        json.dumps(result),
        status_code=200,
        mimetype="application/json"
    )

Andra HTTP-funktionen

@app.function_name("second_http_function")
@app.route(route="second_http_function", auth_level=func.AuthLevel.ANONYMOUS)
@app.service_bus_queue_output(arg_name="outputsbmsg", queue_name="%ServiceBusQueueName%",
                              connection="ServiceBusConnection")
def second_http_function(req: func.HttpRequest, outputsbmsg: func.Out[str]) -> func.HttpResponse:
    logging.info('Python HTTP trigger function (second) processed a request.')

    message = "This is the second function responding."

    # Send a message to the Service Bus queue
    queue_message = "Message from second HTTP function to trigger ServiceBus queue processing"
    outputsbmsg.set(queue_message)
    logging.info('Sent message to ServiceBus queue: %s', queue_message)

    return func.HttpResponse(
        message,
        status_code=200
    )

Service Bus-köutlösare

@app.service_bus_queue_trigger(arg_name="azservicebus", queue_name="%ServiceBusQueueName%",
                               connection="ServiceBusConnection") 
def servicebus_queue_trigger(azservicebus: func.ServiceBusMessage):
    logging.info('Python ServiceBus Queue trigger start processing a message: %s',
                azservicebus.get_body().decode('utf-8'))
    time.sleep(5)  # Simulate processing work
    logging.info('Python ServiceBus Queue trigger end processing a message')

OpenTelemetry-konfigurationen konfigureras i src/otel-sample/index.ts:

import { AzureFunctionsInstrumentation } from '@azure/functions-opentelemetry-instrumentation';
import { AzureMonitorTraceExporter, AzureMonitorLogExporter } 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';

const resource = detectResources({ detectors: getResourceDetectors() });

const tracerProvider = new NodeTracerProvider({ 
  resource, 
  spanProcessors: [new SimpleSpanProcessor(new AzureMonitorTraceExporter())] 
});
tracerProvider.register();

const loggerProvider = new LoggerProvider({
  resource,
  processors: [new SimpleLogRecordProcessor(new AzureMonitorLogExporter())],
});

registerInstrumentations({
    tracerProvider,
    loggerProvider,
    instrumentations: [getNodeAutoInstrumentations(), new AzureFunctionsInstrumentation()],
});

Funktionerna definieras i src/otel-sample/src/functions mappen:

Första HTTP-funktionen

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

  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,
    };

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

Andra HTTP-funktionen

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

  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";

  context.extraOutputs.set(serviceBusOutput, queueMessage);
  context.log("Sent message to ServiceBus queue:", queueMessage);

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

Service Bus-köutlösare

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

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

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

OpenTelemetry-konfigurationen konfigureras i src/OTelSample/Program.cs:

using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.OpenTelemetry;
using OpenTelemetry.Trace;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()    
    .WithTracing(tracing =>
    {
        tracing.AddHttpClientInstrumentation();
    });

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

builder.Services.AddHttpClient();

builder.Build().Run();

Funktionerna definieras i separata klassfiler:

Första HTTP-funktionen

public class FirstHttpTrigger
{
    private readonly ILogger<FirstHttpTrigger> _logger;
    private readonly IHttpClientFactory _httpClientFactory;

    public FirstHttpTrigger(ILogger<FirstHttpTrigger> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;
    }

    [Function("first_http_function")]
    public async Task<IActionResult> Run(
         [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
    {
        _logger.LogInformation("first_http_function function processed a request.");

        var baseUrl = $"{req.Url.AbsoluteUri.Split("/api/")[0]}/api";
        var targetUri = $"{baseUrl}/second_http_function";

        var client = _httpClientFactory.CreateClient();
        var response = await client.GetAsync(targetUri);
        var content = await response.Content.ReadAsStringAsync();

        return new OkObjectResult($"Called second_http_function, status: {response.StatusCode}, content: {content}");
    }
}

Andra HTTP-funktionen

public class SecondHttpTrigger
{
    private readonly ILogger<SecondHttpTrigger> _logger;

    public SecondHttpTrigger(ILogger<SecondHttpTrigger> logger)
    {
        _logger = logger;
    }

    [Function("second_http_function")]
    public MultiResponse Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
    {
        _logger.LogInformation("second_http_function function processed a request.");

        return new MultiResponse
        {
            Messages = new string[] { "Hello" },
            HttpResponse = req.CreateResponse(System.Net.HttpStatusCode.OK)
        };
    }
}

public class MultiResponse
{
    [ServiceBusOutput("%ServiceBusQueueName%", Connection = "ServiceBusConnection")]
    public string[]? Messages { get; set; }

    [HttpResult]
    public HttpResponseData? HttpResponse { get; set; }
}

Service Bus-köutlösare

public class ServiceBusQueueTrigger
{
    private readonly ILogger<ServiceBusQueueTrigger> _logger;

    public ServiceBusQueueTrigger(ILogger<ServiceBusQueueTrigger> logger)
    {
        _logger = logger;
    }

    [Function("servicebus_queue_trigger")]
    public async Task Run(
        [ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnection")]
        ServiceBusReceivedMessage message,
        ServiceBusMessageActions messageActions)
    {
        _logger.LogInformation("Message ID: {id}", message.MessageId);
        _logger.LogInformation("Message Body: {body}", message.Body);

        // Complete the message
        await messageActions.CompleteMessageAsync(message);
    }
}

Distribuerat spårningsflöde

Den här arkitekturen skapar ett fullständigt distribuerat spårningsscenario med följande beteende:

  1. Den första HTTP-funktionen tar emot en HTTP-begäran och anropar den andra HTTP-funktionen
  2. Den andra HTTP-funktionen svarar och skickar ett meddelande till Service Bus
  3. Service Bus-utlösaren bearbetar meddelandet med en fördröjning för att simulera bearbetningsarbete

Viktiga aspekter av OpenTelemetry-implementeringen:

  • OpenTelemetry-integrering: Filen host.json aktiverar OpenTelemetry med "telemetryMode": "OpenTelemetry"
  • Funktionslänkning: Den första funktionen anropar den andra med HJÄLP av HTTP-begäranden och skapar korrelerade spårningar
  • Service Bus-integrering: Den andra funktionen skickar sitt resultat till Service Bus, vilket utlöser den tredje funktionen
  • Anonym autentisering: HTTP-funktionerna använder auth_level=func.AuthLevel.ANONYMOUS, så inga funktionsnycklar krävs

Du kan granska hela mallprojektet här.

  • OpenTelemetry-integrering: Filen index.ts konfigurerar OpenTelemetry med Azure Monitor-exportörer för spårningar och loggar
  • Funktionslänkning: Den första funktionen anropar den andra med axios med automatisk spårningsspridning
  • Service Bus-integrering: Den andra funktionen skickar data till Service Bus genom utdatabindningar, vilket utlöser den tredje funktionen.
  • Hanterad identitet: Alla Service Bus-anslutningar använder hanterad identitet i stället för anslutningssträngar
  • Bearbetningssimulering: 5 sekunders fördröjning i Service Bus-utlösaren simulerar meddelandebearbetningsarbete

Du kan granska hela mallprojektet här.

  • OpenTelemetry-integrering: Filen Program.cs konfigurerar OpenTelemetry med Azure Monitor-exportören
  • Funktionslänkning: Den första funktionen anropar den andra med hjälp av HttpClient med OpenTelemetry-instrumentation
  • Service Bus-integrering: Den andra funktionen ger utdata till Service Bus med hjälp av utgångsbindningar, vilket utlöser den tredje funktionen
  • Hanterad identitet: Alla Service Bus-anslutningar använder hanterad identitet i stället för anslutningssträngar
  • .NET 8 Isolated Worker: Använder den senaste Azure Functions .NET Isolated Worker-modellen för bättre prestanda och flexibilitet

Du kan granska hela mallprojektet här.

När du har verifierat dina funktioner lokalt är det dags att publicera dem till Azure.

Distribuera till Azure

Det här projektet är konfigurerat för att använda azd up kommandot för att distribuera projektet till en ny funktionsapp i en Flex Consumption-plan i Azure med OpenTelemetry-stöd.

Tips/Råd

Det här projektet innehåller en uppsättning Bicep-filer som azd använder för att skapa en säker distribution till en Flex-förbrukningsplan som följer bästa praxis, inklusive hanterade identitetsanslutningar.

  1. Kör det här kommandot för att azd skapa nödvändiga Azure-resurser i Azure och distribuera kodprojektet till den nya funktionsappen:

    azd up
    

    Rotmappen innehåller den azure.yaml definitionsfil som krävs av azd.

    Om du inte redan är inloggad uppmanas du att autentisera med ditt Azure-konto.

  2. Ange följande nödvändiga distributionsparametrar när du uppmanas att göra det:

    Parameter Description
    Azure-prenumeration Prenumeration där dina resurser skapas.
    Azure-lokalisering Azure-region där du kan skapa resursgruppen som innehåller de nya Azure-resurserna. Endast regioner som för närvarande stöder Flex Consumption-planen visas.

    Kommandot azd up använder ditt svar på dessa frågor med Bicep-konfigurationsfilerna för att slutföra dessa distributionsuppgifter:

    • Skapa och konfigurera de här nödvändiga Azure-resurserna (motsvarande azd provision):

      • Azure Functions Flex Consumption Plan och funktionsapp med OpenTelemetry aktiverat
      • Azure Storage (krävs) och Application Insights (rekommenderas)
      • Service Bus-tjänstens namnrymd och kö för att demonstrera distribuerad spårning
      • Åtkomstprinciper och roller för ditt konto
      • Tjänst-till-tjänst-anslutningar med hanterade identiteter (i stället för lagrade anslutningssträng)
    • Paketera och distribuera koden till distributionscontainern (motsvarande azd deploy). Appen startas sedan och körs i det distribuerade paketet.

    När kommandot har slutförts visas länkar till de resurser som du har skapat.

Testa distribuerad spårning

Nu kan du testa funktionerna för distribuerad spårning i OpenTelemetry genom att anropa dina distribuerade funktioner och observera telemetrin i Application Insights.

Anropa funktionen på Azure

Du kan anropa funktionsslutpunkter i Azure genom att göra HTTP-begäranden till deras URL:er. Eftersom HTTP-funktionerna i den här mallen har konfigurerats med anonym åtkomst krävs inga funktionsnycklar.

  1. I den lokala terminalen eller kommandotolken kör du det här kommandot för att hämta funktionsappens namn och konstruera URL:en:

    APP_NAME=$(azd env get-value AZURE_FUNCTION_NAME)
    echo "Function URL: https://$APP_NAME.azurewebsites.net/api/first_http_function"
    

    Kommandot azd env get-value hämtar funktionsappens namn från den lokala miljön.

  2. Testa funktionen i webbläsaren genom att gå till URL:en:

    https://your-function-app.azurewebsites.net/api/first_http_function
    

    Ersätt your-function-app med ditt faktiska funktionsappnamn från föregående steg. Den här enskilda begäran skapar en distribuerad spårning som flödar genom alla tre funktionerna.

Visa distribuerad spårning i Application Insights

När du har anropat funktionen kan du se den fullständiga distribuerade spårningen i Application Insights:

Anmärkning

Det kan ta några minuter innan telemetridata visas i Application Insights när funktionen har anropats. Om du inte ser data omedelbart väntar du några minuter och uppdaterar vyn.

  1. Gå till din Application Insights-resurs i Azure-portalen (du hittar den i samma resursgrupp som funktionsappen).

  2. Öppna programkartan för att se den distribuerade spårningen för alla tre funktionerna. Du bör se flödet från HTTP-begäran via dina funktioner och till Service Bus.

  3. Kontrollera transaktionssökningen för att hitta din begäran och se den fullständiga tidslinjen för spårning. Sök efter transaktioner från funktionsappen.

  4. Välj en specifik transaktion för att se spårningen från slutpunkt till slutpunkt som visar:

    • HTTP-begäran till first_http_function
    • Det interna HTTP-anropet till second_http_function
    • Service Bus-meddelandet som skickas
    • Bearbetningen av servicebus_queue_trigger meddelandet från Service Bus
  5. I spårningsinformationen kan du se:

    • Tidsinformation: Hur lång tid varje steg tog
    • Beroenden: Anslutningarna mellan funktioner
    • Loggar: Programloggar som är korrelerade med spårningen
    • Prestandamått: Svarstider och dataflöde

Det här exemplet visar distribuerad spårning från slutpunkt till slutpunkt över flera Azure Functions med OpenTelemetry-integrering, vilket ger fullständig insyn i programmets beteende och prestanda.

Distribuera om koden

azd up Kör kommandot så många gånger du behöver både etablera dina Azure-resurser och distribuera koduppdateringar till funktionsappen.

Anmärkning

Det senaste distributionspaketet skriver alltid över distribuerade kodfiler.

Dina första svar på azd frågor och eventuella miljövariabler som genereras av azd lagras lokalt i din namngivna miljö. azd env get-values Använd kommandot för att granska alla variabler i din miljö som kommandot använder när du skapar Azure-resurser.

Rensa resurser

När du är klar med funktionsappen och relaterade resurser använder du det här kommandot för att ta bort funktionsappen och dess relaterade resurser från Azure och undvika ytterligare kostnader:

azd down --no-prompt

Anmärkning

Alternativet --no-prompt instruerar azd dig att ta bort resursgruppen utan någon bekräftelse från dig.

Det här kommandot påverkar inte ditt lokala kodprojekt.