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?"