Skip to content

5.0.0 Release #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,22 @@ 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
* `@t` - the event's timestamp, as a `DateTimeOffset`
* `@m` - the rendered message
* `@m` - the rendered message (Note: do not add format specifiers like `:lj` or you'll lose theme color rendering. These format specifiers are not supported as they've become the default and only option - [see the discussion here](https://github.com/serilog/serilog-expressions/issues/56#issuecomment-1146472988)
* `@mt` - the raw message template
* `@l` - the event's level, as a `LogEventLevel`
* `@x` - the exception associated with the event, if any, as an `Exception`
* `@p` - a dictionary containing all first-class properties; this supports properties with non-identifier names, for example `@p['snake-case-name']`
* `@i` - event id; a 32-bit numeric hash of the event's message template
* `@r` - renderings; if any tokens in the message template include .NET-specific formatting, an array of rendered values for each such token
* `@tr` - trace id; The id of the trace that was active when the event was created, if any
* `@sp` - span id; The id of the span that was active when the event was created, if any

The built-in properties mirror those available in the CLEF format.

The exception property `@x` is treated as a scalar and will appear as a string when formatted into text. The properties of
the underlying `Exception` object can be accessed using `Inspect()`, for example `Inspect(@x).Message`, and the type of the
exception retrieved using `TypeOf(@x)`.

### Literals

| Data type | Description | Examples |
Expand Down Expand Up @@ -181,29 +187,30 @@ calling a function will be undefined if:
* any argument is undefined, or
* any argument is of an incompatible type.

| Function | Description |
| :--- | :--- |
| `Coalesce(p0, p1, [..pN])` | Returns the first defined, non-null argument. |
| `Concat(s0, s1, [..sN])` | Concatenate two or more strings. |
| `Contains(s, t)` | Tests whether the string `s` contains the substring `t`. |
| `ElementAt(x, i)` | Retrieves a property of `x` by name `i`, or array element of `x` by numeric index `i`. |
| `EndsWith(s, t)` | Tests whether the string `s` ends with substring `t`. |
| `IndexOf(s, t)` | Returns the first index of substring `t` in string `s`, or -1 if the substring does not appear. |
| `IndexOfMatch(s, p)` | Returns the index of the first match of regular expression `p` in string `s`, or -1 if the regular expression does not match. |
| `IsMatch(s, p)` | Tests whether the regular expression `p` matches within the string `s`. |
| `IsDefined(x)` | Returns `true` if the expression `x` has a value, including `null`, or `false` if `x` is undefined. |
| `LastIndexOf(s, t)` | Returns the last index of substring `t` in string `s`, or -1 if the substring does not appear. |
| `Length(x)` | Returns the length of a string or array. |
| `Now()` | Returns `DateTimeOffset.Now`. |
| `Rest([deep])` | In an `ExpressionTemplate`, returns an object containing the first-class event properties not otherwise referenced in the template. If `deep` is `true`, also excludes properties referenced in the event's message template. |
| `Round(n, m)` | Round the number `n` to `m` decimal places. |
| `StartsWith(s, t)` | Tests whether the string `s` starts with substring `t`. |
| `Substring(s, start, [length])` | Return the substring of string `s` from `start` to the end of the string, or of `length` characters, if this argument is supplied. |
| `TagOf(o)` | Returns the `TypeTag` field of a captured object (i.e. where `TypeOf(x)` is `'object'`). |
| `ToString(x, [format])` | Convert `x` to a string, applying the format string `format` if `x` is `IFormattable`. |
| `TypeOf(x)` | Returns a string describing the type of expression `x`: a .NET type name if `x` is scalar and non-null, or, `'array'`, `'object'`, `'dictionary'`, `'null'`, or `'undefined'`. |
| `Undefined()` | Explicitly mark an undefined value. |
| `UtcDateTime(x)` | Convert a `DateTime` or `DateTimeOffset` into a UTC `DateTime`. |
| Function | Description |
|:--------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Coalesce(p0, p1, [..pN])` | Returns the first defined, non-null argument. |
| `Concat(s0, s1, [..sN])` | Concatenate two or more strings. |
| `Contains(s, t)` | Tests whether the string `s` contains the substring `t`. |
| `ElementAt(x, i)` | Retrieves a property of `x` by name `i`, or array element of `x` by numeric index `i`. |
| `EndsWith(s, t)` | Tests whether the string `s` ends with substring `t`. |
| `IndexOf(s, t)` | Returns the first index of substring `t` in string `s`, or -1 if the substring does not appear. |
| `IndexOfMatch(s, p)` | Returns the index of the first match of regular expression `p` in string `s`, or -1 if the regular expression does not match. |
| `Inspect(o, [deep])` | Read properties from an object captured as the scalar value `o`. |
| `IsMatch(s, p)` | Tests whether the regular expression `p` matches within the string `s`. |
| `IsDefined(x)` | Returns `true` if the expression `x` has a value, including `null`, or `false` if `x` is undefined. |
| `LastIndexOf(s, t)` | Returns the last index of substring `t` in string `s`, or -1 if the substring does not appear. |
| `Length(x)` | Returns the length of a string or array. |
| `Now()` | Returns `DateTimeOffset.Now`. |
| `Rest([deep])` | In an `ExpressionTemplate`, returns an object containing the first-class event properties not otherwise referenced in the template. If `deep` is `true`, also excludes properties referenced in the event's message template. |
| `Round(n, m)` | Round the number `n` to `m` decimal places. |
| `StartsWith(s, t)` | Tests whether the string `s` starts with substring `t`. |
| `Substring(s, start, [length])` | Return the substring of string `s` from `start` to the end of the string, or of `length` characters, if this argument is supplied. |
| `TagOf(o)` | Returns the `TypeTag` field of a captured object (i.e. where `TypeOf(x)` is `'object'`). |
| `ToString(x, [format])` | Convert `x` to a string, applying the format string `format` if `x` is `IFormattable`. |
| `TypeOf(x)` | Returns a string describing the type of expression `x`: a .NET type name if `x` is scalar and non-null, or, `'array'`, `'object'`, `'dictionary'`, `'null'`, or `'undefined'`. |
| `Undefined()` | Explicitly mark an undefined value. |
| `UtcDateTime(x)` | Convert a `DateTime` or `DateTimeOffset` into a UTC `DateTime`. |

Functions that compare text accept an optional postfix `ci` modifier to select case-insensitive comparisons:

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ artifacts:
deploy:
- provider: NuGet
api_key:
secure: AcMGMnsJdQe1+SQwf+9VpRqcKNw93zr96OlxAEmPob52vqxDNH844SmdYidGX0cL
secure: ZCEcKeB0btSRWVPgGPqQKphQeTcljBBsA4GKGW0Gmjw+UfXvS0LCcWzYdPXUWo5N
skip_symbols: true
on:
branch: /^(main|dev)$/
Expand Down
1 change: 1 addition & 0 deletions serilog-expressions.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CI/@EntryIndexedValue">CI</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Acerola/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Comparand/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=delim/@EntryIndexedValue">True</s:Boolean>
Expand Down
8 changes: 8 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An accessor retrieves a property from a (structured) object. For example, in the expression
/// <code>Headers.ContentType</code> the <code>.</code> operator forms an accessor expression that
/// retrieves the <code>ContentType</code> property from the <code>Headers</code> object.
/// </summary>
/// <remarks>Note that the AST type can represent accessors that cannot be validly written using
/// <code>.</code> notation. In these cases, if the accessor is formatted back out as an expression,
/// <see cref="IndexerExpression"/> notation will be used.</remarks>
class AccessorExpression : Expression
{
public AccessorExpression(Expression receiver, string memberName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An ambient name is generally a property name or built-in that appears standalone in an expression. For example,
/// in <code>Headers.ContentType</code>, <code>Headers</code> is an ambient name that produces an
/// <see cref="AmbientNameExpression"/>. Built-ins like <code>@Level</code> are also parsed as ambient names.
/// </summary>
class AmbientNameExpression : Expression
{
readonly bool _requiresEscape;
Expand Down
7 changes: 7 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An array expression constructs an array from a list of elements. For example, <code>[1, 2, 3]</code> is an
/// array expression. The items in an array expression can be literal values or expressions, like in the
/// above example, or they can be spread expressions that describe zero or more elements to include in the
/// list. Whether included via regular elements or spread expressions, undefined values are ignored and won't
/// appear in the resulting array value.
/// </summary>
class ArrayExpression : Expression
{
public ArrayExpression(Element[] elements)
Expand Down
4 changes: 4 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/CallExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// A <see cref="CallExpression"/> is a function call made up of the function name, parenthesised argument
/// list, and optional postfix <code>ci</code> modifier. For example, <code>Substring(RequestPath, 0, 5)</code>.
/// </summary>
class CallExpression : Expression
{
public CallExpression(bool ignoreCase, string operatorName, params Expression[] operands)
Expand Down
22 changes: 10 additions & 12 deletions src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// A constant such as <code>'hello'</code>, <code>true</code>, <code>null</code>, or <code>123.45</code>.
/// </summary>
class ConstantExpression : Expression
{
public ConstantExpression(LogEventPropertyValue constant)
Expand All @@ -30,19 +33,14 @@ public override string ToString()
{
if (Constant is ScalarValue sv)
{
switch (sv.Value)
return sv.Value switch
{
case string s:
return "'" + s.Replace("'", "''") + "'";
case true:
return "true";
case false:
return "false";
case IFormattable formattable:
return formattable.ToString(null, CultureInfo.InvariantCulture);
default:
return (sv.Value ?? "null").ToString() ?? "<ToString() returned null>";
}
string s => "'" + s.Replace("'", "''") + "'",
true => "true",
false => "false",
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),
_ => (sv.Value ?? "null").ToString() ?? "<ToString() returned null>"
};
}

return Constant.ToString();
Expand Down
7 changes: 4 additions & 3 deletions src/Serilog.Expressions/Expressions/Ast/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

namespace Serilog.Expressions.Ast;

abstract class Element
{
}
/// <summary>
/// An element in an <see cref="ArrayExpression"/>.
/// </summary>
abstract class Element;
9 changes: 8 additions & 1 deletion src/Serilog.Expressions/Expressions/Ast/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An AST node.
/// </summary>
abstract class Expression
{
// Used only as an enabler for testing and debugging.
/// <summary>
/// The <see cref="ToString"/> representation of an <see cref="Expression"/> is <strong>not</strong>
/// guaranteed to be syntactically valid: this is provided for debugging purposes only.
/// </summary>
/// <returns>A textual representation of the expression.</returns>
public abstract override string ToString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// A non-syntax expression tree node used when compiling the <see cref="Operators.OpIndexOfMatch"/>,
/// <see cref="Operators.OpIsMatch"/>, and SQL-style <code>like</code> expressions.
/// </summary>
class IndexOfMatchExpression : Expression
{
public Expression Corpus { get; }
Expand Down
5 changes: 5 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An <see cref="IndexerExpression"/> retrieves a property from an object, by name, or an item from an array
/// by zero-based numeric index. For example, <code>Headers['Content-Type']</code> and <code>Items[2]</code> are
/// parsed as indexer expressions.
/// </summary>
class IndexerExpression : Expression
{
public Expression Receiver { get; }
Expand Down
3 changes: 3 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// Describes the wildcard in a <see cref="IndexerWildcardExpression"/>.
/// </summary>
enum IndexerWildcard { Undefined, Any, All }
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An indexer wildcard is a placeholder in a property path expression. For example,
/// in <code>Headers[?] = 'test'</code>, the question-mark token is a wildcard that means "any property of
/// the <code>Headers</code> object". The other wildcard indexer is the asterisk, meaning "all".
/// </summary>
class IndexerWildcardExpression : Expression
{
public IndexerWildcardExpression(IndexerWildcard wildcard)
Expand Down
3 changes: 3 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/ItemElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// A single item in an <see cref="ArrayExpression"/>.
/// </summary>
class ItemElement : Element
{
public Expression Value { get; }
Expand Down
4 changes: 4 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// A non-syntax expression tree node used in the compilation of <see cref="IndexerWildcardExpression"/>. Only
/// very limited support for lambda expressions is currently present.
/// </summary>
class LambdaExpression : Expression
{
public LambdaExpression(ParameterExpression[] parameters, Expression body)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// Represents the iteration variable in template <code>#each</code> directives.
/// </summary>
class LocalNameExpression : Expression
{
public LocalNameExpression(string name)
Expand Down
7 changes: 4 additions & 3 deletions src/Serilog.Expressions/Expressions/Ast/Member.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

namespace Serilog.Expressions.Ast;

abstract class Member
{
}
/// <summary>
/// A member in an <see cref="ObjectExpression"/>.
/// </summary>
abstract class Member;
5 changes: 5 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// Constructs an object given a list of members. Members can be <code>name: value</code> pairs, or spread
/// expressions that include members from another object. Where names conflict, the rightmost appearance of
/// a name wins. Members that evaluate to an undefined value do not appear in the resulting object.
/// </summary>
class ObjectExpression : Expression
{
public ObjectExpression(Member[] members)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// Non-syntax expression type used to represent parameters in <see cref="LambdaExpression"/> bodies.
/// </summary>
class ParameterExpression : Expression
{
public ParameterExpression(string parameterName)
Expand Down
5 changes: 5 additions & 0 deletions src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

namespace Serilog.Expressions.Ast;

/// <summary>
/// An <see cref="ObjectExpression"/> member comprising an optionally-quoted name and a value, for example
/// the object <code>{Username: 'alice'}</code> includes a single <see cref="PropertyMember"/> with name
/// <code>Username</code> and value <code>'alice'</code>.
/// </summary>
class PropertyMember : Member
{
public string Name { get; }
Expand Down
Loading