Monitor Azure Functions

Azure Functions offers built-in integration with Azure Application Insights to monitor functions. This article shows you how to configure Azure Functions to send system-generated log files to Application Insights.

We recommend using Application Insights because it collects log, performance, and error data. It automatically detects performance anomalies and includes powerful analytics tools to help you diagnose issues and to understand how your functions are used. It's designed to help you continuously improve performance and usability. You can even use Application Insights during local function app project development. For more information, see What is Application Insights?.

As the required Application Insights instrumentation is built into Azure Functions, all you need is a valid instrumentation key to connect your function app to an Application Insights resource.

Application Insights pricing and limits

You can try out Application Insights integration with Function Apps for free. There's a daily limit to how much data can be processed for free. You might hit this limit during testing. Azure provides portal and email notifications when you're approaching your daily limit. If you miss those alerts and hit the limit, new logs won't appear in Application Insights queries. Be aware of the limit to avoid unnecessary troubleshooting time. For more information, see Manage pricing and data volume in Application Insights.

Enable Application Insights integration

For a function app to send data to Application Insights, it needs to know the instrumentation key of an Application Insights resource. The key must be in an app setting named APPINSIGHTS_INSTRUMENTATIONKEY.

New function app in the portal

When you create your function app in the Azure portal, Application Insights integration is enabled by default. The Application Insights resource has the same name as your function app, and it's created either in the same region or in nearest region.

To review the Application Insights resource being created, select it to expand the Application Insights window. You can change the New resource name or choose a different Location in an Azure geography where you want to store your data.

Enable Application Insights while creating a function app

When you choose Create, an Application Insights resource is created with your function app, which has the APPINSIGHTS_INSTRUMENTATIONKEY set in application settings. Everything is ready to go.

Add to an existing function app

When you create a function app using the Azure CLI, Visual Studio, or Visual Studio Code, you must create the Application Insights resource. You can then add the instrumentation key from that resource as an application setting in your function app.

Functions makes it simple to add Application Insights integration to a function app from the Azure portal.

  1. In the portal, select All services > Function Apps, select your function app, and then choose the Application Insights banner at the top of the window

    Enable Application Insights from the portal

  2. Create an Application Insights resource by using the settings specified in the table below the image:

    Create an Application Insights resource

    Setting Suggested value Description
    Name Unique app name It's easiest to use the same name as your function app, which must be unique in your subscription.
    Location West Europe If possible, use the same region as your function app, or near to it.
  3. Choose OK. The Application Insights resource is created in the same resource group and subscription as your function app. After creation completes, close the Application Insights window.

  4. Back in your function app, select Application settings, and scroll down to Application settings. When you see a setting named APPINSIGHTS_INSTRUMENTATIONKEY, it means that Application Insights integration is enabled for your function app running in Azure.

Early versions of Functions used built-in monitoring, which is no longer recommended. When enabling Application Insights integration for such a function app, you must also disable built-in logging.

View telemetry in Monitor tab

