Skip to content

Using a JSON formatter

Warren Parad edited this page Jul 3, 2018 · 3 revisions

This library does not send JSON to CloudWatch by default. That is a separate concern of the library and also closely coupled with the usage of the library in an application. Such can be seen from asking the question:

  • Assuming the payload sent to CloudWatch must be wrapped, what would you like the wrapper to be?

The reason for not providing a specific JSON, XML or other custom renderer is that this library is about forwarding logs to cloud watch, and additional dependencies should be avoided for simplicity, single responsibility but also library size perspective.

That said, it's very easy to inject a custom ITextFormatter. One example is a JSON text formatter. To provide a specific example, below some sample code.

In Startup.cs, configure the renderer like this:

var options = new CloudWatchSinkOptions
{
    LogGroupName = logGroupName,
    LogEventRenderer = new JsonTextFormatter()
};

AmazonCloudWatchLogs client = new AmazonCloudWatchLogsClient(settings.GetAwsCredentials(), settings.GetRegionEndpoint());
var loggerConfig = new LoggerConfiguration()
    .MinimumLevel.Information()
    .Destructure.AsScalar<JObject>()
    .Destructure.AsScalar<JArray>()
    .Enrich.FromLogContext()
    //.Enrich.WithWhateverOtherData()
    .WriteTo.AmazonCloudWatch(options, client);

Create a custom rendering class, for example JsonTextFormmatter.cs, and base it on the following idea:

public class JsonTextFormatter : ITextFormatter
{
    // the cached host name which is part of every log message
    private readonly string hostname;

    public JsonTextFormatter()
    {
        hostname = Dns.GetHostName();
    }

    public void Format(LogEvent logEvent, TextWriter output)
    {
        try            
        {
            // flatten the message as a simpler option to look at it within the RenderedMessage property of the JSON file
            var renderedMessage = logEvent.RenderMessage();

            // create the message object
            var message = new {logEvent.MessageTemplate, logEvent.Properties, RenderedMessage = renderedMessage, Level = logEvent.Level.ToString(), Exception = logEvent.Exception?.ToString(), Hostname = hostname};

            // serialize the object as JSON
            output.Write(JsonConvert.SerializeObject(message));
        }
        catch (Exception exception)
        {
            try
            {
                var message = new {RenderedMessage = "Failed to render log message.", MessageTemplate = logEvent.MessageTemplate.Text, Exception = exception.ToString()};
                output.Write(JsonConvert.SerializeObject(message));
            }
            catch (Exception ex)
            {
                // fallback to just return the fact that we were unable to render the log message
                output.Write($"Unable to render log message. Reason was {ex}");
            }
        }
    }
}

Based on above example, it should be straight forward to create additional custom renderers, be it JSON, XML or any other custom format.

Clone this wiki locally