diff --git a/README.md b/README.md index b7996d8..40e31a4 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ _Serilog.Expressions_ adds a number of expression-based overloads and helper met ## Formatting with `ExpressionTemplate` _Serilog.Expressions_ includes the `ExpressionTemplate` class for text formatting. `ExpressionTemplate` implements `ITextFormatter`, so -it works with any text-based Serilog sink: +it works with any text-based Serilog sink, including `Console`, `File`, `Debug`, and `Email`: ```csharp // using Serilog.Templates; @@ -96,7 +96,19 @@ Log.Logger = new LoggerConfiguration() // [21:21:40 INF (Sample.Program)] Cart contains ["Tea","Coffee"] (first item is Tea) ``` -Note the use of `{Cart[0]}`: "holes" in expression templates can include any valid expression over properties from the event. +Templates are based on .NET format strings, and support standard padding, alignment, and format specifiers. + +Along with standard properties for the event timestamp (`@t`), level (`@l`) and so on, "holes" in expression templates can include complex +expressions over the first-class properties of the event, like `{SourceContex}` and `{Cart[0]}` in the example.. + +Templates support customizable color themes when used with the `Console` sink: + +```csharp + .WriteTo.Console(new ExpressionTemplate( + "[{@t:HH:mm:ss} {@l:u3}] {@m}\n{@x}", theme: TemplateTheme.Code)) +``` + +![Screenshot showing colored terminal output](https://raw.githubusercontent.com/serilog/serilog-expressions/dev/assets/screenshot.png) Newline-delimited JSON (for example, replicating the [CLEF format](https://github.com/serilog/serilog-formatting-compact)) can be generated using object literals: @@ -112,7 +124,7 @@ using object literals: The following properties are available in expressions: - * **All first-class properties of the event** — no special syntax: `SourceContext` and `Cart` are used in the formatting examples above + * **All first-class properties of the event** - no special syntax: `SourceContext` and `Cart` are used in the formatting examples above * `@t` - the event's timestamp, as a `DateTimeOffset` * `@m` - the rendered message * `@mt` - the raw message template @@ -339,8 +351,9 @@ convert the result to plain-old-.NET-types like `string`, `bool`, `Dictionary @@ -60,12 +60,15 @@ public static bool TryParse( /// A description of the error, if unsuccessful. /// Optionally, a /// with which to resolve function names that appear in the template. + /// Apply even when + /// or returns true. /// true if the template was well-formed. public static bool TryParse( string template, IFormatProvider? formatProvider, NameResolver? nameResolver, TemplateTheme? theme, + bool applyThemeWhenOutputIsRedirected, [MaybeNullWhen(false)] out ExpressionTemplate result, [MaybeNullWhen(true)] out string error) { @@ -84,7 +87,7 @@ public static bool TryParse( TemplateCompiler.Compile( planned, formatProvider, DefaultFunctionNameResolver.Build(nameResolver), - theme ?? TemplateTheme.None)); + SelectTheme(theme, applyThemeWhenOutputIsRedirected))); return true; } @@ -103,11 +106,14 @@ public static bool TryParse( /// Optionally, a /// with which to resolve function names that appear in the template. /// Optionally, an ANSI theme to apply to the template output. + /// Apply even when + /// or returns true. public ExpressionTemplate( string template, IFormatProvider? formatProvider = null, NameResolver? nameResolver = null, - TemplateTheme? theme = null) + TemplateTheme? theme = null, + bool applyThemeWhenOutputIsRedirected = false) { if (template == null) throw new ArgumentNullException(nameof(template)); @@ -119,7 +125,20 @@ public ExpressionTemplate( _compiled = TemplateCompiler.Compile( planned, - formatProvider, DefaultFunctionNameResolver.Build(nameResolver), theme ?? TemplateTheme.None); + formatProvider, + DefaultFunctionNameResolver.Build(nameResolver), + SelectTheme(theme, applyThemeWhenOutputIsRedirected)); + } + + static TemplateTheme SelectTheme(TemplateTheme? supplied, bool applyThemeWhenOutputIsRedirected) + { + if (supplied == null || + (Console.IsOutputRedirected || Console.IsErrorRedirected) && !applyThemeWhenOutputIsRedirected) + { + return TemplateTheme.None; + } + + return supplied; } /// diff --git a/test/Serilog.Expressions.Tests/TemplateParserTests.cs b/test/Serilog.Expressions.Tests/TemplateParserTests.cs index ee62d39..b17b314 100644 --- a/test/Serilog.Expressions.Tests/TemplateParserTests.cs +++ b/test/Serilog.Expressions.Tests/TemplateParserTests.cs @@ -19,7 +19,7 @@ public class TemplateParserTests [InlineData("Empty {Align,} digits", "Syntax error (line 1, column 14): unexpected `}`, expected alignment and width.")] public void ErrorsAreReported(string input, string error) { - Assert.False(ExpressionTemplate.TryParse(input, null, null, null, out _, out var actual)); + Assert.False(ExpressionTemplate.TryParse(input, null, null, null, false, out _, out var actual)); Assert.Equal(error, actual); }