With Application Insights integration enabled, you can view telemetry data in the Monitor tab.

  1. In the function app page, select a function that has run at least once after Application Insights was configured. Then select the Monitor tab.

    Select Monitor tab

  2. Select Refresh periodically, until the list of function invocations appears.

    It can take up to five minutes for the list to appear while the telemetry client batches data for transmission to the server. (The delay doesn't apply to the Live Metrics Stream. That service connects to the Functions host when you load the page, so logs are streamed directly to the page.)

    Invocations list

  3. To see the logs for a particular function invocation, select the Date column link for that invocation.

    Invocation details link

    The logging output for that invocation appears in a new page.

    Invocation details

You can see that both pages have a Run in Application Insights link to the Application Insights Analytics query that retrieves the data.

Run in Application Insights

The following query is displayed. You can see that the invocation list is limited to the last 30 days. The list shows no more than 20 rows (where timestamp > ago(30d) | take 20). The invocation details list is for the last 30 days with no limit.

Application Insights Analytics invocation list

For more information, see Query telemetry data later in this article.

View telemetry in Application Insights

To open Application Insights from a function app in the Azure portal, go to the function app's Overview page. Under Configured features, select Application Insights.

Open Application Insights from the function app Overview page

For information about how to use Application Insights, see the Application Insights documentation. This section shows some examples of how to view data in Application Insights. If you're already familiar with Application Insights, you can go directly to the sections about how to configure and customize the telemetry data.

Application Insights Overview tab

The following areas of Application Insights can be helpful when evaluating the behavior, performance, and errors in your functions:

Tab Description
Failures Create charts and alerts based on function failures and server exceptions. The Operation Name is the function name. Failures in dependencies aren't shown unless you implement custom telemetry for dependencies.
Performance Analyze performance issues.
Servers View resource utilization and throughput per server. This data can be useful for debugging scenarios where functions are bogging down your underlying resources. Servers are referred to as Cloud role instances.
Metrics Create charts and alerts that are based on metrics. Metrics include the number of function invocations, execution time, and success rates.
Live Metrics Stream View metrics data as it's created in real time.

Query telemetry data

Application Insights Analytics gives you access to all telemetry data in the form of tables in a database. Analytics provides a query language for extracting, manipulating, and visualizing the data.

Select Analytics

Analytics example

Here's a query example that shows the distribution of requests per worker over the last 30 minutes.

requests
| where timestamp > ago(30m) 
| summarize count() by cloud_RoleInstance, bin(timestamp, 1m)
| render timechart

The tables that are available are shown in the Schema tab on the left. You can find data generated by function invocations in the following tables:

Table Description
traces Logs created by the runtime and by function code.
requests One request for each function invocation.
exceptions Any exceptions thrown by the runtime.
customMetrics The count of successful and failing invocations, success rate, and duration.
customEvents Events tracked by the runtime, for example: HTTP requests that trigger a function.
performanceCounters Information about the performance of the servers that the functions are running on.

The other tables are for availability tests, and client and browser telemetry. You can implement custom telemetry to add data to them.

Within each table, some of the Functions-specific data is in a customDimensions field. For example, the following query retrieves all traces that have log level Error.

traces 
| where customDimensions.LogLevel == "Error"

The runtime provides the customDimensions.LogLevel and customDimensions.Category fields. You can provide additional fields in logs that you write in your function code. See Structured logging later in this article.

Configure categories and log levels

You can use Application Insights without any custom configuration. The default configuration can result in high volumes of data. If you're using a Visual Studio Azure subscription, you might hit your data cap for Application Insights. Later in this article, you learn how to configure and customize the data that your functions send to Application Insights. For a function app, logging is configured in the host.json file.

Categories

The Azure Functions logger includes a category for every log. The category indicates which part of the runtime code or your function code wrote the log.

The Functions runtime creates logs with a category that begin with "Host." The "function started," "function executed," and "function completed" logs have the category "Host.Executor."

If you write logs in your function code, their category is "Function."

Log levels

The Azure Functions logger also includes a log level with every log. LogLevel is an enumeration, and the integer code indicates relative importance:

LogLevel Code
Trace 0
Debug 1
Information 2
Warning 3
Error 4
Critical 5
None 6

Log level None is explained in the next section.

Log configuration in host.json

The host.json file configures how much logging a function app sends to Application Insights. For each category, you indicate the minimum log level to send. There are two examples: the first example targets the Functions version 2.x runtime (.NET Core) and the second example is for the version 1.x runtime.

Version 2.x

The v2.x runtime uses the .NET Core logging filter hierarchy.

{
  "logging": {
    "fileLoggingMode": "always",
    "logLevel": {
      "default": "Information",
      "Host.Results": "Error",
      "Function": "Error",
      "Host.Aggregator": "Trace"
    }
  }
}

Version 1.x

{
  "logger": {
    "categoryFilter": {
      "defaultLevel": "Information",
      "categoryLevels": {
        "Host.Results": "Error",
        "Function": "Error",
        "Host.Aggregator": "Trace"
      }
    }
  }
}

This example sets up the following rules:

  • For logs with category Host.Results or Function, send only Error level and above to Application Insights. Logs for Warning level and below are ignored.
  • For logs with category Host.Aggregator, send all logs to Application Insights. The Trace log level is the same as what some loggers call Verbose, but use Trace in the host.json file.
  • For all other logs, send only Information level and above to Application Insights.

The category value in host.json controls logging for all categories that begin with the same value. Host in host.json controls logging for Host.General, Host.Executor, Host.Results, and so on.

If host.json includes multiple categories that start with the same string, the longer ones are matched first. Suppose you want everything from the runtime except Host.Aggregator to log at Error level, but you want Host.Aggregator to log at the Information level:

Version 2.x

{
  "logging": {
    "fileLoggingMode": "always",
    "logLevel": {
      "default": "Information",
      "Host": "Error",
      "Function": "Error",
      "Host.Aggregator": "Information"
    }
  }
}

Version 1.x

{
  "logger": {
    "categoryFilter": {
      "defaultLevel": "Information",
      "categoryLevels": {
        "Host": "Error",
        "Function": "Error",
        "Host.Aggregator": "Information"
      }
    }
  }
}

To suppress all logs for a category, you can use log level None. No logs are written with that category and there's no log level above it.

The following sections describe the main categories of logs that the runtime creates.

Category Host.Results

These logs show as "requests" in Application Insights. They indicate success or failure of a function.

Requests chart

All of these logs are written at Information level. If you filter at Warning or above, you won't see any of this data.

Category Host.Aggregator

These logs provide counts and averages of function invocations over a configurable period of time. The default period is 30 seconds or 1,000 results, whichever comes first.

The logs are available in the customMetrics table in Application Insights. Examples are the number of runs, success rate, and duration.

customMetrics query

All of these logs are written at Information level. If you filter at Warning or above, you won't see any of this data.

Other categories

All logs for categories other than the ones already listed are available in the traces table in Application Insights.

traces query

All logs with categories that begin with Host are written by the Functions runtime. The "Function started" and "Function completed" logs have category Host.Executor. For successful runs, these logs are Information level. Exceptions are logged at Error level. The runtime also creates Warning level logs, for example: queue messages sent to the poison queue.

Logs written by your function code have category Function and can be any log level.

Configure the aggregator

As noted in the previous section, the runtime aggregates data about function executions over a period of time. The default period is 30 seconds or 1,000 runs, whichever comes first. You can configure this setting in the host.json file. Here's an example:

{
    "aggregator": {
      "batchSize": 1000,
      "flushTimeout": "00:00:30"
    }
}

Configure sampling

Application Insights has a sampling feature that can protect you from producing too much telemetry data on completed executions at times of peak load. When the rate of incoming executions exceeds a specified threshold, Application Insights starts to randomly ignore some of the incoming executions. The default setting for maximum number of executions per second is 20 (five in version 1.x). You can configure sampling in host.json. Here's an example:

Version 2.x

{
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "maxTelemetryItemsPerSecond" : 20
      }
    }
  }
}

