DEV Community

Cover image for Fixing console logs for Azure Functions running in a Docker container
Anton Sizikov
Anton Sizikov

Posted on

1 2

Fixing console logs for Azure Functions running in a Docker container

Local development of C# Azure Functions on macOS is still a bit painful.
Even the simple-ish logging might cause issues. Let's assume that we have Azure Functions Core Tools installed and we have a basic function app with one TimerTrigger function created.

Azure Functions app in Rider

With the default Run/Debug configuration

Run configuration

We can compile and run our function

Azure Functions Log

Our function will start and log to console as expected.

I wish all the functions were that simple, right? Unfortunately, sometimes we need more, sometimes we even have to use some shared libraries. So let's create one:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace DockerizedFunc.Dependency
{
public interface IAsyncGreeter
{
Task SayHiAsync();
}
public class AsyncGreeter : IAsyncGreeter
{
private readonly ILogger<AsyncGreeter> _logger;
public AsyncGreeter(ILogger<AsyncGreeter> logger)
{
_logger = logger;
}
public Task SayHiAsync()
{
_logger.LogInformation("Hi from async greeter");
return Task.CompletedTask;
}
}
}
view raw greeter.cs hosted with ❤ by GitHub

Nothing fancy there, one class, one interface accepting the ILogger<T> as a dependency.

Let's update function code accordingly: will make is non-static, and use construction injection to get the IAsyncGreeter injected:

using System;
using System.Threading.Tasks;
using DockerizedFunc.Dependency;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
namespace DockerizedFunc.FunctionApp
{
public class ConsoleWriter
{
private readonly IAsyncGreeter _asyncGreeter;
public ConsoleWriter(IAsyncGreeter asyncGreeter)
{
_asyncGreeter = asyncGreeter;
}
[FunctionName("ConsoleWriter")]
public async Task RunAsync(
[TimerTrigger("20 * * * * *")] TimerInfo myTimer,
ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.UtcNow}");
await _asyncGreeter.SayHiAsync();
}
}
}
view raw function.cs hosted with ❤ by GitHub

In order to register this external dependency and use a propper .NET Core-style DI we need to install Microsoft.Azure.Functions.Extensions NuGet package and create a Startup class like this one:

using DockerizedFunc.FunctionApp;
using DockerizedFunc.Dependency;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(Startup))]
namespace DockerizedFunc.FunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IAsyncGreeter, AsyncGreeter>();
}
}
}
view raw bad-di.cs hosted with ❤ by GitHub

You would expect it to work, right?

I wish...

Just the main app is logging

As you can see it still just logs from the function.

Why would one logger print it's output to console while another one just flushes our logs to void?

Because they are two different loggers with different settings. One logger is instantiated and configured in our Startup.cs class and another one is built and configured by function host and injected into the method instead. Oh, well.

I want to run this function in docker, so let's create a Dockerfile.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
WORKDIR /src
COPY . /src
RUN mkdir -p /home/site/wwwroot && \
dotnet publish DockerizedFunc.FunctionApp/*.csproj --output /home/site/wwwroot
# To enable ssh & remote debugging on app service change the base image to the one below
FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice
#FROM mcr.microsoft.com/azure-functions/dotnet:3.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
view raw Dockerfile hosted with ❤ by GitHub

Note that I'm setting AzureFunctionsJobHost__Logging__Console__IsEnabled environment variable here.

Just the main app is logging

The missing link here is the hosts.json logging configuration.

    "logLevel": {
        "default": "Information"
    }
Enter fullscreen mode Exit fullscreen mode
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
},
"logLevel": {
"default": "Information"
}
}
}
view raw hosts.json hosted with ❤ by GitHub

And voilà, we've got logs in our container:

Logs in a docker container

I hope that would save you some time. Happy logging!

This is a cross post from my personal blog.

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (1)

Collapse
 
cogax profile image
Andreas Gyr

Thanks a lot! Another silly azure functions settings..

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay