1

I've spent several hours trying to get an ILogger working, injected into a class used by an Azure Function via dependency injection (DI). Eventually I came across this fix, mentioned in a couple of comments in GitHub: https://github.com/Azure/azure-functions-host/issues/4689#issuecomment-533195224 and https://github.com/Azure/azure-functions-host/issues/4689#issuecomment-751642356. This works for me.

That was from a few years ago, though. Is there now a neater way of getting an ILogger working via DI in an Azure Function? I'm not talking about logging in the parent function itself but logging in a separate class that's instantiated via DI.

This is what I have, which works but seems an ugly work-around:

Startup class:

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

[assembly: FunctionsStartup(typeof(BlobTrigger.Startup))]

namespace BlobTrigger
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            RegisterServicesWithDI(builder.Services);
        }

        private void RegisterServicesWithDI(IServiceCollection services)
        {
            services
                .AddSingleton<IWorker, Worker>();
        }
    }

Function class:

using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace BlobTrigger
{
    public class BlobTrigger
    {
        private readonly IWorker _worker;
        public BlobTrigger(IWorker worker)
        {
            this._worker = worker;
        }

        [FunctionName("BlobTrigger")]
        public void Run([BlobTrigger("%BlobPath%/{name}", Connection = "AzureStorageConnectionString")]Stream myBlob, 
            string name, ILogger log, ExecutionContext context)
        {
            log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");

            this._worker.DoWork();
        }
    }
}

Worker class:

using Microsoft.Extensions.Logging;

namespace BlobTrigger
{
    public interface IWorker
    {
        void DoWork();
    }

    public class Worker : IWorker
    {
        private readonly ILogger _logger;

        // Ugly!
        public Worker(ILoggerFactory loggerFactory) =>
            _logger = loggerFactory.CreateLogger(Microsoft.Azure.WebJobs.Logging.LogCategories.CreateFunctionUserCategory("BlobTrigger")); 
            
        public void DoWork()
        {
            _logger.LogInformation("Worker running");
        }
    }
}

The host.json file:

{
    "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "BlobTrigger.BlobTrigger": "Information",
      "BlobTrigger.Worker": "Information",
      "default": "Information"
    }
  }
}

EDIT: I've since discovered I didn't need .AddLogging() in the Startup class so I removed it from the code sample above. The ILoggerFactory will still get injected into the Worker class without .AddLogging() in the Startup class.

EDIT 2: Another discovery: Only the logs in the Azure Function "Code + Test" page are a problem. The Function App Streaming Logs do show messages from a logger injected the "traditional" way, as below:

using Microsoft.Extensions.Logging;

namespace BlobTrigger
{
    public interface IWorker
    {
        void DoWork();
    }

    public class Worker : IWorker
    {
        private readonly ILogger<IWorker> _logger;

        public Worker(ILogger<IWorker> logger) => _logger = logger; 
            
        public void DoWork()
        {
            _logger.LogInformation("Worker running");
        }
    }
}

But these messages do not get written to the Azure function "Code + Test" page logs. For messages to appear in that log, they have to have the log category mentioned above.

So the question is really "Is there a neater way of getting an ILogger writing to the Azure function Code + Test logs?"

Simon Elms
  • 17,832
  • 21
  • 87
  • 103
  • You can inject into a `Class` via constructor. Check [this](https://github.com/amigup/CleanArchitecture-For-AzureFunctionV3/blob/master/CleanArchitecture.Core/Implementation/ToDoItemsService.cs) for reference. – user1672994 Mar 28 '22 at 10:26

0 Answers0