Skip to content

Commit 0737469

Browse files
committed
Variadics, lastindexof()
1 parent 13ff836 commit 0737469

File tree

13 files changed

+126
-68
lines changed

13 files changed

+126
-68
lines changed

example/Sample/Program.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22

33
namespace Sample
44
{
5-
public static class Program
5+
public class Program
66
{
77
public static void Main()
88
{
99
const string expr = "@Level = 'Information' and AppId is not null and Items[?] like 'C%'";
1010

1111
using var log = new LoggerConfiguration()
1212
.Enrich.WithProperty("AppId", 10)
13+
.Enrich.WithComputed("FirstItem", "Items[0]")
14+
.Enrich.WithComputed("SourceContext", "coalesce(substring(SourceContext, lastindexof(SourceContext, '.') + 1), SourceContext, '<no source>')")
1315
.Filter.ByIncludingOnly(expr)
14-
.WriteTo.Console()
16+
.WriteTo.Console(outputTemplate:
17+
"[{Timestamp:HH:mm:ss} {Level:u3} ({SourceContext})] {Message:lj} (first item is {FirstItem}){NewLine}{Exception}")
1518
.CreateLogger();
1619

17-
log.Information("Cart contains {@Items}", new[] { "Tea", "Coffee" });
20+
log.ForContext<Program>().Information("Cart contains {@Items}", new[] { "Tea", "Coffee" });
1821
log.Warning("Cart contains {@Items}", new[] { "Tea", "Coffee" });
1922
log.Information("Cart contains {@Items}", new[] { "Apricots" });
2023
log.Information("Cart contains {@Items}", new[] { "Peanuts", "Chocolate" });

serilog-expressions.sln.DotSettings

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
<s:Boolean x:Key="/Default/UserDictionary/Words/=nblumhardt/@EntryIndexedValue">True</s:Boolean>
88
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reorderable/@EntryIndexedValue">True</s:Boolean>
99
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean>
10-
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subproperties/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
10+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subproperties/@EntryIndexedValue">True</s:Boolean>
11+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Variadics/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public AccessorExpression(Expression receiver, string memberName)
1616

1717
public override string ToString()
1818
{
19-
if (SerilogExpression.IsValidPropertyName(MemberName))
19+
if (SerilogExpression.IsValidIdentifier(MemberName))
2020
return Receiver + "." + MemberName;
2121

2222
return $"{Receiver}['{SerilogExpression.EscapeStringContent(MemberName)}']";

src/Serilog.Expressions/Expressions/Ast/AmbientPropertyExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public AmbientPropertyExpression(string propertyName, bool isBuiltIn)
1010
{
1111
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
1212
IsBuiltIn = isBuiltIn;
13-
_requiresEscape = !SerilogExpression.IsValidPropertyName(propertyName);
13+
_requiresEscape = !SerilogExpression.IsValidIdentifier(propertyName);
1414
}
1515

1616
public string PropertyName { get; }

src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Serilog.Expressions.Compilation.Linq;
66
using Serilog.Expressions.Compilation.Properties;
77
using Serilog.Expressions.Compilation.Text;
8+
using Serilog.Expressions.Compilation.Variadics;
89
using Serilog.Expressions.Compilation.Wildcards;
910

1011
namespace Serilog.Expressions.Compilation
@@ -14,6 +15,7 @@ static class ExpressionCompiler
1415
public static CompiledExpression Compile(Expression expression)
1516
{
1617
var actual = expression;
18+
actual = VariadicCallRewriter.Rewrite(actual);
1719
actual = TextMatchingTransformer.Rewrite(actual);
1820
actual = LikeSyntaxTransformer.Rewrite(actual);
1921
actual = PropertiesObjectAccessorTransformer.Rewrite(actual);

src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,12 @@ public static CompiledExpression Compile(Expression expression)
7474
protected override Expression<CompiledExpression> Transform(CallExpression lx)
7575
{
7676
if (!OperatorMethods.TryGetValue(lx.OperatorName, out var m))
77-
throw new ArgumentException($"The function name `{lx.OperatorName}` was not recognised; to search for text instead, enclose the filter in \"double quotes\".");
78-
77+
throw new ArgumentException($"The function name `{lx.OperatorName}` was not recognised.");
78+
7979
if (m.GetParameters().Length != lx.Operands.Length)
80-
throw new ArgumentException($"The function `{lx.OperatorName}` requires {m.GetParameters().Length} arguments; to search for text instead, enclose the filter in \"double quotes\".");
80+
throw new ArgumentException($"The function `{lx.OperatorName}` requires {m.GetParameters().Length} arguments.");
8181

8282
var operands = lx.Operands.Select(Transform).ToArray();
83-
8483
var context = LX.Parameter(typeof(LogEvent));
8584

8685
var operandValues = operands.Select(o => Splice(o, context));
@@ -188,7 +187,7 @@ protected override Expression<CompiledExpression> Transform(AmbientPropertyExpre
188187
return context => NormalizeBuiltInProperty(context.Exception == null ? null : context.Exception.ToString());
189188

190189
if (px.PropertyName == BuiltInProperty.Timestamp)
191-
return context => new ScalarValue(context.Timestamp.ToString("o"));
190+
return context => new ScalarValue(context.Timestamp);
192191

193192
if (px.PropertyName == BuiltInProperty.MessageTemplate)
194193
return context => NormalizeBuiltInProperty(context.MessageTemplate.Text);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Linq;
2+
using Serilog.Expressions.Ast;
3+
using Serilog.Expressions.Compilation.Transformations;
4+
5+
namespace Serilog.Expressions.Compilation.Variadics
6+
{
7+
class VariadicCallRewriter : IdentityTransformer
8+
{
9+
static readonly VariadicCallRewriter Instance = new VariadicCallRewriter();
10+
11+
public static Expression Rewrite(Expression expression)
12+
{
13+
return Instance.Transform(expression);
14+
}
15+
16+
protected override Expression Transform(CallExpression lx)
17+
{
18+
if (Operators.SameOperator(lx.OperatorName, Operators.OpSubstring) && lx.Operands.Length == 2)
19+
{
20+
var operands = lx.Operands
21+
.Select(Transform)
22+
.Concat(new[] {new AmbientPropertyExpression(BuiltInProperty.Undefined, true)})
23+
.ToArray();
24+
return new CallExpression(lx.OperatorName, operands);
25+
}
26+
27+
if (Operators.SameOperator(lx.OperatorName, Operators.OpCoalesce))
28+
{
29+
if (lx.Operands.Length == 0)
30+
return new AmbientPropertyExpression(BuiltInProperty.Undefined, true);
31+
if (lx.Operands.Length == 1)
32+
return Transform(lx.Operands.Single());
33+
if (lx.Operands.Length > 2)
34+
{
35+
var first = Transform(lx.Operands.First());
36+
return new CallExpression(lx.OperatorName, first,
37+
Transform(new CallExpression(lx.OperatorName, lx.Operands.Skip(1).ToArray())));
38+
}
39+
}
40+
41+
return base.Transform(lx);
42+
}
43+
}
44+
}

src/Serilog.Expressions/Expressions/Operators.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static class Operators
3131
public const string OpNot = "Not";
3232
public const string OpContains = "Contains";
3333
public const string OpIndexOf = "IndexOf";
34+
public const string OpLastIndexOf = "IndexOf";
3435
public const string OpLength = "Length";
3536
public const string OpStartsWith = "StartsWith";
3637
public const string OpEndsWith = "EndsWith";

src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Globalization;
32
using System.Linq;
43
using Serilog.Events;
54
using Serilog.Expressions.Compilation.Linq;
@@ -172,15 +171,6 @@ public static LogEventPropertyValue _Internal_In(LogEventPropertyValue item, Log
172171

173172
return null;
174173
}
175-
176-
public static LogEventPropertyValue _Internal_EqualIgnoreCase(LogEventPropertyValue left, LogEventPropertyValue right)
177-
{
178-
if (!Coerce.String(left, out var ls) ||
179-
!Coerce.String(right, out var rs))
180-
return null;
181-
182-
return ScalarBoolean(CompareInfo.Compare(ls, rs, CompareOptions.OrdinalIgnoreCase) == 0);
183-
}
184174

185175
public static LogEventPropertyValue NotEqual(LogEventPropertyValue left, LogEventPropertyValue right)
186176
{
@@ -190,13 +180,6 @@ public static LogEventPropertyValue NotEqual(LogEventPropertyValue left, LogEven
190180
return ScalarBoolean(!UnboxedEqualHelper(left, right));
191181
}
192182

193-
public static LogEventPropertyValue _Internal_NotEqualIgnoreCase(LogEventPropertyValue left, LogEventPropertyValue right)
194-
{
195-
if (Coerce.Boolean(_Internal_EqualIgnoreCase(left, right), out var b))
196-
return ScalarBoolean(!b);
197-
return null;
198-
}
199-
200183
public static LogEventPropertyValue Negate(LogEventPropertyValue operand)
201184
{
202185
if (Coerce.Numeric(operand, out var numeric))
@@ -243,17 +226,6 @@ public static LogEventPropertyValue Contains(LogEventPropertyValue corpus, LogEv
243226
return ScalarBoolean(ctx.Contains(ptx));
244227
}
245228

246-
static readonly CompareInfo CompareInfo = CultureInfo.InvariantCulture.CompareInfo;
247-
248-
public static LogEventPropertyValue _Internal_ContainsIgnoreCase(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
249-
{
250-
if (!Coerce.String(corpus, out var ctx) ||
251-
!Coerce.String(pattern, out var ptx))
252-
return null;
253-
254-
return ScalarBoolean(CompareInfo.IndexOf(ctx, ptx, CompareOptions.OrdinalIgnoreCase) >= 0);
255-
}
256-
257229
public static LogEventPropertyValue IndexOf(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
258230
{
259231
if (!Coerce.String(corpus, out var ctx) ||
@@ -263,13 +235,13 @@ public static LogEventPropertyValue IndexOf(LogEventPropertyValue corpus, LogEve
263235
return new ScalarValue(ctx.IndexOf(ptx, StringComparison.Ordinal));
264236
}
265237

266-
public static LogEventPropertyValue _Internal_IndexOfIgnoreCase(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
238+
public static LogEventPropertyValue LastIndexOf(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
267239
{
268240
if (!Coerce.String(corpus, out var ctx) ||
269241
!Coerce.String(pattern, out var ptx))
270242
return null;
271243

272-
return new ScalarValue(ctx.IndexOf(ptx, StringComparison.OrdinalIgnoreCase));
244+
return new ScalarValue(ctx.LastIndexOf(ptx, StringComparison.Ordinal));
273245
}
274246

275247
public static LogEventPropertyValue Length(LogEventPropertyValue arg)
@@ -292,15 +264,6 @@ public static LogEventPropertyValue StartsWith(LogEventPropertyValue corpus, Log
292264
return ScalarBoolean(ctx.StartsWith(ptx, StringComparison.Ordinal));
293265
}
294266

295-
public static LogEventPropertyValue _Internal_StartsWithIgnoreCase(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
296-
{
297-
if (!Coerce.String(corpus, out var ctx) ||
298-
!Coerce.String(pattern, out var ptx))
299-
return null;
300-
301-
return ScalarBoolean(ctx.StartsWith(ptx, StringComparison.OrdinalIgnoreCase));
302-
}
303-
304267
public static LogEventPropertyValue EndsWith(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
305268
{
306269
if (!Coerce.String(corpus, out var ctx) ||
@@ -310,15 +273,6 @@ public static LogEventPropertyValue EndsWith(LogEventPropertyValue corpus, LogEv
310273
return ScalarBoolean(ctx.EndsWith(ptx, StringComparison.Ordinal));
311274
}
312275

313-
public static LogEventPropertyValue _Internal_EndsWithIgnoreCase(LogEventPropertyValue corpus, LogEventPropertyValue pattern)
314-
{
315-
if (!Coerce.String(corpus, out var ctx) ||
316-
!Coerce.String(pattern, out var ptx))
317-
return null;
318-
319-
return ScalarBoolean(ctx.EndsWith(ptx, StringComparison.OrdinalIgnoreCase));
320-
}
321-
322276
public static LogEventPropertyValue Has(LogEventPropertyValue value)
323277
{
324278
return ScalarBoolean(value != null);

src/Serilog.Expressions/Expressions/SerilogExpression.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public static CompiledExpression Compile(string expression)
3232
/// <param name="result">A function that evaluates the expression in the context of a log event.</param>
3333
/// <param name="error">The reported error, if compilation was unsuccessful.</param>
3434
/// <returns>True if the function could be created; otherwise, false.</returns>
35+
/// <remarks>Regular expression syntax errors currently generate exceptions instead of producing friendly
36+
/// errors.</remarks>
3537
public static bool TryCompile(string expression, out CompiledExpression result, out string error)
3638
{
3739
if (!ExpressionParser.TryParse(expression, out var root, out error))
@@ -72,15 +74,15 @@ public static string EscapeStringContent(string text)
7274
}
7375

7476
/// <summary>
75-
/// Determine if the specified text is a valid property name.
77+
/// Determine if the specified text is a valid identifier.
7678
/// </summary>
77-
/// <param name="propertyName">The text to check.</param>
79+
/// <param name="identifier">The text to check.</param>
7880
/// <returns>True if the text can be used verbatim as a property name.</returns>
79-
public static bool IsValidPropertyName(string propertyName)
81+
public static bool IsValidIdentifier(string identifier)
8082
{
81-
return propertyName.Length != 0 &&
82-
!char.IsDigit(propertyName[0]) &&
83-
propertyName.All(ch => char.IsLetter(ch) || char.IsDigit(ch) || ch == '_');
83+
return identifier.Length != 0 &&
84+
!char.IsDigit(identifier[0]) &&
85+
identifier.All(ch => char.IsLetter(ch) || char.IsDigit(ch) || ch == '_');
8486
}
8587
}
86-
}
88+
}

0 commit comments

Comments
 (0)