Skip to content

Commit f0d8565

Browse files
committed
Documentation for conditionals and repetition
1 parent 8e6e31e commit f0d8565

File tree

1 file changed

+66
-5
lines changed

1 file changed

+66
-5
lines changed

README.md

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ _Serilog.Expressions_ adds a number of expression-based overloads and helper met
7979
* `Enrich.When()` - conditionally enable an enricher when events match an expression
8080
* `Enrich.WithComputed()` - add or modify event properties using an expression
8181

82-
## Formatting
82+
## Formatting with `ExpressionTemplate`
8383

8484
_Serilog.Expressions_ includes the `ExpressionTemplate` class for text formatting. `ExpressionTemplate` implements `ITextFormatter`, so
8585
it works with any text-based Serilog sink:
@@ -89,11 +89,14 @@ it works with any text-based Serilog sink:
8989
9090
Log.Logger = new LoggerConfiguration()
9191
.WriteTo.Console(new ExpressionTemplate(
92-
"[{@t:HH:mm:ss} {@l:u3} ({SourceContext})] {@m} (first item is {Items[0]})\n{@x}"))
92+
"[{@t:HH:mm:ss} {@l:u3} ({SourceContext})] {@m} (first item is {Cart[0]})\n{@x}"))
9393
.CreateLogger();
94+
95+
// Produces log events like:
96+
// [21:21:40 INF (Sample.Program)] Cart contains ["Tea","Coffee"] (first item is Tea)
9497
```
9598

96-
Note the use of `{Items[0]}`: "holes" in expression templates can include any valid expression.
99+
Note the use of `{Cart[0]}`: "holes" in expression templates can include any valid expression over properties from the event.
97100

98101
Newline-delimited JSON (for example, replicating the [CLEF format](https://github.com/serilog/serilog-formatting-compact)) can be generated
99102
using object literals:
@@ -109,7 +112,7 @@ using object literals:
109112

110113
The following properties are available in expressions:
111114

112-
* **All first-class properties of the event** — no special syntax: `SourceContext` and `Items` are used in the formatting example above
115+
* **All first-class properties of the event** — no special syntax: `SourceContext` and `Cart` are used in the formatting examples above
113116
* `@t` - the event's timestamp, as a `DateTimeOffset`
114117
* `@m` - the rendered message
115118
* `@mt` - the raw message template
@@ -149,7 +152,7 @@ A typical set of operators is supported:
149152
* Accessors `a.b`
150153
* Indexers `a['b']` and `a[0]`
151154
* Wildcard indexing - `a[?]` any, and `a[*]` all
152-
* Conditional `if a then b else c` (all branches required)
155+
* Conditional `if a then b else c` (all branches required; see also the section below on _conditional blocks_)
153156

154157
Comparision operators that act on text all accept an optional postfix `ci` modifier to select case-insensitive comparisons:
155158

@@ -195,6 +198,64 @@ Functions that compare text accept an optional postfix `ci` modifier to select c
195198
StartsWith(User.Name, 'n') ci
196199
```
197200

201+
### Template directives
202+
203+
#### Conditional blocks
204+
205+
Within an `ExpressionTemplate`, a portion of the template can be conditionally evaluated using `#if`.
206+
207+
```csharp
208+
Log.Logger = new LoggerConfiguration()
209+
.WriteTo.Console(new ExpressionTemplate(
210+
"[{@t:HH:mm:ss} {@l:u3}{#if SourceContext is not null} ({SourceContext}){#end}] {@m}\n{@x}"))
211+
.CreateLogger();
212+
213+
// Produces log events like:
214+
// [21:21:45 INF] Starting up
215+
// [21:21:46 INF (Sample.Program)] Firing engines
216+
```
217+
218+
The block between the `{#if <expr>}` and `{#end}` directives will only appear in the output if `<expr>` is `true` - in the example, events with a `SourceContext` include this in parentheses, while those without, don't.
219+
220+
It's important to notice that the directive requires a Boolean `true` before the conditional block will be evaluated. It wouldn't be sufficient in this case to write `{#if SourceContext}`, since no values other than `true` are considered "truthy".
221+
222+
The syntax supports `{#if <expr>}`, chained `{#else if <expr>}`, `{#else}`, and `{#end}`, with arbitrary nesting.
223+
224+
#### Repetition
225+
226+
If a log event includes structured data in arrays or objects, a template block can be repeated for each element or member using `#each`/`in` (newlines, double quotes and construction of the `ExpressionTemplate` omitted for clarity):
227+
228+
```
229+
{@l:w4}: {SourceContext}
230+
{#each s in Scope}=> {s}{#delimit} {#end}
231+
{@m}
232+
{@x}
233+
```
234+
235+
This example uses the optional `#delimit` to add a space between each element, producing output like:
236+
237+
```
238+
info: Sample.Program
239+
=> Main => TextFormattingExample
240+
Hello, world!
241+
```
242+
243+
When using `{#each <name> in <expr>}` over an object, such as the built-in `@p` (properties) object, `<name>` will be bound to the _names_ of the properties of the object.
244+
245+
To get to the _values_ of the properties, use a second binding:
246+
247+
```
248+
{#each k, v in @p}{k} = {v}{#delimit},{#end}
249+
```
250+
251+
This example, if an event has three properties, will produce output like:
252+
253+
```
254+
Account = "nblumhardt", Cart = ["Tea", "Coffee"], Powerup = 42
255+
```
256+
257+
The syntax supports `{#each <name>[, <name>] in <expr>}`, an optional `{#delimit}` block, and finally an optional `{#else}` block, which will be evaluated if the array or object is empty.
258+
198259
## Recipes
199260

200261
**Trim down `SourceContext` to a type name only:**

0 commit comments

Comments
 (0)