1

I'm trying to get instance of ILogger<T> when resolving a dependency in the Startup class of an Azure Function v4 in process version.

Only this doesn't work and will result in an exception, null or a not working logger.

I followed the following AutoFac documentation for Azure Functions.

I register some types and try to resolve a type in the Startup class. The problem is the ILogger<T>. In this case the Logger<EventBusServiceBus>.

In the sample I try to resolve IEventBus in the ConfigureEventBus method. Because of this, services.AddSingleton<IEventBus, EventBusServiceBus>(sp => ....); is trying to be resolved in the AddEventBus method.

I don't understand why it can't be resolved because I see some registered types in the Autofac container.

See the comments in the code.

Thanks!

Simplified Startup from the docs:

internal class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Use IServiceCollection.Add extension method to add features as needed, e.g.
        //builder.Services.AddDataProtection();

        builder.Services.AddSingleton(GetContainer(builder.Services));

        // Important: Use AddScoped so our Autofac lifetime scope gets disposed
        // when the function finishes executing
        builder.Services.AddScoped<LifetimeScopeWrapper>();

        builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivator), typeof(AutofacJobActivator)));
        builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivatorEx), typeof(AutofacJobActivator)));

        builder.Services.AddCustomIntegrations(configuration);
        builder.Services.AddEventBus(configuration);

        var serviceProvider = builder.Services.BuildServiceProvider();
        ConfigureEventBus(serviceProvider);
    }

    private static IContainer GetContainer(IServiceCollection serviceCollection)
    {
        var containerBuilder = new ContainerBuilder();
        containerBuilder.Populate(serviceCollection);
        containerBuilder.RegisterModule<LoggerModule>();

        // This is a convenient way to register all your function classes at once
        containerBuilder.RegisterAssemblyTypes(typeof(Startup).Assembly)
            .InNamespaceOf<Function1>();

        // TODO: Register other dependencies with the ContainerBuilder like normal

        return containerBuilder.Build();
    }

    private void ConfigureEventBus(ServiceProvider sp)
    {
        var eventBus = sp.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>();

        // Unable to use eventBus here
    }
}

Some extension methods:

static class CustomExtensionsMethods
{
    public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSingleton<IServiceBusPersisterConnection>(sp =>
        {
            var serviceBusConnectionString = configuration["EventBusConnection"];

            var subscriptionClientName = configuration["SubscriptionClientName"];

            return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
        });

        return services;
    }

    public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
        {
            var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();

            // Get autofac container so we rolve dependencies from there
            var autoFacContainer = sp.GetRequiredService<IContainer>();


            // This doesn't work
            ////var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();

            // This does work
            var iLifetimeScope = autoFacContainer.Resolve<ILifetimeScope>();


            // This doesn't work
            //var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();


            // This doesn't work either but at least now no error/exception. Resolves into empty logger.
            var loggerFactory = new LoggerFactory();
            var logger = loggerFactory.CreateLogger<EventBusServiceBus>();


            // Tried these for logging without luck.
            // Don't understand because I see a loggerfactory and Ilogger in the container...

            //var loggerFactory = autoFacContainer.ResolveNamed<ILoggerFactory>("loggerFactory");
            //var loggerFactory = autoFacContainer.Resolve<ILoggerFactory>();
            ////var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
            ////var logger = loggerFactory.CreateLogger<EventBusServiceBus>();

            ////autoFacContainer.Resolve<ILogger<EventBusServiceBus>>();

            var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
            string subscriptionName = configuration["SubscriptionClientName"];

            return new EventBusServiceBus(serviceBusPersisterConnection, logger,
                eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
        });

        services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();

        return services;
    }
}

LockTar
  • 5,364
  • 3
  • 46
  • 72

1 Answers1

0

So I did found a working solution for now. Maybe someone has a better solution but this works for me for now...

public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
    services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
    {
        var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();

        // Get autofac container so we resolve dependencies from there
        var autoFacContainer = sp.GetRequiredService<IContainer>();

        // This does work
        var iLifetimeScope = autoFacContainer.Resolve<ILifetimeScope>();

        // Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
        var loggerFactory = LoggerFactory.Create(builder =>
        {
            var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);

            builder.AddConsole();
            builder.SetMinimumLevel(logLevel);
        });
        var logger = loggerFactory.CreateLogger<EventBusServiceBus>();

        var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
        string subscriptionName = configuration["SubscriptionClientName"];

        return new EventBusServiceBus(serviceBusPersisterConnection, logger,
            eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
    });

    return services;
}

So the important part is this:

// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
    var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);

    builder.AddConsole();
    builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
LockTar
  • 5,364
  • 3
  • 46
  • 72