Search code examples
c#.netazureazure-webjobsserilog

How do I use Serilog with Azure WebJobs?


I'm trying to setup Azure WebJobs setup with Serilog. Here's my current 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.).

I've seen this answer, but Serilog.Extensions.WebJobs is no longer maintained.

I've attempted variations of this hosting boilerplate setup but it either results in the same exception or no consumption of the queue message.

I've also attempted to review the SDK dependencies, but I'm unaware of where I can find a definitive list of the SDK dependencies to unpick any potential versioning mismatch.

The below works as expected when ProcessQueueMessage uses Microsoft.Extensions.Logging.ILogger but not with Serilog.ILogger:

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
    }
}

Project package references:

<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.4" /> (Also played with prerelease)
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />


Solution

  • Inject ILogger as a function parameter, works like a charm.

    The author of Serilog.Extensions.WebJobs provided a suitable template, which I've condensed below:

    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)
                .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);
    }
    

    Package References:

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

    Credit: @James Skimming