H is for Harre.

harre.dev

Tags: Cloud Foundry Asp.net Core Dotnet Core Logging Itq

ASP.NET Core logging on Cloud Foundry

All apps I’ve ever worked on have some sort of logging going on for various reasons, mostly to keep track of whats going on or debugging, but it’s all the same. Logs are needed to see what your code is up to.

Cloud Foundry is very explicit about how an app should write its logs. As you can see in the documentation, apps must write to stdout or stderr.

For C# that means we can use the static methods on the Console to write our logs. Couldn’t be easier. But… now our code is littered with these Console.Write... lines all over the place!

Ok, easy, wrap it up in a simple class with an interface and inject it. That’s a better solution for sure, but .NET provides an easy to use, injectable ILogger we can take advantage of. Let’s see how we can set it up for console logging and use it in our apps we want to run on Cloud Foundry.

The ILogger mentioned earlier can be configured in the same way the configuration is handled. In the Main method, were the host is built, we can call the .ConfigureLogging(...) method and get access to logging bits.

public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging((logging) => {
            -- [access logging here! --
        })
        .UseStartup<Startup>();

Because we only want to log to the console, we start with removing any default (Console, Debug and EventSource) that might be present:

logging.ClearProviders();

And we add the console provider:

logging.AddConsole();

This setup will make the ILogger instance log only to the console. Using ILogger is just like using a regular service. It’s already present in the services collection and can be injected without further configuration. Here’s an example with the ValuesController from an untouched .NET Core WebAPI project:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    private readonly ILogger<ValuesController> _logger;

    public ValuesController(ILogger<ValuesController> logger)
    {
        _logger = logger;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        _logger.LogTrace("--- trace");
        _logger.LogDebug("--- debug");
        _logger.LogInformation("--- info");
        _logger.LogWarning("--- warning");
        _logger.LogError("--- error");
        _logger.LogCritical("--- critical");

        return new string[] { "value1", "value2" };
    }

    ...
}

Running this locally and calling the /api/values endpoint will show the following (and a whole lot more) output:

trce: app.Controllers.ValuesController[0]
    --- trace
dbug: app.Controllers.ValuesController[0]
    --- debug
info: app.Controllers.ValuesController[0]
    --- info
warn: app.Controllers.ValuesController[0]
    --- warning
fail: app.Controllers.ValuesController[0]
    --- error
crit: app.Controllers.ValuesController[0]
    --- critical

Keep in mind that to see ALL the output, you need to tell the logger to do so in appsettings.*.json by setting the desired log level! (You might want to keep it at Information, or even Warning)

{
    "Logging": {
        "LogLevel": {
            "Default": "Trace"
        }
    }
}

The built-in logger uses a type (in this case ValuesController) as the category as seen in the output. For more options and features you can check to full documentation to for logging in aspnet core here.

When you cf push this to Cloud Foundry and call the same endpoint you’ll see something like this:

2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT trce: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- trace
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT dbug: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- debug
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT info: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- info
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT warn: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- warning
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT fail: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- error
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT crit: app.Controllers.ValuesController[0]
2019-01-10T11:43:19.35+0100 [APP/PROC/WEB/0] OUT       --- critical

So now you are all set up with proper logging in ASP.NET Core (2.2) that complies with the requirements set by the Cloud Foundry platform.