Skip to content

Custom exception formatting

Mykhailo Shevchuk edited this page Jun 6, 2026 · 2 revisions

Exceptions attached to a log event are serialized into the JSON body by an ILokiExceptionFormatter. The default writes a recursive object; replace it to change the shape, scrub sensitive data, or drop stack traces.

The interface

public interface ILokiExceptionFormatter
{
    void Format(Utf8JsonWriter writer, Exception exception);
}

The sink writes the Exception property name for you; your implementation writes the value (object, string, etc.) using the supplied Utf8JsonWriter.

Default - LokiExceptionFormatter

Recursively writes Type, Message, Source, StackTrace, and inner exceptions (InnerException, or an InnerExceptions array for AggregateException). Recursion is depth-limited (20) to guard against cyclic inner-exception chains.

Providing your own

using System.Text.Json;
using Serilog.Sinks.Grafana.Loki;

public class CompactExceptionFormatter : ILokiExceptionFormatter
{
    public void Format(Utf8JsonWriter writer, Exception exception)
    {
        writer.WriteStartObject();
        writer.WriteString("type", exception.GetType().Name);
        writer.WriteString("message", exception.Message);
        writer.WriteEndObject();
    }
}
.WriteTo.GrafanaLoki(
    "http://localhost:3100",
    exceptionFormatter: new CompactExceptionFormatter())

From appsettings.json, reference a type that has a public parameterless constructor:

{
  "Name": "GrafanaLoki",
  "Args": {
    "uri": "http://localhost:3100",
    "exceptionFormatter": "My.Namespace.CompactExceptionFormatter, My.Assembly"
  }
}

Tips: to suppress stack traces in production, write only type and message. To redact PII, sanitize exception.Message before writing it.

Clone this wiki locally