Skip to content

Commit f0fcfdc

Browse files
authored
Disable theming when console output is redirected (#40)
* README updates * Disable theming when console output is redirected
1 parent 81a525a commit f0fcfdc

File tree

4 files changed

+42
-10
lines changed

4 files changed

+42
-10
lines changed

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ _Serilog.Expressions_ adds a number of expression-based overloads and helper met
8282
## Formatting with `ExpressionTemplate`
8383

8484
_Serilog.Expressions_ includes the `ExpressionTemplate` class for text formatting. `ExpressionTemplate` implements `ITextFormatter`, so
85-
it works with any text-based Serilog sink:
85+
it works with any text-based Serilog sink, including `Console`, `File`, `Debug`, and `Email`:
8686

8787
```csharp
8888
// using Serilog.Templates;
@@ -96,7 +96,19 @@ Log.Logger = new LoggerConfiguration()
9696
// [21:21:40 INF (Sample.Program)] Cart contains ["Tea","Coffee"] (first item is Tea)
9797
```
9898

99-
Note the use of `{Cart[0]}`: "holes" in expression templates can include any valid expression over properties from the event.
99+
Templates are based on .NET format strings, and support standard padding, alignment, and format specifiers.
100+
101+
Along with standard properties for the event timestamp (`@t`), level (`@l`) and so on, "holes" in expression templates can include complex
102+
expressions over the first-class properties of the event, like `{SourceContex}` and `{Cart[0]}` in the example..
103+
104+
Templates support customizable color themes when used with the `Console` sink:
105+
106+
```csharp
107+
.WriteTo.Console(new ExpressionTemplate(
108+
"[{@t:HH:mm:ss} {@l:u3}] {@m}\n{@x}", theme: TemplateTheme.Code))
109+
```
110+
111+
![Screenshot showing colored terminal output](https://raw.githubusercontent.com/serilog/serilog-expressions/dev/assets/screenshot.png)
100112

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

113125
The following properties are available in expressions:
114126

115-
* **All first-class properties of the event** — no special syntax: `SourceContext` and `Cart` are used in the formatting examples above
127+
* **All first-class properties of the event** - no special syntax: `SourceContext` and `Cart` are used in the formatting examples above
116128
* `@t` - the event's timestamp, as a `DateTimeOffset`
117129
* `@m` - the rendered message
118130
* `@mt` - the raw message template
@@ -339,8 +351,9 @@ convert the result to plain-old-.NET-types like `string`, `bool`, `Dictionary<K,
339351
User-defined functions can be plugged in by implementing static methods that:
340352

341353
* Return `LogEventPropertyValue?`,
342-
* Have arguments of type `LogEventPropertyValue?`, and
343-
* If the `ci` modifier is supported, accept a `StringComparison` in the first argument position.
354+
* Have arguments of type `LogEventPropertyValue?`,
355+
* If the `ci` modifier is supported, accept a `StringComparison`, and
356+
* If culture-specific formatting or comparisons are used, accepts an `IFormatProvider`.
344357

345358
For example:
346359

assets/screenshot.png

56.6 KB
Loading

src/Serilog.Expressions/Templates/ExpressionTemplate.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static bool TryParse(
4646
[MaybeNullWhen(true)] out string error)
4747
{
4848
if (template == null) throw new ArgumentNullException(nameof(template));
49-
return TryParse(template, null, null, null, out result, out error);
49+
return TryParse(template, null, null, null, false, out result, out error);
5050
}
5151

5252
/// <summary>
@@ -60,12 +60,15 @@ public static bool TryParse(
6060
/// <param name="error">A description of the error, if unsuccessful.</param>
6161
/// <param name="nameResolver">Optionally, a <see cref="NameResolver"/>
6262
/// with which to resolve function names that appear in the template.</param>
63+
/// <param name="applyThemeWhenOutputIsRedirected">Apply <paramref name="theme"/> even when
64+
/// <see cref="System.Console.IsOutputRedirected"/> or <see cref="Console.IsErrorRedirected"/> returns <c>true</c>.</param>
6365
/// <returns><c langword="true">true</c> if the template was well-formed.</returns>
6466
public static bool TryParse(
6567
string template,
6668
IFormatProvider? formatProvider,
6769
NameResolver? nameResolver,
6870
TemplateTheme? theme,
71+
bool applyThemeWhenOutputIsRedirected,
6972
[MaybeNullWhen(false)] out ExpressionTemplate result,
7073
[MaybeNullWhen(true)] out string error)
7174
{
@@ -84,7 +87,7 @@ public static bool TryParse(
8487
TemplateCompiler.Compile(
8588
planned,
8689
formatProvider, DefaultFunctionNameResolver.Build(nameResolver),
87-
theme ?? TemplateTheme.None));
90+
SelectTheme(theme, applyThemeWhenOutputIsRedirected)));
8891

8992
return true;
9093
}
@@ -103,11 +106,14 @@ public static bool TryParse(
103106
/// <param name="nameResolver">Optionally, a <see cref="NameResolver"/>
104107
/// with which to resolve function names that appear in the template.</param>
105108
/// <param name="theme">Optionally, an ANSI theme to apply to the template output.</param>
109+
/// <param name="applyThemeWhenOutputIsRedirected">Apply <paramref name="theme"/> even when
110+
/// <see cref="Console.IsOutputRedirected"/> or <see cref="Console.IsErrorRedirected"/> returns <c>true</c>.</param>
106111
public ExpressionTemplate(
107112
string template,
108113
IFormatProvider? formatProvider = null,
109114
NameResolver? nameResolver = null,
110-
TemplateTheme? theme = null)
115+
TemplateTheme? theme = null,
116+
bool applyThemeWhenOutputIsRedirected = false)
111117
{
112118
if (template == null) throw new ArgumentNullException(nameof(template));
113119

@@ -119,7 +125,20 @@ public ExpressionTemplate(
119125

120126
_compiled = TemplateCompiler.Compile(
121127
planned,
122-
formatProvider, DefaultFunctionNameResolver.Build(nameResolver), theme ?? TemplateTheme.None);
128+
formatProvider,
129+
DefaultFunctionNameResolver.Build(nameResolver),
130+
SelectTheme(theme, applyThemeWhenOutputIsRedirected));
131+
}
132+
133+
static TemplateTheme SelectTheme(TemplateTheme? supplied, bool applyThemeWhenOutputIsRedirected)
134+
{
135+
if (supplied == null ||
136+
(Console.IsOutputRedirected || Console.IsErrorRedirected) && !applyThemeWhenOutputIsRedirected)
137+
{
138+
return TemplateTheme.None;
139+
}
140+
141+
return supplied;
123142
}
124143

125144
/// <inheritdoc />

test/Serilog.Expressions.Tests/TemplateParserTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class TemplateParserTests
1919
[InlineData("Empty {Align,} digits", "Syntax error (line 1, column 14): unexpected `}`, expected alignment and width.")]
2020
public void ErrorsAreReported(string input, string error)
2121
{
22-
Assert.False(ExpressionTemplate.TryParse(input, null, null, null, out _, out var actual));
22+
Assert.False(ExpressionTemplate.TryParse(input, null, null, null, false, out _, out var actual));
2323
Assert.Equal(error, actual);
2424
}
2525

0 commit comments

Comments
 (0)