.net-coreamazon-cloudwatchserilogstructured-logging

How to show log message with substituted parameters in structured logging output from Serilog on AWS Cloudwatch


I'm using AWS Cloudwatch for my application logging. I recently learned that I could use structured logging to make my logs more searchable. I did a little research and decided to go with Serilog for my logging tool.

One difficulty I'm having is, although I'm seeing the structured logging message in Cloudwatch, it's not showing a fully formatted log message.

For example, when I send this to the logs:

_logger.LogError("Hello {person}, {question} are you {time}?", "Dave", "how", "today");

I see this message in the Cloudwatch logs output

Hello {person}, {question} are you {time}?

But I don't see the fully substituted version of the string anywhere i.e.

Hello Dave how are you today?

I followed this walkthrough on setting up Serilog for structured logging in AWS https://martinjt.me/2020/02/16/serilog-and-cloudwatch-with-inbuilt-credentials/ and ended up with this code.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>()
        .UseSerilog(SetupAWSLogging);
    });

public class LoggingSettings
{
    public string LogGroup { get; set; }
}

public static void SetupAWSLogging(WebHostBuilderContext hostingContext, LoggerConfiguration loggerConfiguration)
{
    var config = hostingContext.Configuration;
    var settings = config.GetSection("Logging").Get<LoggingSettings>();

    loggerConfiguration
        .MinimumLevel.Information()
        .Enrich.FromLogContext()
        .WriteTo.Console();

    if (!string.IsNullOrEmpty(settings.LogGroup))
    {
        var options = new CloudWatchSinkOptions
        {
            LogGroupName = settings.LogGroup,
            CreateLogGroup = true,
            MinimumLogEventLevel = LogEventLevel.Information,
            TextFormatter = new JsonFormatter()
        };
        var awsOptions = config.GetAWSOptions();
        var cloudwatchClient = awsOptions.CreateServiceClient<IAmazonCloudWatchLogs>();
        loggerConfiguration
            .WriteTo.AmazonCloudWatch(options, cloudwatchClient);
    }

}

Here's the full entry found in Cloudwatch

{
    "Timestamp": "2021-07-05T09:08:32.5118371-06:00",
    "Level": "Error",
    "MessageTemplate": "Hello {person}, {question} are you {time}?",
    "Properties": {
        "person": "Dave",
        "question": "how",
        "time": "today",
        "SourceContext": "Testing",
        "ActionId": "54758041-ee5f-4297-a7b3-adaa5e675901",
        "ActionName": "Testing.Ping (TestController)",
        "RequestId": "80000007-0005-ff00-b63f-84710c7967bb",
        "RequestPath": "/Testing/Ping"
    }
}

All the information we need to construct the message ourselves is there, but the log is not as readable as if I could see the substituted string as well. Is there something I'm missing or doing wrong? Maybe it's just a configuration in Cloudwatch to show the string with substitutions? It would be better if it were done there I guess to cut down on the log sizes.

Appreciate the help all.


Solution

  • Simplest fix is:

                TextFormatter = new JsonFormatter(renderMessage: true)
    

    Note true to render the message.