Marco
Marco

Reputation: 2473

Re-use scoped ILogger instance from Azure Function

I'm not sure what is the best way to achieve what I am trying to accomplish so let me give you an example.

I am using Azure Functions, which are stateless, with the following signature.

public static Task Run(Message message, ILogger logger)
{
    var controller = Main.Container.GetInstance<ConsumerController>();

    // How can I attach the passed in logger instance so the rest of
    // the services for the current flow re-use this instance?

    return controller.Execute(message);
}

As you can see, the azure function framework passes me an instance of the ILogger already configured and initialized for this function call only.

I read through the documentation and I think I need a new scope here but I'm not sure. I only want this ILogger instance to be used during the async execution of this one method call. Each function call will use their own.

And just to be clear, the controller is only one of possibly many services (services, repositories, request handlers) involved in the execution of the task.

Any help would be great?

Upvotes: 1

Views: 905

Answers (1)

Steven
Steven

Reputation: 172646

You can do the following:

  • Create a Proxy (e.g. ProxyLogger) implementation that implements ILogger, contains a ILogger Logger property, and forwards any call to that property.
  • Register that Proxy both as ILogger and ProxyLogger as Lifestyle.Scoped.
  • Resolve ProxyLogger within your function.
  • Set ProxyLogger.Logger using the function's supplied ILogger.
  • Resolve the root object and use it.

Create a Proxy:

public class ProxyLogger : ILogger
{
    public ILogger Logger { get; set; }

    public void Log<TState>(LogLevel l, EventId id, TState s, Exception ex,
       Func<TState,Exception,String> f) =>
       this.Logger.Log<TState>(l, id, s, ex, f);
    // Implement other functions
}

Register that Proxy:

container.Register<ProxyLogger>(Lifestyle.Scoped);
container.Register<ILogger, ProxyLogger>(Lifestyle.Scoped);

Resolve ProxyLogger within your function, set ProxyLogger.Logger using the function's supplied ILogger, and resolve the root object and use it.

public static Task Run(Message message, ILogger logger)
{
    using (AsyncScopedLifestyle.BeginScope(Main.Container)
    {
        Main.Container.GetInstance<ProxyLogger>().Logger = logger;

        var controller = Main.Container.GetInstance<ConsumerController>();

        return controller.Execute(message);
    }
}

I do think, however, that this model leads to a very large amount of infrastructural code. Preferably you wish to keep this infrastructure to the absolute minimum. Instead, you could try keeping your Azure Functions small Humble Objects, as described here. That might not completely solve your initial problem, but you might not need to have a method specific logger anyway. But if, on the other hand, you need that, you might be able to mix that Humble Object approach with the use of C#'s CallerMemberName attribute and the ProxyLogger approach. You don't really need the Azure injected ILogger to do that for you.

Upvotes: 1

Related Questions