Version 1.x

{
  "applicationInsights": {
    "sampling": {
      "isEnabled": true,
      "maxTelemetryItemsPerSecond" : 5
    }
  }
}

Note

Sampling is enabled by default. If you appear to be missing data, you might need to adjust the sampling settings to fit your particular monitoring scenario.

Write logs in C# functions

You can write logs in your function code that appear as traces in Application Insights.

ILogger

Use an ILogger parameter in your functions instead of a TraceWriter parameter. Logs created by using TraceWriter go to Application Insights, but ILogger lets you do structured logging.

With an ILogger object, you call Log<level> extension methods on ILogger to create logs. The following code writes Information logs with category "Function."

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger logger)
{
    logger.LogInformation("Request for item with key={itemKey}.", id);

Structured logging

The order of placeholders, not their names, determines which parameters are used in the log message. Suppose you have the following code:

string partitionKey = "partitionKey";
string rowKey = "rowKey";
logger.LogInformation("partitionKey={partitionKey}, rowKey={rowKey}", partitionKey, rowKey);

If you keep the same message string and reverse the order of the parameters, the resulting message text would have the values in the wrong places.

Placeholders are handled this way so that you can do structured logging. Application Insights stores the parameter name-value pairs and the message string. The result is that the message arguments become fields that you can query on.

If your logger method call looks like the previous example, you can query the field customDimensions.prop__rowKey. The prop__ prefix is added to ensure there are no collisions between fields the runtime adds and fields your function code adds.

You can also query on the original message string by referencing the field customDimensions.prop__{OriginalFormat}.

Here's a sample JSON representation of customDimensions data:

{
  customDimensions: {
    "prop__{OriginalFormat}":"C# Queue trigger function processed: {message}",
    "Category":"Function",
    "LogLevel":"Information",
    "prop__message":"c9519cbf-b1e6-4b9b-bf24-cb7d10b1bb89"
  }
}

Custom metrics logging

In C# script functions, you can use the LogMetric extension method on ILogger to create custom metrics in Application Insights. Here's a sample method call:

logger.LogMetric("TestMetric", 1234);

This code is an alternative to calling TrackMetric by using the Application Insights API for .NET.

Write logs in JavaScript functions

In Node.js functions, use context.log to write logs. Structured logging isn't enabled.

context.log('JavaScript HTTP trigger function processed a request.' + context.invocationId);

Custom metrics logging

When you're running on version 1.x of the Functions runtime, Node.js functions can use the context.log.metric method to create custom metrics in Application Insights. This method isn't currently supported in version 2.x. Here's a sample method call:

context.log.metric("TestMetric", 1234);

This code is an alternative to calling trackMetric by using the Node.js SDK for Application Insights.

Log custom telemetry in C# functions

You can use the Microsoft.ApplicationInsights NuGet package to send custom telemetry data to Application Insights. The following C# example uses the custom telemetry API. The example is for a .NET class library, but the Application Insights code is the same for C# script.

Version 2.x

The version 2.x runtime uses newer features in Application Insights to automatically correlate telemetry with the current operation. There's no need to manually set the operation Id, ParentId, or Name fields.

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;

namespace functionapp0915
{
    public class HttpTrigger2
    {
        private readonly TelemetryClient telemetryClient;

        /// Using dependency injection will guarantee that you use the same configuration for telemetry collected automatically and manually.
        public HttpTrigger2(TelemetryConfiguration telemetryConfiguration)
        {
            this.telemetryClient = new TelemetryClient(telemetryConfiguration);
        }

        [FunctionName("HttpTrigger2")]
        public Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req, ExecutionContext context, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            DateTime start = DateTime.UtcNow;

            // Parse query parameter
            string name = req.Query
                .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
                .Value;

            // Track an Event
            var evt = new EventTelemetry("Function called");
            evt.Context.User.Id = name;
            this.telemetryClient.TrackEvent(evt);

            // Track a Metric
            var metric = new MetricTelemetry("Test Metric", DateTime.Now.Millisecond);
            metric.Context.User.Id = name;
            this.telemetryClient.TrackMetric(metric);

            // Track a Dependency
            var dependency = new DependencyTelemetry
            {
                Name = "GET api/planets/1/",
                Target = "swapi.co",
                Data = "https://swapi.co/api/planets/1/",
                Timestamp = start,
                Duration = DateTime.UtcNow - start,
                Success = true
            };
            dependency.Context.User.Id = name;
            this.telemetryClient.TrackDependency(dependency);

            return Task.FromResult<IActionResult>(new OkResult());
        }
    }
}

