serilog / serilog-extensions-hosting

Serilog logging for Microsoft.Extensions.Hosting
Apache License 2.0
139 stars 30 forks source link

System.InvalidOperationException: Cannot bind parameter 'logger' to type ILogger. #46

Closed remy90 closed 3 years ago

remy90 commented 3 years ago

I'm attempting to add Serilog to an Azure WebJobs sdk project and getting a parameter binding exception:

Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException: Error indexing method 'Functions.ProcessQueueMessage' ---> System.InvalidOperationException: Cannot bind parameter 'logger' to type ILogger. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Here's my repro example:

class Program
{
    static async Task Main()
    {
        var builder = new HostBuilder();
        builder.UseSerilog();
        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddAzureStorage();
        });
        var host = builder.Build();
        try { await host.RunAsync(); }
        catch (Exception ex) { Log.Logger.Fatal(ex.Message, ex); }
    }
}

public class Functions
{
    public static void ProcessQueueMessage([QueueTrigger("queue")] string message, ILogger logger)
    {
        logger.Information(message); // Serilog ILogger
    }
}

For completeness, these are the package references: \ \ \ \

JSkimming commented 3 years ago

@remy90 Try this. Don't bother with parameter bindings, inject the logger on the function constructor. I've added a timer to show it working immediately.

class Program
{
    static async Task Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.Console()
            .CreateLogger();

        IHostBuilder builder = Host.CreateDefaultBuilder(args)
            .UseSerilog(Log.Logger)
            .ConfigureAppConfiguration(config =>
            {
                config.AddInMemoryCollection(new Dictionary<string, string>
                {
                    ["AzureWebJobsStorage"] = "UseDevelopmentStorage=true",
                });
            })
            .ConfigureServices(s =>
            {
                s.AddSingleton(Log.Logger);
            })
            .ConfigureWebJobs(b =>
            {
                b.AddAzureStorageCoreServices();
                b.AddAzureStorage();
                b.AddTimers();
            });

        IHost host = builder.Build();
        try { await host.RunAsync(); }
        catch (Exception ex) { Log.Fatal(ex.Message, ex); }
        finally { Log.CloseAndFlush(); }
    }
}

public class Functions
{
    readonly ILogger _logger;

    public Functions(ILogger logger) => _logger = logger.ForContext<Functions>();

    public void ProcessQueueMessage([QueueTrigger("queue")] string message)
    {
        _logger.Information(message); // Serilog ILogger
    }

    public void ProcessTimer([TimerTrigger("0 * * * * *", RunOnStartup = true)] TimerInfo myTimer)
    {
        _logger.Information(
            "C# Timer trigger function executed at: {Now:R}! IsPastDue={IsPastDue}.",
            DateTime.UtcNow,
            myTimer.IsPastDue);
    }
}

I use these packages

<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.27" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
remy90 commented 3 years ago

Exactly what I was looking for, thanks!