Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 8 - Isolated Worker - IHostBuilder - Unable to log DEBUG or TRACE to Application Insights from Linux S1 App Service Plans #2883

Open
springcomp opened this issue Nov 28, 2024 · 0 comments
Labels
Needs: Triage (Functions) potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug

Comments

@springcomp
Copy link

springcomp commented Nov 28, 2024

Description

The Guide for running C# Azure Functions in the isolated worker mode page does a really great job outlining how to properly setup logging with Application Insights.

However, the page highlight two syntax flavors, one named IHostBuilder and the other named IHostApplicationBuilder.

Using the IHostBuilder syntax, I find that it is impossible to send Debug or Trace logs to Application Insights out of the box.

Here is the syntax used (as taken straight from the documentation referred to above)

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
        services.Configure<LoggerFilterOptions>(options => {
          // The Application Insights SDK adds a default logging filter that instructs ILogger to capture only Warning and more severe logs. Application Insights requires an explicit override.
          // Log levels can also be configured using appsettings.json. For more information, see https://learn.microsoft.com/en-us/azure/azure-monitor/app/worker-service#ilogger-logs
          LoggerFilterRule? toRemove = options.Rules.FirstOrDefault(rule => rule.ProviderName
            == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");
          if (toRemove is not null)
          {
              options.Rules.Remove(toRemove);
          }
      });
    })
    .Build();

host.Run();

In contrast to the IHostApplicationBuilder syntax, I find that the code shown here does not automatically load the existing appsettings.json file. As a result, the log levels and categories are not setup at all with this code, when running on a Linux S1 App Service Plan.

I then need to add the following two fragments of code which are not in the documentation.
The first fragment registers the appsettings.json file as the source for configuration:

.ConfigureAppConfiguration((hostContext, config) =>
    {
        // Add appsettings.json configuration so we can set logging in configuration.
        // For instance, add a new file called "appsettings.json" to the root of your project
        // and set the following properties to:
        // Build Action: None
        // Copy to Output Directory: Copy if newer
        //
        // Content:
        // {
        //   "Logging": {
        //     "LogLevel": {
        //       "Default": "Warning" // Change this to i.e. "Trace" for more logging
        //     }
        //   }
        // }
        config.AddJsonFile("appsettings.json", optional: true);
        config.AddEnvironmentVariables();
    })

The second fragment actually loads and configure log levels and categories from the Logging configuration section:

.ConfigureLogging((hostingContext, logging) =>
    {
        var services = logging.Services;
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    })

When the code is complete, this gives us full control on log levels and categories when running on Windows App Service Plans.
However, it does not work when running on Linux S1 App Service Plans.
There, I am unable to emit logs lower than Information.

Is the documentation correct ?
Is this a bug in the Application Insights SDK (I find this unlikely) ?
Is this a bug on Linux S1 App Service Plans ? In that case, how shall I report that bug ?
Can someone explain why there are two different syntaxes to achieve seemingly the same things ?

Steps to reproduce

  1. Create a new function app running on Azure Portal with Linux S1 App Service Plan
  2. In Visual Studio Code, install the Function App extension.
  3. Create a new Function App project in Visual Studio Code using .NET 8.0 Isolated LTS.
  4. Add a new HttpTrigger named HelloWorldHttpTrigger under namespace AzFuncUni.Logging.
  5. Enable Application Insights by uncommenting the references to the packages in the .csproj file.
  6. Replace the Program.cs with the following code using the IHostBuilder pattern:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
        services.Configure<LoggerFilterOptions>(options => {
          // The Application Insights SDK adds a default logging filter that instructs ILogger to capture only Warning and more severe logs. Application Insights requires an explicit override.
          // Log levels can also be configured using appsettings.json. For more information, see https://learn.microsoft.com/en-us/azure/azure-monitor/app/worker-service#ilogger-logs
          LoggerFilterRule? toRemove = options.Rules.FirstOrDefault(rule => rule.ProviderName
            == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");
          if (toRemove is not null)
          {
              options.Rules.Remove(toRemove);
          }
      });
    })
    .Build();

host.Run();
  1. Create a file named appsettings.json with the following contents:
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "AzFuncUni.Logging.HelloWorldHttpTrigger": "Trace"
    }
}
  1. Make sure that the appsettings.json file is copied to the output folder.
  2. Replace the code of the HttpTrigger function with the following code:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace AzFuncUni.Logging
{
    public class HelloWorldHttpTrigger
    {
        private readonly ILogger<HelloWorldHttpTrigger> _logger;

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

        [Function("HelloWorldHttpTrigger")]
        public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
        {
            _logger.LogTrace("TRAC: C# HTTP trigger function processed a request.");
            _logger.LogDebug("DBUG: C# HTTP trigger function processed a request.");
            _logger.LogInformation("INFO: C# HTTP trigger function processed a request.");
            _logger.LogWarning("WARN: C# HTTP trigger function processed a request.");
            _logger.LogError("ERR: C# HTTP trigger function processed a request.");

            return new OkObjectResult("Welcome to Azure Functions!");
        }
    }
}
  1. Compile and deploy the function on Azure.
  2. Sends some HTTP requests to the function app
POST https://<your-function-app>.azurewebsites.net/api/HelloWorldHttpTrigger?code=<your-function-app-key>

Notice that the logs DBUG and TRAC are not emitted. Only logs INFO, WARN and ERR are recorded to application insights.

  1. Replace the code in Program.cs with the one documented for IHostApplicationBuilder like so:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

// Logging to Application Insights

builder.Services
    .AddApplicationInsightsTelemetryWorkerService()
    .ConfigureFunctionsApplicationInsights()
    .Configure<LoggerFilterOptions>(options =>
    {
        // The Application Insights SDK adds a default logging filter that instructs ILogger to capture only Warning and more severe logs. Application Insights requires an explicit override.
        // Log levels can also be configured using appsettings.json. For more information, see https://learn.microsoft.com/en-us/azure/azure-monitor/app/worker-service#ilogger-logs
        LoggerFilterRule? toRemove = options.Rules.FirstOrDefault(rule => rule.ProviderName
            == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");
        if (toRemove is not null)
        {
            options.Rules.Remove(toRemove);
        }
    })
    ;

builder.Build().Run();
  1. Compile and deploy the function app again.
  2. Send some more HTTP requests.

Notice that, this time, TRAC and DBUG logs are, indeed, recorded in Application Insights, along with INFO, WARN and ERR logs.

@springcomp springcomp added the potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug label Nov 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage (Functions) potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug
Projects
None yet
Development

No branches or pull requests

1 participant