Skip to content

04 - Logging and Monitoring (Consumption)

Enable production-grade observability with Application Insights, structured logs, and baseline alerting for .NET isolated worker handlers.

Prerequisites

Tool Version Purpose
.NET SDK 8.0 (LTS) Build and run isolated worker functions
Azure Functions Core Tools v4 Start local host and publish artifacts
Azure CLI 2.61+ Provision Azure resources and inspect app state

Consumption plan basics

Consumption (Y1) is serverless with scale-to-zero, up to 200 instances, 1.5 GB memory per instance, and a default 5-minute timeout (max 10 minutes).

What You'll Build

You will instrument .NET isolated worker handlers with structured logs via ILogger<T>, route telemetry to Application Insights, and validate query-based monitoring signals for a Consumption-hosted app.

flowchart LR
    A["ILogger<T> in handler"] --> B[FunctionAppLogs]
    B --> C[Application Insights]
    C --> D[Dashboards and alerts]

Steps

Step 1 - Emit structured logs in handler methods

The .NET isolated worker uses constructor-injected ILogger<T> for structured logging. Here is the LogLevelsFunction that emits at multiple severity levels:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace AzureFunctionsGuide.Functions;

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

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

    [Function("logLevels")]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "loglevels")] HttpRequest req)
    {
        _logger.LogDebug("Debug level log message");
        _logger.LogInformation("Info level log message");
        _logger.LogWarning("Warning level log message");
        _logger.LogError("Error level log message");
        _logger.LogCritical("Critical level log message");

        return new OkObjectResult(new { logged = true });
    }
}

Step 2 - Register Application Insights in the isolated worker

The Program.cs must register Application Insights services for telemetry collection:

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .Build();

host.Run();

Isolated worker telemetry packages

The reference app includes Microsoft.ApplicationInsights.WorkerService and Microsoft.Azure.Functions.Worker.ApplicationInsights packages in the .csproj for telemetry collection.

Step 3 - Generate telemetry by calling endpoints

# Trigger structured logging
curl --request GET "https://$APP_NAME.azurewebsites.net/api/loglevels"

# Trigger health check
curl --request GET "https://$APP_NAME.azurewebsites.net/api/health"

# Trigger intentional errors for error telemetry
curl --request GET "https://$APP_NAME.azurewebsites.net/api/testerror"

Telemetry ingestion delay

Application Insights telemetry takes 2-5 minutes to become available for queries after the first request. Wait before running queries.

Step 4 - Confirm Application Insights connection

Application Insights is auto-created with the function app. Verify the connection:

az functionapp config appsettings list \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --query "[?name=='APPLICATIONINSIGHTS_CONNECTION_STRING'].value" \
  --output tsv

Step 5 - Query recent traces

az monitor app-insights query \
  --app "$APP_NAME" \
  --resource-group "$RG" \
  --analytics-query "traces | where timestamp > ago(30m) | project timestamp, message, severityLevel | order by timestamp desc | take 20"

Use function app name for --app

Since Application Insights is auto-created with the same name as the function app, use --app "$APP_NAME" for queries. Add --resource-group "$RG" to avoid ambiguity.

Step 6 - Query request metrics

az monitor app-insights query \
  --app "$APP_NAME" \
  --resource-group "$RG" \
  --analytics-query "requests | where timestamp > ago(30m) | project timestamp, name, resultCode, duration | order by timestamp desc | take 20"

Step 7 - View live log stream

az webapp log tail \
  --name "$APP_NAME" \
  --resource-group "$RG"

Streaming logs on Consumption

On Consumption plan, az webapp log tail may return a 404 error because the app scales to zero when idle. This is expected behavior. Use Application Insights queries instead for historical log data.

Step 8 - Add an alert for HTTP 5xx spikes

FUNCTION_APP_ID=$(az functionapp show \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --query "id" \
  --output tsv)

az monitor metrics alert create \
  --name "func-dotnet-http5xx" \
  --resource-group "$RG" \
  --scopes "$FUNCTION_APP_ID" \
  --condition "total Http5xx > 5" \
  --window-size 5m \
  --evaluation-frequency 1m

Verification

Traces query output:

timestamp                    message                           severityLevel
---------------------------  --------------------------------  -------------
2026-04-09T17:52:20.000Z     Info level log message            1
2026-04-09T17:52:20.000Z     Warning level log message         2
2026-04-09T17:52:20.000Z     Error level log message           3
2026-04-09T17:52:20.000Z     Critical level log message        4

Requests query output:

timestamp                    name            resultCode    duration
---------------------------  --------------  ----------    --------
2026-04-09T17:52:20.000Z     logLevels       200           45.12
2026-04-09T17:52:19.000Z     health          200           12.34
2026-04-09T17:52:18.000Z     testError       500           8.56

LogLevels endpoint response:

{"logged":true}

Next Steps

Next: 05 - Infrastructure as Code

See Also

Sources