The latest Serilog version 1.3.7 fixes some long-standing text formatting quirks.

Serilog has a bit of an idiosyncratic take on formatting text. You could probably say the same about its designer: before Serilog, I used to write a lot of log statements like this:

Log.Information("Could not find documents matching '{0}'", searchTerm);

If you look carefully you’ll note an extra couple of single quotes (') in there around the the {0} token that will be replaced with the search term being logged. I got into this habit because when reading logs I find it less jarring to see random string data quoted, so that my brain doesn’t try to join the whole lot up in a single bewildering sentence:

Could not find any documents matching my pants

vs.:

Could not find any documents matching 'my pants'

String formatting in Serilog message templates

When I started writing Serilog I thought I’d save a few characters, and encourage consistency, by making this little idiom part of its string formatting engine:

Log.Information("Could not find documents matching {Term}", searchTerm);

yields:

Could not find any documents matching "my pants"

I’m sure there are plenty of people who’d rather a different default, but so far no one’s raised it as particularly problematic. If you need to match a specific output format you can use a special “formatting” specifier to turn this off - just append :l (yes, a lowercase letter L) to the token:

Log.Information("Could not find documents matching {Term:l}", searchTerm);

yields:

Could not find any documents matching my pants

Reusing message templates for output

The formatting above is applied when rendering log messages into text. A bit later in the design process, I think some time around when Serilog became a public project, we needed another formatting mechanism, this time to control how the different parts of the log event including the message get laid out e.g. for display at the console.

Log.Logger = new LoggerConfiguration()
  .WriteTo.Console("{Timestamp:G} [{Level}] {Message}{NewLine:l}{Exception:l}")
  .CreateLogger();

You’ll notice the same style of format string appears in there. The engine used to write the text is the same; there were (and are) a lot of advantages to reusing this code for “output templates” like this.

Unfortunately, some of the decisions that work well when rendering a structured message, get in the way when presenting text like this. You’ll see above one of the oddities - the pre-defined NewLine and Exception properties are represented as strings under the hood, so the :l literal specifier has to appear in there to avoid unsightly quotation marks appearing in the output. Not a big deal really, but too much to have to grasp as a new user.

The other issue is that in message templates, i.e. when calling Log.Information() and friends, it’s an error to provide less arguments than the number of tokens that appear in the string. Serilog helpfully prints out the whole token as text - {Token} so that these issues are obvious when reading the rendered message.

In output templates this isn’t so desirable. Using {HttpRequestId} in the format string should just be ignored if the event doesn’t have such a property attached.

Serilog 1.3.7 changes

I almost versioned this one “1.4”, given there’s quite a change being made, but since the API is binary-identical this one’s out as a patch.

Put simply, Serilog now has slightly different handling of message templates vs. output templates. Message templates keep their current behaviour - quotation marks and all. When used to configure a sink as an output template however:

  • The :l specifier is now “on by default”

  • Missing tokens will simply be ignored

The default output templates all used :l formatting anyway, and so will almost all custom ones, so for 99% of current users I’d say this change will go unnoticed. If you define your own output templates though you can now clean up a bit of clutter and remove the literal specifier from fields like {Exception} and {NewLine}.

If you’re coming to Serilog as a new user, hopefully these changes give you one less reason to go looking for explanatory posts like this one!