Skip to content

Commit

Permalink
Struct alternative to LogEventProperty; optimizations (serilog#1315)
Browse files Browse the repository at this point in the history
Includes a .NET SDK and AppVeyor build image update.
  • Loading branch information
nblumhardt authored and Twinki14 committed Dec 30, 2023
1 parent 3b31c78 commit ab4b0cc
Show file tree
Hide file tree
Showing 33 changed files with 381 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -331,3 +331,4 @@ ASALocalRun/

# MFractors (Xamarin productivity tool) working folder
.mfractor/
*.orig
2 changes: 1 addition & 1 deletion BuildNativeUWP.ps1
Expand Up @@ -5,7 +5,7 @@ param(

echo "build: running .NET Native toolchain for UWP test project"

$msbuild = $(cmd /c where /R 'C:\Program Files (x86)\Microsoft Visual Studio\2017' msbuild.exe | select -First 1)
$msbuild = $(cmd /c where /R 'C:\Program Files (x86)\Microsoft Visual Studio\2019' msbuild.exe | select -First 1)

if ($customLogger) {
& $msbuild $proj /logger:"$customLogger" /verbosity:minimal /t:Restore
Expand Down
2 changes: 2 additions & 0 deletions Serilog.sln
Expand Up @@ -8,8 +8,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5
appveyor.yml = appveyor.yml
Build.ps1 = Build.ps1
build.sh = build.sh
BuildNativeUWP.ps1 = BuildNativeUWP.ps1
CHANGES.md = CHANGES.md
Directory.Build.props = Directory.Build.props
global.json = global.json
NuGet.config = NuGet.config
README.md = README.md
run_perf_tests.sh = run_perf_tests.sh
Expand Down
8 changes: 7 additions & 1 deletion Serilog.sln.DotSettings
Expand Up @@ -543,6 +543,7 @@ II.2.12 <HandlesEvent />
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -556,4 +557,9 @@ II.2.12 &lt;HandlesEvent /&gt;&#xD;


<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue">&lt;data /&gt;</s:String>
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cacheable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=destructure/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enrichers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
10 changes: 10 additions & 0 deletions Setup.ps1
@@ -0,0 +1,10 @@
$ErrorActionPreference = "Stop"

$RequiredDotnetVersion = $(cat ./global.json | convertfrom-json).sdk.version

mkdir "./build"
Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./build/installcli.ps1"
& ./build/installcli.ps1 -InstallDir "$pwd/.dotnetcli" -NoPath -Version $RequiredDotnetVersion
if ($LASTEXITCODE) { exit 1 }

$env:Path = "$pwd/.dotnetcli;$env:Path"
10 changes: 7 additions & 3 deletions appveyor.yml
@@ -1,18 +1,22 @@
version: '{build}'
skip_tags: true
image:
- Visual Studio 2017
- Ubuntu
- Visual Studio 2019
- Ubuntu1804
configuration: Release
test: off
install:
- ps: ./Setup.ps1
build_script:
- ps: ./Build.ps1
- ps: ./BuildNativeUWP.ps1
for:
-
matrix:
only:
- image: Ubuntu
- image: Ubuntu1804
install:
- sh setup.sh
build_script:
- sh build.sh
artifacts:
Expand Down
8 changes: 6 additions & 2 deletions build.sh
@@ -1,6 +1,9 @@
#!/bin/bash

set -e

export PATH=$HOME/.dotnetcli:$PATH

dotnet --info
dotnet --list-sdks
dotnet restore
Expand All @@ -9,13 +12,14 @@ echo "🤖 Attempting to build..."
for path in src/**/*.csproj; do
dotnet build -f netstandard1.0 -c Release ${path}
dotnet build -f netstandard1.3 -c Release ${path}
dotnet build -f netstandard2.0 -c Release ${path}
done

echo "🤖 Running tests..."
for path in test/*.Tests/*.csproj; do
dotnet test -f netcoreapp2.0 -c Release ${path}
dotnet test -f netcoreapp2.2 -c Release ${path}
done

for path in test/*.PerformanceTests/*.PerformanceTests.csproj; do
dotnet build -f netcoreapp2.0 -c Release ${path}
dotnet build -f netcoreapp2.2 -c Release ${path}
done
2 changes: 1 addition & 1 deletion global.json
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "2.1.4"
"version": "2.2.300"
}
}
7 changes: 7 additions & 0 deletions setup.sh
@@ -0,0 +1,7 @@
#!/bin/bash

sudo apt-get update && sudo apt-get install -y --no-install-recommends jq

RequiredDotnetVersion=$(jq -r '.sdk.version' global.json)

curl https://dot.net/v1/dotnet-install.sh -sSf | bash -s -- --install-dir $HOME/.dotnetcli --no-path --version $RequiredDotnetVersion
8 changes: 6 additions & 2 deletions src/Serilog/Capturing/MessageTemplateProcessor.cs
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections.Generic;
using Serilog.Core;
using Serilog.Core.Pipeline;
using Serilog.Events;
Expand All @@ -32,7 +31,7 @@ public MessageTemplateProcessor(PropertyValueConverter propertyValueConverter)
_propertyBinder = new PropertyBinder(_propertyValueConverter);
}

public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out IEnumerable<LogEventProperty> properties)
public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties)
{
parsedTemplate = _parser.Parse(messageTemplate);
properties = _propertyBinder.ConstructProperties(parsedTemplate, messageTemplateParameters);
Expand All @@ -42,5 +41,10 @@ public LogEventProperty CreateProperty(string name, object value, bool destructu
{
return _propertyValueConverter.CreateProperty(name, value, destructureObjects);
}

public LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects = false)
{
return _propertyValueConverter.CreatePropertyValue(value, destructureObjects);
}
}
}
24 changes: 11 additions & 13 deletions src/Serilog/Capturing/PropertyBinder.cs
Expand Up @@ -13,8 +13,6 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using Serilog.Debugging;
using Serilog.Events;
using Serilog.Parsing;
Expand All @@ -26,7 +24,7 @@ class PropertyBinder
{
readonly PropertyValueConverter _valueConverter;

static readonly LogEventProperty[] NoProperties = new LogEventProperty[0];
static readonly EventProperty[] NoProperties = new EventProperty[0];

public PropertyBinder(PropertyValueConverter valueConverter)
{
Expand All @@ -41,7 +39,7 @@ public PropertyBinder(PropertyValueConverter valueConverter)
/// represented in the message template.</param>
/// <returns>A list of properties; if the template is malformed then
/// this will be empty.</returns>
public IEnumerable<LogEventProperty> ConstructProperties(MessageTemplate messageTemplate, object[] messageTemplateParameters)
public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, object[] messageTemplateParameters)
{
if (messageTemplateParameters == null || messageTemplateParameters.Length == 0)
{
Expand All @@ -57,14 +55,14 @@ public IEnumerable<LogEventProperty> ConstructProperties(MessageTemplate message
return ConstructNamedProperties(messageTemplate, messageTemplateParameters);
}

IEnumerable<LogEventProperty> ConstructPositionalProperties(MessageTemplate template, object[] messageTemplateParameters)
EventProperty[] ConstructPositionalProperties(MessageTemplate template, object[] messageTemplateParameters)
{
var positionalProperties = template.PositionalProperties;

if (positionalProperties.Length != messageTemplateParameters.Length)
SelfLog.WriteLine("Positional property count does not match parameter count: {0}", template);

var result = new LogEventProperty[messageTemplateParameters.Length];
var result = new EventProperty[messageTemplateParameters.Length];
foreach (var property in positionalProperties)
{
if (property.TryGetPositionalValue(out int position))
Expand All @@ -79,7 +77,7 @@ IEnumerable<LogEventProperty> ConstructPositionalProperties(MessageTemplate temp
var next = 0;
for (var i = 0; i < result.Length; ++i)
{
if (result[i] != null)
if (!result[i].Equals(EventProperty.None))
{
result[next] = result[i];
++next;
Expand All @@ -92,11 +90,11 @@ IEnumerable<LogEventProperty> ConstructPositionalProperties(MessageTemplate temp
return result;
}

IEnumerable<LogEventProperty> ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
EventProperty[] ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
{
var namedProperties = template.NamedProperties;
if (namedProperties == null)
return Enumerable.Empty<LogEventProperty>();
return NoProperties;

var matchedRun = namedProperties.Length;
if (namedProperties.Length != messageTemplateParameters.Length)
Expand All @@ -105,7 +103,7 @@ IEnumerable<LogEventProperty> ConstructNamedProperties(MessageTemplate template,
SelfLog.WriteLine("Named property count does not match parameter count: {0}", template);
}

var result = new LogEventProperty[messageTemplateParameters.Length];
var result = new EventProperty[messageTemplateParameters.Length];
for (var i = 0; i < matchedRun; ++i)
{
var property = template.NamedProperties[i];
Expand All @@ -116,14 +114,14 @@ IEnumerable<LogEventProperty> ConstructNamedProperties(MessageTemplate template,
for (var i = matchedRun; i < messageTemplateParameters.Length; ++i)
{
var value = _valueConverter.CreatePropertyValue(messageTemplateParameters[i]);
result[i] = new LogEventProperty("__" + i, value);
result[i] = new EventProperty("__" + i, value);
}
return result;
}

LogEventProperty ConstructProperty(PropertyToken propertyToken, object value)
EventProperty ConstructProperty(PropertyToken propertyToken, object value)
{
return new LogEventProperty(
return new EventProperty(
propertyToken.PropertyName,
_valueConverter.CreatePropertyValue(value, propertyToken.Destructuring));
}
Expand Down
10 changes: 7 additions & 3 deletions src/Serilog/Capturing/PropertyValueConverter.cs
Expand Up @@ -133,9 +133,6 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
return Stringify(value);
}

var valueType = value.GetType();
DepthLimiter.SetCurrentDepth(depth);

if (destructuring == Destructuring.Destructure)
{
if (value is string stringValue)
Expand All @@ -144,12 +141,17 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
}
}

if (value is string)
return new ScalarValue(value);

foreach (var scalarConversionPolicy in _scalarConversionPolicies)
{
if (scalarConversionPolicy.TryConvertToScalar(value, out var converted))
return converted;
}

DepthLimiter.SetCurrentDepth(depth);

if (destructuring == Destructuring.Destructure)
{
foreach (var destructuringPolicy in _destructuringPolicies)
Expand All @@ -159,6 +161,8 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
}
}

var valueType = value.GetType();

if (TryConvertEnumerable(value, destructuring, valueType, out var enumerableResult))
return enumerableResult;

Expand Down
11 changes: 6 additions & 5 deletions src/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
Expand Up @@ -19,17 +19,18 @@ namespace Serilog.Core.Enrichers
{
class FixedPropertyEnricher : ILogEventEnricher
{
readonly LogEventProperty _logEventProperty;
readonly EventProperty _eventProperty;

public FixedPropertyEnricher(LogEventProperty logEventProperty)
public FixedPropertyEnricher(in EventProperty eventProperty)
{
_logEventProperty = logEventProperty ?? throw new ArgumentNullException(nameof(logEventProperty));
if (eventProperty.Equals(EventProperty.None)) throw new ArgumentNullException(nameof(eventProperty));
_eventProperty = eventProperty;
}

public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
logEvent.AddPropertyIfAbsent(_logEventProperty);
logEvent.AddPropertyIfAbsent(_eventProperty);
}
}
}
}
17 changes: 11 additions & 6 deletions src/Serilog/Core/Logger.cs
Expand Up @@ -14,6 +14,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Serilog.Capturing;
using Serilog.Core.Enrichers;
using Serilog.Core.Pipeline;
Expand All @@ -32,6 +33,7 @@ namespace Serilog.Core
public sealed class Logger : ILogger, ILogEventSink, IDisposable
{
static readonly object[] NoPropertyValues = new object[0];
static readonly LogEventProperty[] NoProperties = new LogEventProperty[0];

readonly MessageTemplateProcessor _messageTemplateProcessor;
readonly ILogEventSink _sink;
Expand Down Expand Up @@ -141,10 +143,9 @@ public ILogger ForContext(string propertyName, object value, bool destructureObj
}

// It'd be nice to do the destructuring lazily, but unfortunately `value` may be mutated between
// now and the first log event written...
// A future optimization opportunity may be to implement ILogEventEnricher on LogEventProperty to
// remove one more allocation.
var enricher = new FixedPropertyEnricher(_messageTemplateProcessor.CreateProperty(propertyName, value, destructureObjects));
// now and the first log event written.
var propertyValue = _messageTemplateProcessor.CreatePropertyValue(value, destructureObjects);
var enricher = new FixedPropertyEnricher(new EventProperty(propertyName, propertyValue));

var minimumLevel = _minimumLevel;
var levelSwitch = _levelSwitch;
Expand Down Expand Up @@ -367,7 +368,7 @@ public void Write(LogEventLevel level, Exception exception, string messageTempla
propertyValues.GetType() != typeof(object[]))
propertyValues = new object[] { propertyValues };

_messageTemplateProcessor.Process(messageTemplate, propertyValues, out MessageTemplate parsedTemplate, out IEnumerable<LogEventProperty> boundProperties);
_messageTemplateProcessor.Process(messageTemplate, propertyValues, out MessageTemplate parsedTemplate, out var boundProperties);

var logEvent = new LogEvent(DateTimeOffset.Now, level, exception, parsedTemplate, boundProperties);
Dispatch(logEvent);
Expand Down Expand Up @@ -1333,7 +1334,11 @@ public bool BindMessageTemplate(string messageTemplate, object[] propertyValues,
return false;
}

_messageTemplateProcessor.Process(messageTemplate, propertyValues, out parsedTemplate, out boundProperties);
_messageTemplateProcessor.Process(messageTemplate, propertyValues, out parsedTemplate, out var boundEventProperties);
boundProperties = boundEventProperties.Length == 0 ?
NoProperties :
boundEventProperties.Select(p => new LogEventProperty(p));

return true;
}

Expand Down
8 changes: 1 addition & 7 deletions src/Serilog/Core/Sinks/SecondaryLoggerSink.cs
Expand Up @@ -40,13 +40,7 @@ public void Emit(LogEvent logEvent)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));

var copy = new LogEvent(
logEvent.Timestamp,
logEvent.Level,
logEvent.Exception,
logEvent.MessageTemplate,
logEvent.Properties.Select(p => new LogEventProperty(p.Key, p.Value)));

var copy = logEvent.Copy();
_logger.Write(copy);
}

Expand Down

0 comments on commit ab4b0cc

Please sign in to comment.