44

I am using Azure Function v2. Here is my function that uses the constructor injection:

public sealed class FindAccountFunction
{
    private readonly IAccountWorkflow m_accountWorkflow;

    private readonly IMapper m_mapper;

    private readonly ILogger m_logger;

    public FindAccountFunction(ILogger logger, IMapper mapper, IAccountWorkflow accountWorkflow)
    {
        m_logger = logger;
        m_mapper = mapper;
        m_accountWorkflow = accountWorkflow;
    }

    [FunctionName("FindAccount")]
    public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, Verbs.Get, Route = "v1/accounts/")] HttpRequest httpRequest, ILogger logger)
    {
        // Do stuff.
    }
}

I am declaring all the dependencies that I want to inject into my Azure Function in the Startup class that derives from IWebJobsStartup:

    public sealed class Startup : IWebJobsStartup
    {
        public void Configure(IWebJobsBuilder webJobsBuilder)
        {
            //  Registers the application settings' class.
            webJobsBuilder.Services.AddSingleton<IApplicationSettings, ApplicationSettings>();

            //  ** Registers the ILogger instance **
            //  ** ?? **

            //  Registers the IMapper instance for the contracts.
            var mapperConfiguration = new MapperConfiguration(cfg => cfg.AddProfile(new MyProfile()));

     webJobsBuilder.Services.AddSingleton(mapperConfiguration.CreateMapper());

            // Registers custom services.
            webJobsBuilder.Services.AddTransient<IStorageService, StorageService>();

            webJobsBuilder.Services.AddTransient<IAccountWorkflow, AccountWorkflow>();
        }
   }

The Azure Function calls other injected services that do depends on the ILogger as well, such as the IAccountWorkflow:

public sealed class AccountWorkflow : IAccountWorkflow
{  
    public AccountWorkflow(ILogger logger, IStorageService storageService)
    {
        if(logger is null)
            throw new ArgumentNullException();
    }
}

The problem is that the DI is unable to find any ILogger implementation and fails to resolve services since a null ILogger is injected.

Question

How can I setup the injection of the ILogger in IWebJobsStartup?

