Skip to content

Commit

Permalink
Merge pull request #111 from serilog/dev
Browse files Browse the repository at this point in the history
5.0.0 Release
  • Loading branch information
nblumhardt authored Jun 4, 2024
2 parents 63f621d + 3198122 commit a2a6ea7
Show file tree
Hide file tree
Showing 56 changed files with 353 additions and 189 deletions.
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

0 comments on commit a2a6ea7

Please sign in to comment.