Skip to content

Commit

Permalink
Disable theming when console output is redirected (#40)
Browse files Browse the repository at this point in the history
* README updates

* Disable theming when console output is redirected
  • Loading branch information
nblumhardt authored Jun 1, 2021
1 parent 81a525a commit f0fcfdc
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -339,8 +351,9 @@ convert the result to plain-old-.NET-types like `string`, `bool`, `Dictionary<K,
User-defined functions can be plugged in by implementing static methods that:

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

For example:

Expand Down
Binary file added assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 23 additions & 4 deletions src/Serilog.Expressions/Templates/ExpressionTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static bool TryParse(
[MaybeNullWhen(true)] out string error)
{
if (template == null) throw new ArgumentNullException(nameof(template));
return TryParse(template, null, null, null, out result, out error);
return TryParse(template, null, null, null, false, out result, out error);
}

/// <summary>
Expand All @@ -60,12 +60,15 @@ public static bool TryParse(
/// <param name="error">A description of the error, if unsuccessful.</param>
/// <param name="nameResolver">Optionally, a <see cref="NameResolver"/>
/// with which to resolve function names that appear in the template.</param>
/// <param name="applyThemeWhenOutputIsRedirected">Apply <paramref name="theme"/> even when
/// <see cref="System.Console.IsOutputRedirected"/> or <see cref="Console.IsErrorRedirected"/> returns <c>true</c>.</param>
/// <returns><c langword="true">true</c> if the template was well-formed.</returns>
public static bool TryParse(
string template,
IFormatProvider? formatProvider,
NameResolver? nameResolver,
TemplateTheme? theme,
bool applyThemeWhenOutputIsRedirected,
[MaybeNullWhen(false)] out ExpressionTemplate result,
[MaybeNullWhen(true)] out string error)
{
Expand All @@ -84,7 +87,7 @@ public static bool TryParse(
TemplateCompiler.Compile(
planned,
formatProvider, DefaultFunctionNameResolver.Build(nameResolver),
theme ?? TemplateTheme.None));
SelectTheme(theme, applyThemeWhenOutputIsRedirected)));

return true;
}
Expand All @@ -103,11 +106,14 @@ public static bool TryParse(
/// <param name="nameResolver">Optionally, a <see cref="NameResolver"/>
/// with which to resolve function names that appear in the template.</param>
/// <param name="theme">Optionally, an ANSI theme to apply to the template output.</param>
/// <param name="applyThemeWhenOutputIsRedirected">Apply <paramref name="theme"/> even when
/// <see cref="Console.IsOutputRedirected"/> or <see cref="Console.IsErrorRedirected"/> returns <c>true</c>.</param>
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));

Expand All @@ -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;
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion test/Serilog.Expressions.Tests/TemplateParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit f0fcfdc

Please sign in to comment.