Kzryzstof
  • 7,688
  • 10
  • 61
  • 108
  • I thought functions were suppose to be static? – Nkosi Feb 26 '19 at 01:02
  • 3
    Oh man :) . I just discovered this today and it made my day. Now, we can remove the static keyword and do some sweet ctor injection. I just need to inject the freaking logger for it to be perfect :) – Kzryzstof Feb 26 '19 at 01:12
  • Oh, this is new? are there any docs I can research this? – Nkosi Feb 26 '19 at 01:14
  • 2
    @Nkosi this is part of the work for using DI in azure function. You can look at this issue : [Dependency Injection support for Functions](https://github.com/Azure/azure-functions-host/issues/3736) . See the `Port changes to enable instance methods` – NicoD Feb 26 '19 at 11:10
  • 2
    @Nkosi Hence instance methods are supported since v2.0.12265 of the runtime : [Azure Functions Runtime 2.0.12265](https://github.com/Azure/azure-functions-host/releases/tag/v2.0.12265) – NicoD Feb 26 '19 at 11:13

5 Answers5

29

UPDATE

Reference Use dependency injection in .NET Azure Functions

Registering services

To register services, you can create a configure method and add components to an IFunctionsHostBuilder instance. The Azure Functions host creates an IFunctionsHostBuilder and passes it directly into your configured method.

To register your configure method, you must add an assembly attribute that specifies the type for your configure method using the FunctionsStartup attribute.

So in this case

[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]    
namespace MyNamespace {
    public class Startup : FunctionsStartup {
        public override void Configure(IFunctionsHostBuilder builder) {
            //  ** Registers the ILogger instance **
            builder.Services.AddLogging();

            //  Registers the application settings' class.
            //...

            //...omitted for brevity    
        }
    }
}

ORIGINAL

I believe since you have access to the service collection, you should be able to add logging to it

public void Configure(IWebJobsBuilder webJobsBuilder) {       

    //  ** Registers the ILogger instance **
    webJobsBuilder.Services.AddLogging();

    //OR
    //webJobsBuilder.Services.AddLogging(builder => {
    //    //...
    //});

    //  Registers the application settings' class.
    //...

    //...removed for brevity
}

and having anILoggerFactory in the Function's constructor.

//...

//Ctor
public FindAccountFunction(ILoggerFactory loggerFactory, IMapper mapper, IAccountWorkflow accountWorkflow) {
    m_logger = loggerFactory.CreateLogger<FindAccountFunction>();
    m_mapper = mapper;
    m_accountWorkflow = accountWorkflow;
}

//...
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 3
    I think this is somewhat outdated now, should use [FunctionsStartup](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#registering-services) instead. – Kapé May 13 '19 at 16:02
  • 2
    @Kapé that is correct. This would have been done before that version was released. Thank you for the reference link. I have updated answer to reflect recent version. – Nkosi May 13 '19 at 17:05
  • 8
    Thanks for updating, but I think `builder.Services.AddLogging();`is not required anymore as well? Seems to be set in the `Services` collection already. – Kapé May 13 '19 at 21:25
  • Using the updated approach of leveraging `FunctionsStartup` is not working for me. I tried with and without `builder.Services.AddLogging`. – user3613932 Nov 08 '19 at 01:34
  • 5
    Using `ILogger` worked just fine. – user3613932 Nov 08 '19 at 19:21
  • 2
    @user3613932 yes the newer versions have logging added by default so using `ILogger` should would out of the box. – Nkosi Nov 08 '19 at 23:38
  • 1
    If you don't see the logs in Azure Functions console, add to `host.json` the following code `"logging": { "logLevel": { "default": "Information" } }` – Artemious Jun 23 '22 at 18:51
26

I managed to resolve this problem:

Injecting into my class as below:

MyClass.cs:

public class MyClass
{
    private readonly ILogger<MyClass> _logger;

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

Startup.cs:

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

namespace Namespace {    
public class Startup : FunctionsStartup 
{
    public override void Configure(IFunctionsHostBuilder builder) 
    {
        builder.Services.AddLogging(); 
    }
  }
}
jhoops
  • 265
  • 3
  • 5
  • 16
    It worked for me. However, it's important to inject it like ```ILogger _logger``` instead of ```ILogger _logger``` – kord Feb 17 '20 at 18:31
15

You should remove the call of AddLogging method from your startup class. The default logger is already setup by the azure function host.

[assembly: WebJobsStartup(typeof(StartUp))]
public class StartUp : IWebJobsStartup
{
    public void Configure(IWebJobsBuilder builder)
    {
        builder.Services.AddSingleton<AppSettings>();

        builder.Services.AddTransient<IMyService, MyService>();
    }
}

public MyFunction(IMyService service, ILogger<IMyService> logger)
{
    this.service = service;
    this.logger = logger;
}

Instance methods are now supported with azure function since Azure Functions Runtime 2.0.12265

enter image description here

NicoD
  • 1,179
  • 8
  • 19
  • Hum... Still the same error. Do I have to use the generic version of ILogger? – Kzryzstof Feb 26 '19 at 12:52
  • Yep for me it works only with the generic version yesterday :) – NicoD Feb 26 '19 at 12:55
  • 1
    I think only ILogger<> is registered with `services.AddLogging()`. See [LoggingServiceCollectionExtensions](https://github.com/aspnet/Logging/blob/master/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs) – NicoD Feb 26 '19 at 13:01
  • Looks like it does work. I will do more tests after work but it looks like you got it :) – Kzryzstof Feb 26 '19 at 14:19
  • 5
    I do not have any errors but I am not seeing the logs where I used to (In the Azure Function monitor sections or in App Insights)... Need more investigation to make sure it does work – Kzryzstof Feb 27 '19 at 14:06
  • I will mark this as an accepted answer because it does solve the crashing issue. I still do not see the logs in Application Insights like I used to but I will ask a question specifically for this new problem. Thanks! – Kzryzstof Mar 01 '19 at 13:18
  • 5
    @Kzrystof add to `host.json` the following code ```"logging": { "logLevel": { "default": "Information" } }``` – Monsignor Nov 08 '19 at 15:27
9

In addition to registering the class in function Startup class, we would need to add the namespace in host.json file as well in order to log the messages to App insights. If we don't add it, it will simply log the message to console locally but when deployed on azure, it won't log anything and won't give any error either.

{
  "version": "2.0",
  "logging": {
    "logLevel": {
      "FunctionProjectNameSpace.RegisteredClass": "Information"
    }
  }
}

Sample code here for reference - https://gist.github.com/nareshnagpal06/82c6b4df2a987087425c32adb58312c2

Vivek Jain
  • 2,730
  • 6
  • 12
  • 27
Naresh Nagpal
  • 299
  • 4
  • 6
7

You can add

  "logging": {
    "fileLoggingMode": "debugOnly",
    "logLevel": {
      "default": "Information"
    }
  }

without the need to add DI if you want to use IloggerFactory

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
almbrmg
  • 152
  • 1
  • 9
  • 1
    I've tested all approaches listed here and this works with the least effort - just make sure you DI `ILogger` into your function's ctor. – White hawk Mar 26 '21 at 09:54