13
votes

About a month ago, I noticed that some of the monitoring functionality in the old Azure Functions portal interface stopped working. I wrote more details about the issues on the Azure Functions Host GitHub but my particular questions are as of yet unanswered.

Now it seems the Azure Functions portal interface default to the new "management experience" that looks more similar to the rest of Azure, and with that, it's even more apparent that something is wrong in the way we use logging and tracing.

My question is: Does anybody have any code samples as to how to set up Azure Function logging, live metrics, and app insights tracing so that it:

  1. Works with dependency injection
  2. Works with the new "management experience" interface

Currently, in order to see what a particular Azure Function is doing, I have to go to the old Azure interface and study the log stream. The Functions do work, and they spit out information in the log stream, but only in the old interface, and not much else in terms of monitoring seems to work. Using the old interface:

  • The invocation logs, the one you get when you press the "Monitor" link under "Functions(Read Only) > [function] > Monitor, shows no invocations at all even though the functions are definitely being called according to the logs.
  • The Live app metrics link results in the default "Not available: your app is offline or using an older SDK" with some animated demo charts.

These worked fine a month ago. Now, not so much.

Using the new interface:

  • Monitoring > Log stream shows nothing except the word "Connected!", regardless of verbosity.
  • Monitoring > Log Stream > Open in Live Metrics again just yields the default "Not available: your app is offline or using an older SDK".

Going to a specific function in the new interface by using Functions > Functions > [click on a function]:

  • Developer > Code + Test > Test-button > Run, the Logs window pops up, just says "Connected!" and nothing else, again regardless of verbosity.
  • Monitor > Invocations, there are no invocation traces registered here, even though the function is obviously being called according to the old interface log stream.
  • Monitor > Logs, again, just says "Connected!", regardless of verbosity.

I don't understand why it suddenly stopped working a month back, and why so many things don't seem to work with the new interface. Our Functions' NuGet packages are all up to date.

In terms of logging, the logger is dependency injected so that we can use it in multiple classes and not just in the default Functions.cs class:

using Microsoft.Extensions.Logging;

public class EventForwarder
{
    private readonly ILogger<EventForwarder> log;

And we log through the use of extension methods, nothing fancy really:

using Microsoft.Extensions.Logging;

public static class LoggerExtensions
{
    public static void Info(this ILogger log, string msg) => log.LogInformation(msg);

The app insights tracer is also dependency injected using a workaround suggested here, i.e. our Startup.cs looks lite this:

using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(EventForwarder.Startup))]
namespace EventForwarder
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // https://github.com/Azure/azure-functions-host/issues/5353
            builder.Services.AddSingleton(sp =>
            {
                var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
                return string.IsNullOrWhiteSpace(key) ? new TelemetryConfiguration() : new TelemetryConfiguration(key);
            });

We're performing traces of Http retries, among other things, like so:

public class HttpRetryPolicyService
{
    private readonly ILogger<HttpRetryPolicyService> log;
    private readonly TelemetryClient insights;

    public HttpRetryPolicyService(ILogger<HttpRetryPolicyService> log,
        TelemetryConfiguration insightsConfig)
    {
        this.log = log;
        insights = new TelemetryClient(insightsConfig);
    }
...
private void LogRetry(DelegateResult<HttpResponseMessage> message, TimeSpan delay, int attempt, Context context)
{
    if (message.Exception != null)
    {
        log.Warn($"Exception details: {message.Exception}");
        insights.Track(message.Exception);

And we're using extension methods to trace, like so:

using Microsoft.ApplicationInsights;

namespace EventForwarder.Static
{
    public static class TelemetryExtensions
    {
        public static void Track(this TelemetryClient insights, string eventName)
        {
            insights.TrackEvent(eventName);
            insights.Flush();
        }

What am I missing?

Edit #1: Btw, adding Application Insights as a Service Dependency in the Publish dialog unfortunately does not solve these issues.

Edit #2: Also, preemtively, our Functions host.json files all look like this:

{
    "version": "2.0",
    "healthMonitor": {
        "enabled": true,
        "healthCheckInterval": "00:00:10",
        "healthCheckWindow": "00:02:00",
        "healthCheckThreshold": 6,
        "counterThreshold": 0.80
    },
    "logging": {
        "fileLoggingMode": "always",
        "applicationInsights": {
            "enableLiveMetrics": true,
            "samplingSettings": {
                "isEnabled": true
            }
        },
        "logLevel": {
            "EventForwarder": "Information"
        }
    }
}
1
Hi, I'm just using the new azure function, and everything for application insights seems to work well(just enable the application insights from azure portal). So may I know that why you want to use DI?Ivan Yang
@IvanYang Same as for any other use case for dependency injection, i.e. testability, mockability, IoC, avoiding static/global contexts, and not having to pass around logging, tracing, and other service classes with each method/function call. In my opinion it clutters up the code even if you wrap them in some kind of service object. It is officially supported since version 2x: docs.microsoft.com/en-us/azure/azure-functions/…Linus Proxy

1 Answers

8
votes

This is what breaks your app, remove it and everything should work:

// https://github.com/Azure/azure-functions-host/issues/5353
builder.Services.AddSingleton(sp =>
{
    var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
    return string.IsNullOrWhiteSpace(key) ? new TelemetryConfiguration() : new TelemetryConfiguration(key);
});

My guess would be that the workaround actually breaks the logging now that the bugfix has been rolled out.

I created a sample app where logging and log stream work quite nicely, also with dependency injection. I tested it with both Windows and Linux consumption plans. The function app was created using the wizard in the Azure Portal, selecting .NET Core 3.1. Please be aware that TrackEvent does not show up in the function's log stream. It shows up in Application Insights Live Metrics. It can also take up to 30s after "Connected" shows up until actual logs are shown. The Live Metrics view works better, especially if you open directly from application insights.

I was able to reproduce your issues by applying the "workaround" mentioned above. Without it, everything works fine.

Full sample: https://github.com/LXBdev/Functions-V3-sample

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddScoped<MyService>();
    }
  "logging": {
    "applicationInsights": {
      "samplingExcludedTypes": "Request",
      "samplingSettings": {
        "isEnabled": true
      }
    },
    "logLevel": {
      "Functions_V3_sample": "Information"
    }
  }
public MyService(ILogger<MyService> logger, TelemetryClient telemetry)
{
    Logger = logger ?? throw new ArgumentNullException(nameof(logger));
    Telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry));
}

public void Foo()
{
    Logger.LogInformation("Foo");
    Telemetry.TrackTrace("BarLog", Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Information);
    Telemetry.TrackEvent("BarEvent");
}

Update: There was an issue with the host.json in my original answer and sample - logs weren't really persisted to AppInsights because of https://github.com/Azure/azure-functions-host/issues/4345. I updated the code accordingly.

execution count

log stream

live metrics

appsettings