DataDog / serilog-sinks-datadog-logs

Serilog Sink that sends log events to Datadog https://www.datadoghq.com/
Apache License 2.0
60 stars 41 forks source link

Add support for custom `ITextFormatter` #86

Closed gh123man closed 1 year ago

gh123man commented 1 year ago

This change allows users to inject a custom ITextFormatter implementation to change the output format of the logs before they are sent to Datadog.

Background

In order to correctly support this - our existing JSON serialization needed to be refactored. Datadog has a set of reserved fields for logs. Previously these were injected alongside the user attributes in the final JSON payload. However this prevented us from using a custom text formatter as we would still need to inject these reserved fields which would conflict with the custom format.

To fix this - we need to change how logs are packaged. Datadog will treat any JSON in the message fields as JSON in the app and parse it appropriately. So the new flow is:

  1. Receive log event
  2. Apply the ITextFormatter to the log message
  3. Encode the message in the message field of another JSON payload (+ the source/service/host/tags)
  4. Send to backend

By doing this - users can format their logs any way they want (even non-json) while still preserving the source/service/host/tags on the log message. (This is how the datadog-agent works)

Challenges

As part of this change we need to have our own default JSON formatter (an ITextFormatter implementation). Ideally we would use serilog-expressions (the new recommended way to format logs) - however it does not support some old .net versions that we (and Serilog core) does.

The second option was to subclass JsonFormatter (the existing formatter we were using) - however this is deprecated.

As a result we have to use the recommended approach - a custom JsonValueFormatter based serializer that gives us the best balance of backwards compatibility and simplicity.

Additional changes

Future work

We should consider truncated logs like the Datadog agent does instead of dropping them and throwing an exception. Previously this was difficult because truncating the payload would damage the JSON encoding and lose the source/service/host/tags context (making them impossible to find). But now that logs are wrapped in a parent JSON structure - we can truncate logs without losing context.

Tests performed