Version 1.x

using System;
using System.Net;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Linq;

namespace functionapp0915
{
    public static class HttpTrigger2
    {
        private static string key = TelemetryConfiguration.Active.InstrumentationKey = 
            System.Environment.GetEnvironmentVariable(
                "APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);

        private static TelemetryClient telemetryClient = 
            new TelemetryClient() { InstrumentationKey = key };

        [FunctionName("HttpTrigger2")]
        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
            HttpRequestMessage req, ExecutionContext context, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            DateTime start = DateTime.UtcNow;

            // Parse query parameter
            string name = req.GetQueryNameValuePairs()
                .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
                .Value;

            // Get request body
            dynamic data = await req.Content.ReadAsAsync<object>();

            // Set name to query string or body data
            name = name ?? data?.name;
         
            // Track an Event
            var evt = new EventTelemetry("Function called");
            UpdateTelemetryContext(evt.Context, context, name);
            telemetryClient.TrackEvent(evt);
            
            // Track a Metric
            var metric = new MetricTelemetry("Test Metric", DateTime.Now.Millisecond);
            UpdateTelemetryContext(metric.Context, context, name);
            telemetryClient.TrackMetric(metric);
            
            // Track a Dependency
            var dependency = new DependencyTelemetry
                {
                    Name = "GET api/planets/1/",
                    Target = "swapi.co",
                    Data = "https://swapi.co/api/planets/1/",
                    Timestamp = start,
                    Duration = DateTime.UtcNow - start,
                    Success = true
                };
            UpdateTelemetryContext(dependency.Context, context, name);
            telemetryClient.TrackDependency(dependency);
        }
        
