diff --git a/Build.ps1 b/Build.ps1 index 7c5a85f..8283851 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -12,40 +12,49 @@ if(Test-Path .\artifacts) { $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; $revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "master" -and $revision -ne "local"] +$commitHash = $(git rev-parse --short HEAD) +$buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""] -echo "build: Version suffix is $suffix" +echo "build: Package version suffix is $suffix" +echo "build: Build version suffix is $buildSuffix" foreach ($src in ls src/*) { Push-Location $src echo "build: Packaging project in $src" - & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix + & dotnet build -c Release --version-suffix=$buildSuffix + + if($suffix) { + & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts --version-suffix=$suffix + } else { + & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts + } if($LASTEXITCODE -ne 0) { exit 1 } Pop-Location } -foreach ($test in ls test/*.PerformanceTests) { +foreach ($test in ls test/*.Tests) { Push-Location $test - echo "build: Building performance test project in $test" + echo "build: Testing project in $test" - & dotnet build -c Release - if($LASTEXITCODE -ne 0) { exit 2 } + & dotnet test -c Release + if($LASTEXITCODE -ne 0) { exit 3 } Pop-Location } -foreach ($test in ls test/*.Tests) { +foreach ($test in ls test/*.PerformanceTests) { Push-Location $test - echo "build: Testing project in $test" + echo "build: Building performance test project in $test" - & dotnet test -c Release - if($LASTEXITCODE -ne 0) { exit 3 } + & dotnet build -c Release + if($LASTEXITCODE -ne 0) { exit 2 } Pop-Location } -Pop-Location +Pop-Location \ No newline at end of file diff --git a/README.md b/README.md index f0395e9..6cfbd34 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Serilog.Filters.Expressions [![Build status](https://ci.appveyor.com/api/projects/status/wnh0ig2udlld9oe4?svg=true)](https://ci.appveyor.com/project/serilog/serilog-filters-expressions) [![NuGet Pre Release](https://img.shields.io/nuget/vpre/Serilog.Filters.Expressions.svg)](https://nuget.org/packages/serilog.filters.expressions) +# Serilog.Filters.Expressions [![Build status](https://ci.appveyor.com/api/projects/status/wnh0ig2udlld9oe4?svg=true)](https://ci.appveyor.com/project/serilog/serilog-filters-expressions) [![NuGet Release](https://img.shields.io/nuget/v/Serilog.Filters.Expressions.svg)](https://nuget.org/packages/serilog.filters.expressions) Expression-based event filtering for [Serilog](https://serilog.net). @@ -8,7 +8,7 @@ var expr = "@Level = 'Information' and AppId is not null and Items[?] like 'C%'" Log.Logger = new LoggerConfiguration() .Enrich.WithProperty("AppId", 10) .Filter.ByIncludingOnly(expr) - .WriteTo.LiterateConsole() + .WriteTo.Console() .CreateLogger(); // Printed @@ -40,10 +40,10 @@ The syntax is based on SQL, with added support for object structures, arrays, an | :--- | :--- | | **Literals** | `123`, `123.4`, `'Hello'`, `true`, `false`, `null` | | **Properties** | `A`, `A.B`, `@Level`, `@Timestamp`, `@Exception`, `@Message`, `@MessageTemplate`, `@Properties['A-b-c']` | -| **Comparisons** | `A = B`, `A <> B`, `A > B`, `A >= B`, `A is null`, `A is not null` | -| **Text** | `A like 'H%'`, `A not like 'H%'`, `A like 'Hel_o'`, `Contains(A, 'H')`, `StartsWith(A, 'H')`, `EndsWith(A, 'H')`, `IndexOf(A, 'H')` | +| **Comparisons** | `A = B`, `A <> B`, `A > B`, `A >= B`, `A is null`, `A is not null`, `A in [1, 2]` | +| **Text** | `A like 'H%'`, `A not like 'H%'`, `A like 'Hel_o'`, `Contains(A, 'H')`, `StartsWith(A, 'H')`, `EndsWith(A, 'H')`, `IndexOf(A, 'H')`, `Length(A)` | | **Regular expressions** | `A = /H.*o/`, `Contains(A, /[lL]/)`, other string functions | -| **Collections** | `A[0] = 'Hello'`, `A[?] = 'Hello'` (any), `StartsWith(A[*], 'H')` (all) | +| **Collections** | `A[0] = 'Hello'`, `A[?] = 'Hello'` (any), `StartsWith(A[*], 'H')` (all), `Length(A)` | | **Maths** | `A + 2`, `A - 2`, `A * 2`, `A % 2` | | **Logic** | `not A`, `A and B`, `A or B` | | **Grouping** | `A * (B + C)` | diff --git a/appveyor.yml b/appveyor.yml index a33021a..cf14c5c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,13 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2015 -configuration: Release -install: - - ps: mkdir -Force ".\build\" | Out-Null - - ps: Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview2/scripts/obtain/dotnet-install.ps1" -OutFile ".\build\installcli.ps1" - - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli" - - ps: '& .\build\installcli.ps1 -InstallDir "$env:DOTNET_INSTALL_DIR" -NoPath -Version 1.0.0-preview2-003131' - - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path" +image: Visual Studio 2017 build_script: - ps: ./Build.ps1 test: off diff --git a/example/Sample/Sample.csproj b/example/Sample/Sample.csproj new file mode 100644 index 0000000..e397cba --- /dev/null +++ b/example/Sample/Sample.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp1.0 + Sample + Exe + Sample + $(PackageTargetFallback);dnxcore50 + false + false + false + + + + + + + + + + + diff --git a/example/Sample/Sample.xproj b/example/Sample/Sample.xproj deleted file mode 100644 index 5ff8cdd..0000000 --- a/example/Sample/Sample.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 776eecac-3c50-45ea-847d-0ebe5158e51e - Sample - .\obj - .\bin\ - v4.5.2 - - - - 2.0 - - - diff --git a/example/Sample/project.json b/example/Sample/project.json deleted file mode 100644 index 415afe6..0000000 --- a/example/Sample/project.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "1.0.0-*", - "buildOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.1" - }, - "Serilog.Filters.Expressions": {"target": "project"}, - "Serilog.Sinks.Literate": "2.0.0" - }, - - "frameworks": { - "netcoreapp1.0": { - "imports": "dnxcore50" - } - } -} diff --git a/global.json b/global.json deleted file mode 100644 index d1816c2..0000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "projects": [ "src", "test", "example" ], - "sdk": { - "version": "1.0.0-preview2-003131" - } -} diff --git a/serilog-filters-expressions.sln b/serilog-filters-expressions.sln index a1f5d19..98eb56a 100644 --- a/serilog-filters-expressions.sln +++ b/serilog-filters-expressions.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{91E482DE-E1E7-4CE1-9511-C0AF07F3648A}" EndProject @@ -11,7 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{24B112 .gitignore = .gitignore appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 - global.json = global.json LICENSE = LICENSE README.md = README.md RunPerfTests.ps1 = RunPerfTests.ps1 @@ -19,15 +18,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{24B112 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{BD94A77E-34B1-478E-B921-E87A5F71B574}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Sample", "example\Sample\Sample.xproj", "{776EECAC-3C50-45EA-847D-0EBE5158E51E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B03B3086-D197-4B32-9AE2-8536C345EA2D}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions", "src\Serilog.Filters.Expressions\Serilog.Filters.Expressions.xproj", "{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "example\Sample\Sample.csproj", "{776EECAC-3C50-45EA-847D-0EBE5158E51E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B03B3086-D197-4B32-9AE2-8536C345EA2D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions", "src\Serilog.Filters.Expressions\Serilog.Filters.Expressions.csproj", "{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions.Tests", "test\Serilog.Filters.Expressions.Tests\Serilog.Filters.Expressions.Tests.xproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions.Tests", "test\Serilog.Filters.Expressions.Tests\Serilog.Filters.Expressions.Tests.csproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions.PerformanceTests", "test\Serilog.Filters.Expressions.PerformanceTests\Serilog.Filters.Expressions.PerformanceTests.xproj", "{D7A37F73-BBA3-4DAE-9648-1A753A86F968}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions.PerformanceTests", "test\Serilog.Filters.Expressions.PerformanceTests\Serilog.Filters.Expressions.PerformanceTests.csproj", "{D7A37F73-BBA3-4DAE-9648-1A753A86F968}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs new file mode 100644 index 0000000..1880fb8 --- /dev/null +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; + +namespace Serilog.Filters.Expressions.Ast +{ + class FilterArrayExpression : FilterExpression + { + public FilterArrayExpression(FilterExpression[] elements) + { + Elements = elements ?? throw new ArgumentNullException(nameof(elements)); + } + + public FilterExpression[] Elements { get; } + + public override string ToString() + { + return "[" + string.Join(",", Elements.Select(o => o.ToString())) + "]"; + } + } +} diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs index 19ca1ea..a2f6d2e 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs @@ -5,30 +5,19 @@ namespace Serilog.Filters.Expressions.Ast { class FilterCallExpression : FilterExpression { - readonly string _operatorName; - readonly FilterExpression[] _operands; - public FilterCallExpression(string operatorName, params FilterExpression[] operands) { - if (operatorName == null) throw new ArgumentNullException(nameof(operatorName)); - if (operands == null) throw new ArgumentNullException(nameof(operands)); - _operatorName = operatorName; - _operands = operands; + OperatorName = operatorName ?? throw new ArgumentNullException(nameof(operatorName)); + Operands = operands ?? throw new ArgumentNullException(nameof(operands)); } - public string OperatorName - { - get { return _operatorName; } - } + public string OperatorName { get; } - public FilterExpression[] Operands - { - get { return _operands; } - } + public FilterExpression[] Operands { get; } public override string ToString() { - if (OperatorName == "ElementAt" && Operands.Length == 2) + if (OperatorName == Operators.OpElementAt && Operands.Length == 2) { return Operands[0] + "[" + Operands[1] + "]"; } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs index c9deba9..8eba462 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs @@ -10,6 +10,7 @@ enum FilterExpressionType Subproperty, Wildcard, Parameter, - Lambda + Lambda, + Array } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs new file mode 100644 index 0000000..dbdbc2a --- /dev/null +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs @@ -0,0 +1,32 @@ +using System.Linq; +using Serilog.Events; +using Serilog.Filters.Expressions.Ast; +using Serilog.Filters.Expressions.Compilation.Transformations; +using Serilog.Filters.Expressions.Runtime; + +namespace Serilog.Filters.Expressions.Compilation.Arrays +{ + class FilterExpressionConstantArrayEvaluator : FilterExpressionIdentityTransformer + { + public static FilterExpression Evaluate(FilterExpression expression) + { + var evaluator = new FilterExpressionConstantArrayEvaluator(); + return evaluator.Transform(expression); + } + + protected override FilterExpression Transform(FilterArrayExpression ax) + { + // This should probably go depth-first. + + if (ax.Elements.All(el => el is FilterConstantExpression)) + { + return new FilterConstantExpression( + new SequenceValue(ax.Elements + .Cast() + .Select(ce => Representation.Recapture(ce.ConstantValue)))); + } + + return base.Transform(ax); + } + } +} diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs index 08caf65..81d38fa 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs @@ -1,6 +1,5 @@ using Serilog.Filters.Expressions.Ast; using Serilog.Filters.Expressions.Compilation.Transformations; -using Superpower; using System.Collections.Generic; using System.Linq; @@ -134,5 +133,10 @@ protected override FilterExpressionCosting Transform(FilterCallExpression lx) new FilterCallExpression(lx.OperatorName, operands.Select(o => o.Expression).ToArray()), operands.Sum(o => o.Costing) + 0.1); } + + protected override FilterExpressionCosting Transform(FilterArrayExpression ax) + { + return new FilterExpressionCosting(ax, 0.2); + } } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs index e0ecaea..dfd32b4 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs @@ -7,8 +7,10 @@ using Serilog.Filters.Expressions.Compilation.Text; using Serilog.Filters.Expressions.Compilation.Wildcards; using Serilog.Filters.Expressions.Compilation.Properties; -using Serilog.Serilog.Filters.Expressions.Runtime; +using Serilog.Filters.Expressions.Runtime; using System; +using Serilog.Filters.Expressions.Compilation.Arrays; +using Serilog.Filters.Expressions.Compilation.In; namespace Serilog.Filters.Expressions.Compilation { @@ -18,6 +20,8 @@ public static Func CompileAndExpose(FilterExpression expressio { var actual = expression; actual = PropertiesObjectAccessorTransformer.Rewrite(actual); + actual = FilterExpressionConstantArrayEvaluator.Evaluate(actual); + actual = FilterExpressionNotInRewriter.Rewrite(actual); actual = WildcardComprehensionTransformer.Expand(actual); actual = LikeOperatorTransformer.Rewrite(actual); actual = IsOperatorTransformer.Rewrite(actual); diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs new file mode 100644 index 0000000..1f5e033 --- /dev/null +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs @@ -0,0 +1,22 @@ +using Serilog.Filters.Expressions.Ast; +using Serilog.Filters.Expressions.Compilation.Transformations; + +namespace Serilog.Filters.Expressions.Compilation.In +{ + class FilterExpressionNotInRewriter : FilterExpressionIdentityTransformer + { + public static FilterExpression Rewrite(FilterExpression expression) + { + var rewriter = new FilterExpressionNotInRewriter(); + return rewriter.Transform(expression); + } + + protected override FilterExpression Transform(FilterCallExpression lx) + { + if (Operators.SameOperator(Operators.IntermediateOpSqlNotIn, lx.OperatorName)) + return new FilterCallExpression(Operators.RuntimeOpStrictNot, + new FilterCallExpression(Operators.RuntimeOpSqlIn, lx.Operands)); + return base.Transform(lx); + } + } +} diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs index e7e0674..c38836f 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs @@ -2,7 +2,6 @@ using Serilog.Filters.Expressions.Ast; using Serilog.Filters.Expressions.Compilation.Transformations; using Serilog.Filters.Expressions.Runtime; -using Serilog.Serilog.Filters.Expressions.Runtime; using System; using System.Collections.Generic; using System.Linq; @@ -161,7 +160,7 @@ protected override Expression Transform(FilterProperty if (px.IsBuiltIn) { if (px.PropertyName == "Level") - return context => context.Level.ToString() ?? "Information"; + return context => context.Level.ToString(); if (px.PropertyName == "Message") return context => NormalizeBaseDocumentProperty(context.RenderMessage(null)); @@ -257,5 +256,14 @@ protected override Expression Transform(FilterWildcard { return context => Undefined.Value; } + + protected override Expression Transform(FilterArrayExpression ax) + { + var context = Expression.Parameter(typeof(LogEvent)); + var elements = ax.Elements.Select(Transform).Select(ex => Splice(ex, context)).ToArray(); + var arr = Expression.NewArrayInit(typeof(object), elements); + var sv = Expression.Call(OperatorMethods[Operators.RuntimeOpNewSequence], arr); + return Expression.Lambda(Expression.Convert(sv, typeof(object)), context); + } } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs index 2dc7743..8f93618 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs @@ -47,5 +47,10 @@ protected override FilterExpression Transform(FilterWildcardExpression wx) { return wx; } + + protected override FilterExpression Transform(FilterArrayExpression ax) + { + return new FilterArrayExpression(ax.Elements.Select(Transform).ToArray()); + } } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs index dc0dff3..45e4c6a 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs @@ -25,6 +25,8 @@ public TResult Transform(FilterExpression expression) return Transform((FilterParameterExpression)expression); case FilterExpressionType.Wildcard: return Transform((FilterWildcardExpression)expression); + case FilterExpressionType.Array: + return Transform((FilterArrayExpression)expression); default: throw new ArgumentException(expression.Type + " is not a valid expression type"); } @@ -38,5 +40,6 @@ public TResult Transform(FilterExpression expression) protected abstract TResult Transform(FilterLambdaExpression lmx); protected abstract TResult Transform(FilterParameterExpression prx); protected abstract TResult Transform(FilterWildcardExpression wx); + protected abstract TResult Transform(FilterArrayExpression ax); } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs index 6ece0f2..cacd66e 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs @@ -59,5 +59,10 @@ protected override FilterCallExpression Transform(FilterWildcardExpression wx) // Must be RHS of ElementAt() return null; } + + protected override FilterCallExpression Transform(FilterArrayExpression ax) + { + return null; + } } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs index 464ddf7..11f6c23 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs @@ -3,6 +3,7 @@ using Serilog.Filters.Expressions.Compilation; using System; using System.Linq; +using Serilog.Filters.Expressions.Parsing; namespace Serilog.Filters.Expressions { diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs index fedd94d..f0690b4 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs @@ -62,8 +62,11 @@ static class Operators public const string IntermediateOpSqlLike = "_Internal_Like"; public const string IntermediateOpSqlNotLike = "_Internal_NotLike"; public const string IntermediateOpSqlIs = "_Internal_Is"; + public const string RuntimeOpSqlIn = "_Internal_In"; + public const string IntermediateOpSqlNotIn = "_Internal_NotIn"; public const string RuntimeOpStrictNot = "_Internal_StrictNot"; public const string OpSubstring = "Substring"; + public const string RuntimeOpNewSequence = "_Internal_NewSequence"; // Breaks the symmetry because there's no other way to express this in SQL. public const string OpIndexOfIgnoreCase = "IndexOfIgnoreCase"; @@ -81,6 +84,8 @@ static class Operators OpGreaterThanOrEqual, IntermediateOpSqlLike, IntermediateOpSqlNotLike, + RuntimeOpSqlIn, + IntermediateOpSqlNotIn, IntermediateOpSqlIs }; @@ -105,6 +110,8 @@ static class Operators OpGreaterThanOrEqual, IntermediateOpSqlLike, IntermediateOpSqlNotLike, + RuntimeOpSqlIn, + IntermediateOpSqlNotIn, IntermediateOpSqlIs }; diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs index 36a3b4d..52d3970 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs @@ -1,15 +1,11 @@ -using Serilog.Filters.Expressions.Ast; -using Serilog.Filters.Expressions.Parsing; +using System; +using Serilog.Filters.Expressions.Ast; using Superpower; -using System; -using System.Collections.Generic; -namespace Serilog.Filters.Expressions +namespace Serilog.Filters.Expressions.Parsing { static class FilterExpressionParser { - static readonly FilterExpressionTokenizer _tokenizer = new FilterExpressionTokenizer(); - public static FilterExpression Parse(string filterExpression) { FilterExpression root; @@ -24,9 +20,9 @@ public static bool TryParse(string filterExpression, out FilterExpression root, { if (filterExpression == null) throw new ArgumentNullException(nameof(filterExpression)); - var tokenList = _tokenizer.TryTokenize(filterExpression); - - var errorList = new List(); + var tokenizer = new FilterExpressionTokenizer(); + var tokenList = tokenizer.TryTokenize(filterExpression); + if (!tokenList.HasValue) { error = tokenList.ToString(); diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs index 28560a1..42b99b1 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs @@ -81,7 +81,10 @@ enum FilterExpressionToken [Token(Category = "keyword", Example = "and")] And, - + + [Token(Category = "keyword", Example = "in")] + In, + [Token(Category = "keyword", Example = "is")] Is, diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs index a5c29ba..f214aff 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs @@ -27,6 +27,8 @@ static class FilterExpressionTokenParsers static readonly TokenListParser Not = Token.EqualTo(FilterExpressionToken.Not).Value(Operators.OpNot); static readonly TokenListParser Like = Token.EqualTo(FilterExpressionToken.Like).Value(Operators.IntermediateOpSqlLike); static readonly TokenListParser NotLike = Not.IgnoreThen(Like).Value(Operators.IntermediateOpSqlNotLike); + static readonly TokenListParser In = Token.EqualTo(FilterExpressionToken.In).Value(Operators.RuntimeOpSqlIn); + static readonly TokenListParser NotIn = Not.IgnoreThen(In).Value(Operators.IntermediateOpSqlNotIn); static readonly TokenListParser Is = Token.EqualTo(FilterExpressionToken.Is).Value(Operators.IntermediateOpSqlIs); static readonly TokenListParser> PropertyPathStep = @@ -46,10 +48,16 @@ from close in Token.EqualTo(FilterExpressionToken.RBracket) static readonly TokenListParser Function = (from name in Token.EqualTo(FilterExpressionToken.Identifier) - from lparen in Token.EqualTo(FilterExpressionToken.LParen) - from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma)) + from lparen in Token.EqualTo(FilterExpressionToken.LParen) + from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma)) from rparen in Token.EqualTo(FilterExpressionToken.RParen) - select (FilterExpression)new FilterCallExpression(name.ToStringValue(), expr)).Named("function"); + select (FilterExpression)new FilterCallExpression(name.ToStringValue(), expr)).Named("function"); + + static readonly TokenListParser ArrayLiteral = + (from lbracket in Token.EqualTo(FilterExpressionToken.LBracket) + from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma)) + from rbracket in Token.EqualTo(FilterExpressionToken.RBracket) + select (FilterExpression)new FilterArrayExpression(expr)).Named("array"); static readonly TokenListParser RootProperty = Token.EqualTo(FilterExpressionToken.BuiltInIdentifier).Select(b => (FilterExpression)new FilterPropertyExpression(b.ToStringValue().Substring(1), true)) @@ -96,7 +104,7 @@ from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many() .Or(Token.EqualTo(FilterExpressionToken.Null).Value((FilterExpression)new FilterConstantExpression(null))) .Named("literal"); - static readonly TokenListParser Item = Literal.Or(PropertyPath).Or(Function); + static readonly TokenListParser Item = Literal.Or(PropertyPath).Or(Function).Or(ArrayLiteral); static readonly TokenListParser Factor = (from lparen in Token.EqualTo(FilterExpressionToken.LParen) @@ -116,7 +124,7 @@ from factor in Factor static readonly TokenListParser Comparand = Parse.Chain(Add.Or(Subtract), Term, MakeBinary); - static readonly TokenListParser Comparison = Parse.Chain(Is.Or(NotLike.Try().Or(Like)).Or(Lte.Or(Neq).Or(Lt)).Or(Gte.Or(Gt)).Or(Eq), Comparand, MakeBinary); + static readonly TokenListParser Comparison = Parse.Chain(Is.Or(NotLike.Try().Or(Like)).Or(NotIn.Try().Or(In)).Or(Lte.Or(Neq).Or(Lt)).Or(Gte.Or(Gt)).Or(Eq), Comparand, MakeBinary); static readonly TokenListParser Conjunction = Parse.Chain(And, Comparison, MakeBinary); diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs index 66a01da..2f02a4e 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs @@ -23,12 +23,14 @@ class FilterExpressionTokenizer : Tokenizer FilterExpressionToken.GreaterThanOrEqual, FilterExpressionToken.LessThan, FilterExpressionToken.LessThanOrEqual, + FilterExpressionToken.In, FilterExpressionToken.Is }; static readonly FilterExpressionKeyword[] SqlKeywords = { new FilterExpressionKeyword("and", FilterExpressionToken.And), + new FilterExpressionKeyword("in", FilterExpressionToken.In), new FilterExpressionKeyword("is", FilterExpressionToken.Is), new FilterExpressionKeyword("like", FilterExpressionToken.Like), new FilterExpressionKeyword("not", FilterExpressionToken.Not), diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs index 1225825..7b83c08 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs @@ -1,9 +1,8 @@ using Serilog.Events; -using Serilog.Filters.Expressions.Runtime; using System; using System.Linq; -namespace Serilog.Serilog.Filters.Expressions.Runtime +namespace Serilog.Filters.Expressions.Runtime { static class Representation { @@ -64,5 +63,10 @@ public static object Expose(object internalValue) return internalValue; } + + public static LogEventPropertyValue Recapture(object value) + { + return value is LogEventPropertyValue lepv ? lepv : new ScalarValue(value); + } } } diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs index 72be8f1..e9d9ffe 100644 --- a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs +++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs @@ -1,6 +1,5 @@ using Serilog.Events; using Serilog.Filters.Expressions.Compilation.Linq; -using Serilog.Serilog.Filters.Expressions.Runtime; using System; using System.Globalization; using System.Linq; @@ -94,16 +93,38 @@ public static object GreaterThan(object left, object right) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object GreaterThanOrEqual(object left, object right) { - return ((decimal)left) >= ((decimal)right); + return (decimal)left >= (decimal)right; } [AcceptNull] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object Equal(object left, object right) { - return left == null ? right == null : left.Equals(right); + return UnboxedEqualHelper(left, right); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool UnboxedEqualHelper(object left, object right) + { + return left?.Equals(right) ?? right == null; + } + + [AcceptNull] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object _Internal_In(object item, object collection) + { + if (collection is SequenceValue arr) + { + for (var i = 0; i < arr.Elements.Count; ++i) + if (UnboxedEqualHelper(Representation.Represent(arr.Elements[i]), item)) + return true; + + return false; + } + + return Undefined.Value; + } + [AcceptNull] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object _Internal_EqualIgnoreCase(object left, object right) @@ -138,7 +159,7 @@ public static object _Internal_EqualPattern(object left, object right) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object NotEqual(object left, object right) { - return !(bool)Equal(left, right); + return !UnboxedEqualHelper(left, right); } [AcceptNull] @@ -252,12 +273,15 @@ public static object _Internal_IndexOfPattern(object corpus, object pattern) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object Length(object corpus) + public static object Length(object arg) { - var ctx = corpus as string; - if (ctx == null) - return Undefined.Value; - return (decimal)ctx.Length; + if (arg is string str) + return (decimal)str.Length; + + if (arg is SequenceValue seq) + return (decimal) seq.Elements.Count; + + return Undefined.Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -335,8 +359,7 @@ public static object Has(object value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object ElementAt(object items, object index) { - var arr = items as SequenceValue; - if (arr != null) + if (items is SequenceValue arr) { if (!(index is decimal)) return Undefined.Value; @@ -346,14 +369,13 @@ public static object ElementAt(object items, object index) return Undefined.Value; var idx = (int)dec; - if (idx >= arr.Elements.Count()) + if (idx >= arr.Elements.Count) return Undefined.Value; return Representation.Represent(arr.Elements.ElementAt(idx)); } - - var dict = items as StructureValue; - if (dict != null) + + if (items is StructureValue dict) { var s = index as string; if (s == null) @@ -366,24 +388,23 @@ public static object ElementAt(object items, object index) return Representation.Represent(value); } + // Case for DictionaryValue is missing, here. + return Undefined.Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object _Internal_Any(object items, object predicate) { - var pred = predicate as Func; - if (pred == null) + if (!(predicate is Func pred)) return Undefined.Value; - SequenceValue arr = items as SequenceValue; - if (arr != null) + if (items is SequenceValue arr) { return arr.Elements.Any(e => true.Equals(pred(Representation.Represent(e)))); } - var structure = items as StructureValue; - if (structure != null) + if (items is StructureValue structure) { return structure.Properties.Any(e => true.Equals(pred(Representation.Represent(e.Value)))); } @@ -413,14 +434,35 @@ public static object _Internal_All(object items, object predicate) return Undefined.Value; } + [AcceptNull, AcceptUndefined] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static object TypeOf(object value) { - var dict = value as StructureValue; - if (dict == null) - return Undefined.Value; + if (value is DictionaryValue) + return "object"; // Follow the JSON system here + + if (value is StructureValue structure) + return structure.TypeTag ?? "object"; - return dict.TypeTag; + if (value is SequenceValue) + return "array"; + + if (value is string) + return "string"; + + if (value is decimal) + return "number"; + + if (value is bool) + return "boolean"; + + if (value is Undefined) + return "undefined"; + + if (value == null) + return "null"; + + return Undefined.Value; } [AcceptUndefined, AcceptNull] @@ -472,5 +514,14 @@ public static object Substring(object sval, object startIndex, object length) return str.Substring((int)si, (int)len); } + + // This helper method is not called as an operator; rather, we use it + // to avoid uglier LINQ expression code to create array literals in + // `LinqExpressionCompiler`. + public static SequenceValue _Internal_NewSequence(object[] arr) + { + if (arr == null) throw new ArgumentNullException(nameof(arr)); + return new SequenceValue(arr.Select(Representation.Recapture)); + } } } diff --git a/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.csproj b/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.csproj new file mode 100644 index 0000000..6b3f67a --- /dev/null +++ b/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.csproj @@ -0,0 +1,33 @@ + + + + Expression-based event filtering for Serilog. + 1.1.0 + Serilog Contributors + net45;netstandard1.5 + true + true + Serilog.Filters.Expressions + Serilog + ../../assets/Serilog.snk + true + true + Serilog.Filters.Expressions + serilog + http://serilog.net/images/serilog-extension-nuget.png + https://github.com/serilog/serilog-filters-expressions + http://www.apache.org/licenses/LICENSE-2.0 + 1.6.0 + + + + + + + + + + + + + diff --git a/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.xproj b/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.xproj deleted file mode 100644 index 286539a..0000000 --- a/src/Serilog.Filters.Expressions/Serilog.Filters.Expressions.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - a420c4e3-3a2d-4369-88eb-77e4db1d0219 - Serilog - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Serilog.Filters.Expressions/project.json b/src/Serilog.Filters.Expressions/project.json deleted file mode 100644 index d634978..0000000 --- a/src/Serilog.Filters.Expressions/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "1.0.1-*", - "description": "Expression-based event filtering for Serilog.", - "authors": [ "Serilog Contributors" ], - "packOptions": { - "tags": [ "serilog" ], - "projectUrl": "https://github.com/serilog/serilog-filters-expressions", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "iconUrl": "http://serilog.net/images/serilog-extension-nuget.png" - }, - "dependencies": { - "Serilog": "2.3.0", - "Superpower": "1.0.1" - }, - "buildOptions": { - "keyFile": "../../assets/Serilog.snk", - "xmlDoc": true, - "warningsAsErrors": true - }, - "frameworks": { - "net4.5": {}, - "netstandard1.5": { - "dependencies": { - "NETStandard.Library": "1.6.0" - } - } - } -} diff --git a/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.csproj b/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.csproj new file mode 100644 index 0000000..5601665 --- /dev/null +++ b/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp1.1;net462 + Serilog.Filters.Expressions.PerformanceTests + ../../assets/Serilog.snk + true + true + Serilog.Filters.Expressions.PerformanceTests + true + + + + + + + + + + + + + + + + + + diff --git a/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.xproj b/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.xproj deleted file mode 100644 index 69b266f..0000000 --- a/test/Serilog.Filters.Expressions.PerformanceTests/Serilog.Filters.Expressions.PerformanceTests.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - d7a37f73-bba3-4dae-9648-1a753a86f968 - Serilog.Filters.Expressions.PerformanceTests - .\obj - .\bin\ - - - 2.0 - - - - - - \ No newline at end of file diff --git a/test/Serilog.Filters.Expressions.PerformanceTests/project.json b/test/Serilog.Filters.Expressions.PerformanceTests/project.json deleted file mode 100644 index dbb24f8..0000000 --- a/test/Serilog.Filters.Expressions.PerformanceTests/project.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "testRunner": "xunit", - - "dependencies": { - "Serilog.Filters.Expressions": { "target": "project" }, - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-rc2-build10025", - "BenchmarkDotNet": "0.9.9" - }, - "buildOptions": { - "keyFile": "../../assets/Serilog.snk" - }, - "frameworks": { - "netcoreapp1.0": { - "dependencies": { - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.0" - }, - "System.Collections": "4.0.11" - }, - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - }, - "net4.5.2": { - } - } -} diff --git a/test/Serilog.Filters.Expressions.Tests/FilterExpressionCompilerTests.cs b/test/Serilog.Filters.Expressions.Tests/FilterExpressionCompilerTests.cs index 95424ef..105ab83 100644 --- a/test/Serilog.Filters.Expressions.Tests/FilterExpressionCompilerTests.cs +++ b/test/Serilog.Filters.Expressions.Tests/FilterExpressionCompilerTests.cs @@ -3,6 +3,8 @@ using System.Linq; using Xunit; +// ReSharper disable CoVariantArrayConversion + namespace Serilog.Filters.Expressions.Tests { public class FilterExpressionCompilerTests @@ -105,6 +107,32 @@ public void FilterExpressionsEvaluateSubproperties() Some.InformationEvent("Checking out {@Cart}", new { Total = 5 })); } + + [Fact] + public void SequenceLengthCanBeDetermined() + { + AssertFiltering("length(Items) > 1", + Some.InformationEvent("Checking out {Items}", new object[] { new[] { "pears", "apples" }}), + Some.InformationEvent("Checking out {Items}", new object[] { new[] { "pears" }})); + } + + [Fact] + public void InMatchesLiterals() + { + AssertFiltering("@Level in ['Warning', 'Error']", + Some.LogEvent(LogEventLevel.Error, "Hello"), + Some.InformationEvent("Hello")); + } + + [Fact] + public void InExaminesSequenceValues() + { + AssertFiltering("5 not in Numbers", + Some.InformationEvent("{Numbers}", new object[] {new []{1, 2, 3}}), + Some.InformationEvent("{Numbers}", new object[] { new [] { 1, 5, 3 }}), + Some.InformationEvent()); + } + static void AssertFiltering(string expression, LogEvent match, params LogEvent[] noMatches) { var sink = new CollectingSink(); @@ -119,7 +147,7 @@ static void AssertFiltering(string expression, LogEvent match, params LogEvent[] log.Write(match); - Assert.Equal(1, sink.Events.Count); + Assert.Single(sink.Events); Assert.Same(match, sink.Events.Single()); } } diff --git a/test/Serilog.Filters.Expressions.Tests/FilterExpressionParserTests.cs b/test/Serilog.Filters.Expressions.Tests/FilterExpressionParserTests.cs index 5a5f559..9c0c58c 100644 --- a/test/Serilog.Filters.Expressions.Tests/FilterExpressionParserTests.cs +++ b/test/Serilog.Filters.Expressions.Tests/FilterExpressionParserTests.cs @@ -1,4 +1,5 @@ using System; +using Serilog.Filters.Expressions.Parsing; using Xunit; namespace Serilog.Filters.Expressions.Tests @@ -30,11 +31,13 @@ public class FilterExpressionParserTests [InlineData("equal(AProperty[*].Description,1)", null)] [InlineData("equal(AProperty[ * ].Description,1)", "equal(AProperty[*].Description,1)")] [InlineData("AProperty like '%foo'", "_Internal_Like(AProperty,@\"%foo\")")] - [InlineData("A is null", "_Internal_Is(A,null)")] + [InlineData("AProperty not like '%foo'", "_Internal_NotLike(AProperty,@\"%foo\")")] [InlineData("A is null", "_Internal_Is(A,null)")] [InlineData("A IS NOT NULL", "_Internal_Is(A,Not(null))")] [InlineData("A is not null or B", "Or(_Internal_Is(A,Not(null)),B)")] [InlineData("@EventType = 0xC0ffee", "Equal(@EventType,12648430)")] + [InlineData("@Level in ['Error', 'Warning']", "_Internal_In(@Level,[@\"Error\",@\"Warning\"])")] + [InlineData("5 not in [1, 2]", "_Internal_NotIn(5,[1,2])")] public void ValidSyntaxIsAccepted(string input, string expected = null) { var roundTrip = FilterExpressionParser.Parse(input).ToString(); diff --git a/test/Serilog.Filters.Expressions.Tests/LoggingFilterSwitchTests.cs b/test/Serilog.Filters.Expressions.Tests/LoggingFilterSwitchTests.cs index 4a0b2be..121fbf0 100644 --- a/test/Serilog.Filters.Expressions.Tests/LoggingFilterSwitchTests.cs +++ b/test/Serilog.Filters.Expressions.Tests/LoggingFilterSwitchTests.cs @@ -26,7 +26,7 @@ public void WhenTheFilterExpressionIsModifiedTheFilterChanges() @switch.Expression = "Volume > 12"; log.Write(v11); - Assert.Equal(0, sink.Events.Count); + Assert.Empty(sink.Events); @switch.Expression = "Volume > 10"; diff --git a/test/Serilog.Filters.Expressions.Tests/PackagingTests.cs b/test/Serilog.Filters.Expressions.Tests/PackagingTests.cs new file mode 100644 index 0000000..07ba975 --- /dev/null +++ b/test/Serilog.Filters.Expressions.Tests/PackagingTests.cs @@ -0,0 +1,15 @@ +using System.Reflection; +using Xunit; + +namespace Serilog.Filters.Expressions.Tests +{ + public class PackagingTests + { + [Fact] + public void AssemblyVersionIsSet() + { + var version = typeof(LoggerFilterConfigurationExtensions).GetTypeInfo().Assembly.GetName().Version; + Assert.Equal("1", version.ToString(1)); + } + } +} diff --git a/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.csproj b/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.csproj new file mode 100644 index 0000000..ded4f51 --- /dev/null +++ b/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.csproj @@ -0,0 +1,37 @@ + + + + netcoreapp1.0;net452 + true + Serilog.Filters.Expressions.Tests + ../../assets/Serilog.snk + true + true + Serilog.Filters.Expressions.Tests + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.xproj b/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.xproj deleted file mode 100644 index c9b738b..0000000 --- a/test/Serilog.Filters.Expressions.Tests/Serilog.Filters.Expressions.Tests.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 3c2d8e01-5580-426a-bdd9-ec59cd98e618 - Serilog.Filters.Expressions.Tests - .\obj - .\bin\ - - - 2.0 - - - - - - \ No newline at end of file diff --git a/test/Serilog.Filters.Expressions.Tests/project.json b/test/Serilog.Filters.Expressions.Tests/project.json deleted file mode 100644 index b84bc49..0000000 --- a/test/Serilog.Filters.Expressions.Tests/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "testRunner": "xunit", - "dependencies": { - "Serilog.Filters.Expressions": { "target": "project" }, - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-rc2-build10025" - }, - "buildOptions": { - "keyFile": "../../assets/Serilog.snk", - "warningsAsErrors": true - }, - "frameworks": { - "netcoreapp1.0": { - "dependencies": { - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.0" - } - }, - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - }, - "net4.5.2": { - } - } -}