Serilog is built around the idea of structured event data, so complex structures are given first-class support.

var identity = new { Name = "Alice", Email = "[email protected]" };
Log.Information("Started a new session for {@Identity}", identity);

If you’ve spent some time with Serilog you will have encountered the @ ‘destructuring’ operator. This tells Serilog that instead of calling ToString() on identity, the properties of the object should be serialized and stored in structured form.

{"Identity": {"Name": "Alice", "Email": "[email protected]"}}

You might also have wondered – since Serilog is built around structured data, why isn’t serialization the default – or, why is @ required at all?

If you consider the process of serialization in general, this starts to make sense. Have you ever tried to convert an entity from EntityFramework to JSON with a general-purpose serializer? A System.Type? Give it a try! If you’re lucky enough to get a result, you’ll probably find it’s a very big blob of text indeed. Some serializers will bail out when circular references are detected, others will chew up RAM indefinitely. Most objects aren’t designed for serilalization, so they’re interconnected with many others that themselves link to yet more objects, and so-on.

Serializers – Serilog included – are not made for serializing arbitrarily-connected object graphs. Serilog has some safeguards to make sure your application survives a “mass serialization” event, but the effects on the health of your logging pipeline won’t be pretty.

In a logging library there’s a delicate balance to make between safety and runtime cost, so the best that most Serilog sinks can do is drop or reject events that are too large before trying to ship them elsewhere (the v2 version of the Seq sink now defaults to a 256 KB cap on event bodies).

What’s the TL;DR? When you serialize data as part of a Serilog event, make sure you know exactly what will be included. The @ operator is a powerful but sharp tool that needs to be used with precision.