        // Correlate all telemetry with the current Function invocation
        private static void UpdateTelemetryContext(TelemetryContext context, ExecutionContext functionContext, string userName)
        {
            context.Operation.Id = functionContext.InvocationId.ToString();
            context.Operation.ParentId = functionContext.InvocationId.ToString();
            context.Operation.Name = functionContext.FunctionName;
            context.User.Id = userName;
        }
    }    
}

Don't call TrackRequest or StartOperation<RequestTelemetry> because you'll see duplicate requests for a function invocation. The Functions runtime automatically tracks requests.

Don't set telemetryClient.Context.Operation.Id. This global setting causes incorrect correlation when many functions are running simultaneously. Instead, create a new telemetry instance (DependencyTelemetry, EventTelemetry) and modify its Context property. Then pass in the telemetry instance to the corresponding Track method on TelemetryClient (TrackDependency(), TrackEvent()). This method ensures that the telemetry has the correct correlation details for the current function invocation.

Log custom telemetry in JavaScript functions

The Application Insights Node.js SDK is currently in beta. Here's some sample code that sends custom telemetry to Application Insights:

const appInsights = require("applicationinsights");
appInsights.setup();
const client = appInsights.defaultClient;

module.exports = function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    client.trackEvent({name: "my custom event", tagOverrides:{"ai.operation.id": context.invocationId}, properties: {customProperty2: "custom property value"}});
    client.trackException({exception: new Error("handled exceptions can be logged with this method"), tagOverrides:{"ai.operation.id": context.invocationId}});
    client.trackMetric({name: "custom metric", value: 3, tagOverrides:{"ai.operation.id": context.invocationId}});
    client.trackTrace({message: "trace message", tagOverrides:{"ai.operation.id": context.invocationId}});
    client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL", tagOverrides:{"ai.operation.id": context.invocationId}});
    client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true, tagOverrides:{"ai.operation.id": context.invocationId}});

    context.done();
};

The tagOverrides parameter sets the operation_Id to the function's invocation ID. This setting enables you to correlate all of the automatically generated and custom telemetry for a given function invocation.

Dependencies

Functions v2 automatically collects dependencies for HTTP requests, ServiceBus, and SQL.

You can write custom code to show the dependencies. For examples, see the sample code in the C# custom telemetry section. The sample code results in an application map in Application Insights that looks like the following image:

Application map

Report issues

To report an issue with Application Insights integration in Functions, or to make a suggestion or request, create an issue in GitHub.

Streaming Logs

While developing an application, it is often useful to see logging information in near-real time. You can view a stream of log files being generated by your functions either in the Azure portal or in a command-line session on your local computer.

This is equivalent to the output seen when you debug your functions during local development. For more information, see How to stream logs.

Note

Streaming logs support only a single instance of the Functions host. When your function is scaled to multiple instances, data from other instances are not shown in the log stream. The Live Metrics Stream in Application Insights does supported multiple instances. While also in near real time, streaming analytics are also based on sampled data.

Portal

To view streaming logs in the portal, select the Platform features tab in your function app. Then, under Monitoring, choose Log streaming.

Enable streaming logs in the portal

This connects your app to the log streaming service and application logs are displayed in the window. You can toggle between Application logs and Web server logs.

View streaming logs in the portal

Azure CLI

You can enable streaming logs by using the Azure CLI. Use the following commands to sign in, choose your subscription, and stream log files:

az login
az account list
az account set --subscription <subscriptionNameOrId>
az webapp log tail --resource-group <RESOURCE_GROUP_NAME> --name <FUNCTION_APP_NAME>

Azure PowerShell

You can enable streaming logs by using Azure PowerShell. For PowerShell, use the following commands to add your Azure account, choose your subscription, and stream log files:

Add-AzAccount
Get-AzSubscription
Get-AzSubscription -SubscriptionName "<subscription name>" | Select-AzSubscription
Get-AzWebSiteLog -Name <FUNCTION_APP_NAME> -Tail

Disable built-in logging

When you enable Application Insights, disable the built-in logging that uses Azure Storage. The built-in logging is useful for testing with light workloads, but isn't intended for high-load production use. For production monitoring, we recommend Application Insights. If built-in logging is used in production, the logging record might be incomplete because of throttling on Azure Storage.

To disable built-in logging, delete the AzureWebJobsDashboard app setting. For information about how to delete app settings in the Azure portal, see the Application settings section of How to manage a function app. Before you delete the app setting, make sure no existing functions in the same function app use the setting for Azure Storage triggers or bindings.

Next steps

For more information, see the following resources: