Dependancy Update: Switch to Serilog.Expressions away from deprecated Serilog.Filters.Expressions (#12000)
* Uninstall Serilog.Filters.Expressions as it is deprecated by the Serilog team NOTE: This package brought in a dependenacy on a package called SuperPower from Datalust * Install replacement package Serilog.Expressions * Update implementation of Serilog.Expressions * Add new test cases to verify expressions still working Currently Serilog.Expressions only supports short names such as @x @l @m and @mt as opposed @Exception @Level @Message and @MessageTemplate * Use Serilog.Expressions NamedFilters extension point, to plug the missing Has() function in expressions that was not ported across by the library * Update to dev build of Serilog.Expressions to verify new method we can override works * Update to release build of Serilog.Expressions 3.3.0 now its been pushed to Nuget
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Serilog.Events;
|
||||
using Serilog.Filters.Expressions;
|
||||
using Serilog.Expressions;
|
||||
using Umbraco.Cms.Infrastructure.Logging.Viewer;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Logging.Viewer
|
||||
@@ -10,32 +11,43 @@ namespace Umbraco.Cms.Core.Logging.Viewer
|
||||
internal class ExpressionFilter : ILogFilter
|
||||
{
|
||||
private readonly Func<LogEvent, bool> _filter;
|
||||
private const string ExpressionOperators = "()+=*<>%-";
|
||||
private const string s_expressionOperators = "()+=*<>%-";
|
||||
|
||||
public ExpressionFilter(string filterExpression)
|
||||
{
|
||||
Func<LogEvent, bool> filter;
|
||||
|
||||
// Our custom Serilog Functions to extend Serilog.Expressions
|
||||
// In this case we are plugging the gap for the missing Has()
|
||||
// function from porting away from Serilog.Filters.Expressions to Serilog.Expressions
|
||||
// Along with patching support for the more verbose built in property names
|
||||
var customSerilogFunctions = new SerilogLegacyNameResolver(typeof(SerilogExpressionsFunctions));
|
||||
|
||||
if (string.IsNullOrEmpty(filterExpression))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the expression is one word and doesn't contain a serilog operator then we can perform a like search
|
||||
if (!filterExpression.Contains(" ") && !filterExpression.ContainsAny(ExpressionOperators.Select(c => c)))
|
||||
if (!filterExpression.Contains(" ") && !filterExpression.ContainsAny(s_expressionOperators.Select(c => c)))
|
||||
{
|
||||
filter = PerformMessageLikeFilter(filterExpression);
|
||||
}
|
||||
else // check if it's a valid expression
|
||||
{
|
||||
// If the expression evaluates then make it into a filter
|
||||
if (FilterLanguage.TryCreateFilter(filterExpression, out var eval, out _))
|
||||
if (SerilogExpression.TryCompile(filterExpression, null, customSerilogFunctions, out CompiledExpression compiled, out var error))
|
||||
{
|
||||
filter = evt => true.Equals(eval(evt));
|
||||
filter = evt =>
|
||||
{
|
||||
LogEventPropertyValue result = compiled(evt);
|
||||
return ExpressionResult.IsTrue(result);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
//Assume the expression was a search string and make a Like filter from that
|
||||
// 'error' describes a syntax error, where it was unable to compile an expression
|
||||
// Assume the expression was a search string and make a Like filter from that
|
||||
filter = PerformMessageLikeFilter(filterExpression);
|
||||
}
|
||||
}
|
||||
@@ -50,10 +62,15 @@ namespace Umbraco.Cms.Core.Logging.Viewer
|
||||
|
||||
private Func<LogEvent, bool> PerformMessageLikeFilter(string filterExpression)
|
||||
{
|
||||
var filterSearch = $"@Message like '%{FilterLanguage.EscapeLikeExpressionContent(filterExpression)}%'";
|
||||
if (FilterLanguage.TryCreateFilter(filterSearch, out var eval, out _))
|
||||
var filterSearch = $"@Message like '%{SerilogExpression.EscapeLikeExpressionContent(filterExpression)}%'";
|
||||
if (SerilogExpression.TryCompile(filterSearch, out CompiledExpression compiled, out var error))
|
||||
{
|
||||
return evt => true.Equals(eval(evt));
|
||||
// `compiled` is a function that can be executed against `LogEvent`s:
|
||||
return evt =>
|
||||
{
|
||||
LogEventPropertyValue result = compiled(evt);
|
||||
return ExpressionResult.IsTrue(result);
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Logging.Viewer
|
||||
{
|
||||
public class SerilogExpressionsFunctions
|
||||
{
|
||||
// This Has() code is the same as the renamed IsDefined() function
|
||||
// Added this to help backport and ensure saved queries continue to work if using Has()
|
||||
public static LogEventPropertyValue? Has(LogEventPropertyValue? value)
|
||||
{
|
||||
return new ScalarValue(value != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Serilog.Expressions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Logging.Viewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherits Serilog's StaticMemberNameResolver to ensure we get same functionality
|
||||
/// Of easily allowing any static methods definied in the passed in class/type
|
||||
/// To extend as functions to use for filtering logs such as Has() and any other custom ones
|
||||
/// </summary>
|
||||
public class SerilogLegacyNameResolver : StaticMemberNameResolver
|
||||
{
|
||||
public SerilogLegacyNameResolver(Type type) : base(type)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows us to fix the gap from migrating away from Serilog.Filters.Expressions
|
||||
/// So we can still support the more verbose built in property names such as
|
||||
/// Exception, Level, MessageTemplate etc
|
||||
/// </summary>
|
||||
public override bool TryResolveBuiltInPropertyName(string alias, out string target)
|
||||
{
|
||||
target = alias switch
|
||||
{
|
||||
"Exception" => "x",
|
||||
"Level" => "l",
|
||||
"Message" => "m",
|
||||
"MessageTemplate" => "mt",
|
||||
"Properties" => "p",
|
||||
"Timestamp" => "t",
|
||||
_ => null
|
||||
};
|
||||
|
||||
return target != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Filters.Expressions" Version="2.1.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="1.0.5" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
|
||||
|
||||
@@ -186,10 +186,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Logging
|
||||
|
||||
[TestCase("", 102)]
|
||||
[TestCase("Has(@Exception)", 1)]
|
||||
[TestCase("Has(@x)", 1)]
|
||||
[TestCase("Has(Duration) and Duration > 1000", 2)]
|
||||
[TestCase("Not(@Level = 'Verbose') and Not(@Level= 'Debug')", 45)]
|
||||
[TestCase("Not(@Level = 'Verbose') and Not(@Level = 'Debug')", 45)]
|
||||
[TestCase("Not(@l = 'Verbose') and Not(@l = 'Debug')", 45)]
|
||||
[TestCase("StartsWith(SourceContext, 'Umbraco.Core')", 86)]
|
||||
[TestCase("@MessageTemplate = '{EndMessage} ({Duration}ms) [Timing {TimingId}]'", 26)]
|
||||
[TestCase("@mt = '{EndMessage} ({Duration}ms) [Timing {TimingId}]'", 26)]
|
||||
[TestCase("SortedComponentTypes[?] = 'Umbraco.Web.Search.ExamineComponent'", 1)]
|
||||
[TestCase("Contains(SortedComponentTypes[?], 'DatabaseServer')", 1)]
|
||||
[Test]
|
||||
|
||||
Reference in New Issue
Block a user