diff --git a/.gitignore b/.gitignore
index bba82e6fb..c88ba5dc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -331,3 +331,4 @@ ASALocalRun/
# MFractors (Xamarin productivity tool) working folder
.mfractor/
+*.orig
diff --git a/BuildNativeUWP.ps1 b/BuildNativeUWP.ps1
index 3def3da7a..8e1993440 100644
--- a/BuildNativeUWP.ps1
+++ b/BuildNativeUWP.ps1
@@ -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
diff --git a/Serilog.sln b/Serilog.sln
index 6475f21a2..67033eadc 100644
--- a/Serilog.sln
+++ b/Serilog.sln
@@ -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
diff --git a/Serilog.sln.DotSettings b/Serilog.sln.DotSettings
index 33cc3008c..fcce79117 100644
--- a/Serilog.sln.DotSettings
+++ b/Serilog.sln.DotSettings
@@ -543,6 +543,7 @@ II.2.12 <HandlesEvent />
True
True
True
+ True
True
True
True
@@ -556,4 +557,9 @@ II.2.12 <HandlesEvent />
<data />
- <data><IncludeFilters /><ExcludeFilters /></data>
\ No newline at end of file
+ <data><IncludeFilters /><ExcludeFilters /></data>
+ True
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/Setup.ps1 b/Setup.ps1
new file mode 100644
index 000000000..83d7a6723
--- /dev/null
+++ b/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"
diff --git a/appveyor.yml b/appveyor.yml
index 744f9b71c..a0b5ef1ab 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,10 +1,12 @@
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
@@ -12,7 +14,9 @@ for:
-
matrix:
only:
- - image: Ubuntu
+ - image: Ubuntu1804
+ install:
+ - sh setup.sh
build_script:
- sh build.sh
artifacts:
diff --git a/build.sh b/build.sh
index 573c706eb..9dcb67856 100755
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,9 @@
#!/bin/bash
set -e
+
+export PATH=$HOME/.dotnetcli:$PATH
+
dotnet --info
dotnet --list-sdks
dotnet restore
@@ -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
diff --git a/global.json b/global.json
index 815be4bfb..3580810ef 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "2.1.4"
+ "version": "2.2.300"
}
}
diff --git a/setup.sh b/setup.sh
new file mode 100644
index 000000000..b2c156b18
--- /dev/null
+++ b/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
diff --git a/src/Serilog/Capturing/MessageTemplateProcessor.cs b/src/Serilog/Capturing/MessageTemplateProcessor.cs
index 974f6fe8a..596f09995 100644
--- a/src/Serilog/Capturing/MessageTemplateProcessor.cs
+++ b/src/Serilog/Capturing/MessageTemplateProcessor.cs
@@ -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;
@@ -32,7 +31,7 @@ public MessageTemplateProcessor(PropertyValueConverter propertyValueConverter)
_propertyBinder = new PropertyBinder(_propertyValueConverter);
}
- public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out IEnumerable properties)
+ public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties)
{
parsedTemplate = _parser.Parse(messageTemplate);
properties = _propertyBinder.ConstructProperties(parsedTemplate, messageTemplateParameters);
@@ -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);
+ }
}
}
diff --git a/src/Serilog/Capturing/PropertyBinder.cs b/src/Serilog/Capturing/PropertyBinder.cs
index 03613e0f1..0b41ada1e 100644
--- a/src/Serilog/Capturing/PropertyBinder.cs
+++ b/src/Serilog/Capturing/PropertyBinder.cs
@@ -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;
@@ -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)
{
@@ -41,7 +39,7 @@ public PropertyBinder(PropertyValueConverter valueConverter)
/// represented in the message template.
/// A list of properties; if the template is malformed then
/// this will be empty.
- public IEnumerable ConstructProperties(MessageTemplate messageTemplate, object[] messageTemplateParameters)
+ public EventProperty[] ConstructProperties(MessageTemplate messageTemplate, object[] messageTemplateParameters)
{
if (messageTemplateParameters == null || messageTemplateParameters.Length == 0)
{
@@ -57,14 +55,14 @@ public IEnumerable ConstructProperties(MessageTemplate message
return ConstructNamedProperties(messageTemplate, messageTemplateParameters);
}
- IEnumerable 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))
@@ -79,7 +77,7 @@ IEnumerable 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;
@@ -92,11 +90,11 @@ IEnumerable ConstructPositionalProperties(MessageTemplate temp
return result;
}
- IEnumerable ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
+ EventProperty[] ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
{
var namedProperties = template.NamedProperties;
if (namedProperties == null)
- return Enumerable.Empty();
+ return NoProperties;
var matchedRun = namedProperties.Length;
if (namedProperties.Length != messageTemplateParameters.Length)
@@ -105,7 +103,7 @@ IEnumerable 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];
@@ -116,14 +114,14 @@ IEnumerable 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));
}
diff --git a/src/Serilog/Capturing/PropertyValueConverter.cs b/src/Serilog/Capturing/PropertyValueConverter.cs
index 61606285e..0638afaf0 100644
--- a/src/Serilog/Capturing/PropertyValueConverter.cs
+++ b/src/Serilog/Capturing/PropertyValueConverter.cs
@@ -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)
@@ -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)
@@ -159,6 +161,8 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
}
}
+ var valueType = value.GetType();
+
if (TryConvertEnumerable(value, destructuring, valueType, out var enumerableResult))
return enumerableResult;
diff --git a/src/Serilog/Core/Enrichers/FixedPropertyEnricher.cs b/src/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
index 16df1c427..c9847f71c 100644
--- a/src/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
+++ b/src/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
@@ -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);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Serilog/Core/Logger.cs b/src/Serilog/Core/Logger.cs
index 1a1ae9dc1..d6e500e8c 100644
--- a/src/Serilog/Core/Logger.cs
+++ b/src/Serilog/Core/Logger.cs
@@ -14,6 +14,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Serilog.Capturing;
using Serilog.Core.Enrichers;
using Serilog.Core.Pipeline;
@@ -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;
@@ -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;
@@ -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 boundProperties);
+ _messageTemplateProcessor.Process(messageTemplate, propertyValues, out MessageTemplate parsedTemplate, out var boundProperties);
var logEvent = new LogEvent(DateTimeOffset.Now, level, exception, parsedTemplate, boundProperties);
Dispatch(logEvent);
@@ -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;
}
diff --git a/src/Serilog/Core/Sinks/SecondaryLoggerSink.cs b/src/Serilog/Core/Sinks/SecondaryLoggerSink.cs
index 4e854c065..5285489c8 100644
--- a/src/Serilog/Core/Sinks/SecondaryLoggerSink.cs
+++ b/src/Serilog/Core/Sinks/SecondaryLoggerSink.cs
@@ -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);
}
diff --git a/src/Serilog/Events/EventProperty.cs b/src/Serilog/Events/EventProperty.cs
new file mode 100644
index 000000000..ce16b5bfc
--- /dev/null
+++ b/src/Serilog/Events/EventProperty.cs
@@ -0,0 +1,98 @@
+// Copyright 2019 Serilog Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace Serilog.Events
+{
+ ///
+ /// A property associated with a .
+ ///
+ /// This type is currently internal, while we consider future directions for the logging pipeline, but should end up public
+ /// in future.
+ readonly struct EventProperty
+ {
+ ///
+ /// No property.
+ ///
+ public static EventProperty None = default;
+
+ ///
+ /// The name of the property.
+ ///
+ public string Name { get; }
+
+ ///
+ /// The value of the property.
+ ///
+ public LogEventPropertyValue Value { get; }
+
+ ///
+ /// Construct a with the specified name and value.
+ ///
+ /// The name of the property.
+ /// The value of the property.
+ ///
+ ///
+ public EventProperty(string name, LogEventPropertyValue value)
+ {
+ EnsureValid(name, value);
+
+ Name = name;
+ Value = value;
+ }
+
+ ///
+ /// Permit deconstruction of the property into a name/value pair.
+ ///
+ /// The name of the property.
+ /// The value of the property.
+ public void Deconstruct(out string name, out LogEventPropertyValue value)
+ {
+ name = Name;
+ value = Value;
+ }
+
+ static void EnsureValid(string name, LogEventPropertyValue value)
+ {
+ if (value == null) throw new ArgumentNullException(nameof(value));
+ if (!LogEventProperty.IsValidName(name))
+ throw new ArgumentException("Property name is not valid.");
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is EventProperty other && Equals(other);
+ }
+
+ /// Indicates whether this instance and a specified are equal.
+ /// The to compare with the current instance.
+ ///
+ /// if and this instance represent the same value; otherwise, .
+ public bool Equals(EventProperty other)
+ {
+ return string.Equals(Name, other.Name) && Equals(Value, other.Value);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ (Value != null ? Value.GetHashCode() : 0);
+ }
+ }
+ }
+}
diff --git a/src/Serilog/Events/LogEvent.cs b/src/Serilog/Events/LogEvent.cs
index 1906960f7..45e0056bd 100644
--- a/src/Serilog/Events/LogEvent.cs
+++ b/src/Serilog/Events/LogEvent.cs
@@ -25,6 +25,15 @@ public class LogEvent
{
readonly Dictionary _properties;
+ LogEvent(DateTimeOffset timestamp, LogEventLevel level, Exception exception, MessageTemplate messageTemplate, Dictionary properties)
+ {
+ Timestamp = timestamp;
+ Level = level;
+ Exception = exception;
+ MessageTemplate = messageTemplate ?? throw new ArgumentNullException(nameof(messageTemplate));
+ _properties = properties ?? throw new ArgumentNullException(nameof(properties));
+ }
+
///
/// Construct a new .
///
@@ -34,16 +43,26 @@ public class LogEvent
/// The message template describing the event.
/// Properties associated with the event, including those presented in .
public LogEvent(DateTimeOffset timestamp, LogEventLevel level, Exception exception, MessageTemplate messageTemplate, IEnumerable properties)
+ : this(timestamp, level, exception, messageTemplate, new Dictionary())
{
- if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate));
if (properties == null) throw new ArgumentNullException(nameof(properties));
- Timestamp = timestamp;
- Level = level;
- Exception = exception;
- MessageTemplate = messageTemplate;
- _properties = new Dictionary();
- foreach (var p in properties)
- AddOrUpdateProperty(p);
+ foreach (var property in properties)
+ AddOrUpdateProperty(property);
+ }
+
+ ///
+ /// Construct a new .
+ ///
+ /// The time at which the event occurred.
+ /// The level of the event.
+ /// An exception associated with the event, or null.
+ /// The message template describing the event.
+ /// Properties associated with the event, including those presented in .
+ internal LogEvent(DateTimeOffset timestamp, LogEventLevel level, Exception exception, MessageTemplate messageTemplate, EventProperty[] properties)
+ : this(timestamp, level, exception, messageTemplate, new Dictionary(properties?.Length ?? throw new ArgumentNullException(nameof(properties))))
+ {
+ for (var i = 0; i < properties.Length; ++i)
+ _properties[properties[i].Name] = properties[i].Value;
}
///
@@ -103,6 +122,17 @@ public void AddOrUpdateProperty(LogEventProperty property)
_properties[property.Name] = property.Value;
}
+ ///
+ /// Add a property to the event if not already present, otherwise, update its value.
+ ///
+ /// The property to add or update.
+ ///
+ internal void AddOrUpdateProperty(in EventProperty property)
+ {
+ if (property.Equals(EventProperty.None)) throw new ArgumentNullException(nameof(property));
+ _properties[property.Name] = property.Value;
+ }
+
///
/// Add a property to the event if not already present.
///
@@ -115,6 +145,18 @@ public void AddPropertyIfAbsent(LogEventProperty property)
_properties.Add(property.Name, property.Value);
}
+ ///
+ /// Add a property to the event if not already present.
+ ///
+ /// The property to add.
+ ///
+ internal void AddPropertyIfAbsent(in EventProperty property)
+ {
+ if (property.Equals(EventProperty.None)) throw new ArgumentNullException(nameof(property));
+ if (!_properties.ContainsKey(property.Name))
+ _properties.Add(property.Name, property.Value);
+ }
+
///
/// Remove a property from the event, if present. Otherwise no action
/// is performed.
@@ -124,5 +166,19 @@ public void RemovePropertyIfPresent(string propertyName)
{
_properties.Remove(propertyName);
}
+
+ internal LogEvent Copy()
+ {
+ var properties = new Dictionary(Properties.Count);
+ foreach (var key in _properties.Keys)
+ properties.Add(key, _properties[key]);
+
+ return new LogEvent(
+ Timestamp,
+ Level,
+ Exception,
+ MessageTemplate,
+ properties);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Serilog/Events/LogEventProperty.cs b/src/Serilog/Events/LogEventProperty.cs
index 8bea942fa..f5cbc9027 100644
--- a/src/Serilog/Events/LogEventProperty.cs
+++ b/src/Serilog/Events/LogEventProperty.cs
@@ -38,6 +38,19 @@ public LogEventProperty(string name, LogEventPropertyValue value)
Value = value;
}
+ ///
+ /// Construct a from an existing instance.
+ ///
+ /// The existing property.
+ ///
+ ///
+ internal LogEventProperty(EventProperty property)
+ {
+ if (property.Equals(EventProperty.None)) throw new ArgumentNullException(nameof(property));
+ Name = property.Name;
+ Value = property.Value;
+ }
+
///
/// The name of the property.
///
diff --git a/test/Serilog.PerformanceTests/BindingBenchmark.cs b/test/Serilog.PerformanceTests/BindingBenchmark.cs
new file mode 100644
index 000000000..857665351
--- /dev/null
+++ b/test/Serilog.PerformanceTests/BindingBenchmark.cs
@@ -0,0 +1,75 @@
+// Copyright 2013-2016 Serilog Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using BenchmarkDotNet.Attributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Serilog.Events;
+using Serilog.Parsing;
+using Serilog.PerformanceTests.Support;
+
+namespace Serilog.PerformanceTests
+{
+ ///
+ /// Tests the cost of writing through the logging pipeline.
+ ///
+ [MemoryDiagnoser]
+ public class BindingBenchmark
+ {
+ const string
+ MT0 = "Zero",
+ MT1 = "Zero{A}one",
+ MT5 = "Zero{A}one{B}two{C}three{D}four{E}five";
+
+ ILogger _log;
+ object[] _zero, _one, _five;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _log = new LoggerConfiguration()
+ .WriteTo.Sink(new NullSink())
+ .CreateLogger();
+
+ _zero = new object[0];
+ _one = new object[] { 1 };
+ _five = new object[] { 1, 2, 3, 4, 5 };
+ }
+
+ // The benchmarks run p.Count() to force enumeration; this will be representative of how the API
+ // is consumed (there's not much point benchmarking time to return a lazy enumerator).
+
+ [Benchmark(Baseline = true)]
+ public (MessageTemplate, int) BindZero()
+ {
+ _log.BindMessageTemplate(MT0, _zero, out var mt, out var p);
+ return (mt, p.Count());
+ }
+
+ [Benchmark]
+ public (MessageTemplate, int) BindOne()
+ {
+ _log.BindMessageTemplate(MT1, _one, out var mt, out var p);
+ return (mt, p.Count());
+ }
+
+ [Benchmark]
+ public (MessageTemplate, int) BindFive()
+ {
+ _log.BindMessageTemplate(MT5, _five, out var mt, out var p);
+ return (mt, p.Count());
+ }
+ }
+}
diff --git a/test/Serilog.PerformanceTests/Harness.cs b/test/Serilog.PerformanceTests/Harness.cs
index 081987f65..14b3ba963 100644
--- a/test/Serilog.PerformanceTests/Harness.cs
+++ b/test/Serilog.PerformanceTests/Harness.cs
@@ -17,17 +17,22 @@
namespace Serilog.PerformanceTests
{
+ ///
+ /// Wrappers that make it easy to run benchmark suites through the dotnet test runner.
+ ///
+ ///
+ /// dotnet test -c Release -f netcoreapp2.2 --filter "FullyQualifiedName=Serilog.PerformanceTests.Harness.Allocations"
+ ///
public class Harness
{
- // dotnet test -c Release -f net46 --filter "FullyQualifiedName=Serilog.PerformanceTests.Harness.AllocationsBenchmark"
[Fact]
- public void AllocationsBenchmark()
+ public void Allocations()
{
BenchmarkRunner.Run();
}
[Fact]
- public void MessageTemplateCacheBenchmark()
+ public void MessageTemplateCache()
{
BenchmarkRunner.Run();
BenchmarkRunner.Run();
@@ -74,11 +79,17 @@ public void OutputTemplateRendering()
{
BenchmarkRunner.Run();
}
-
+
[Fact]
- public void MessageTemplateRenderingBenchmark()
+ public void MessageTemplateRendering()
{
BenchmarkRunner.Run();
}
+
+ [Fact]
+ public void Binding()
+ {
+ BenchmarkRunner.Run();
+ }
}
}
diff --git a/test/Serilog.PerformanceTests/LevelControlBenchmark.cs b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs
index 4c6ae2849..1740c1dad 100644
--- a/test/Serilog.PerformanceTests/LevelControlBenchmark.cs
+++ b/test/Serilog.PerformanceTests/LevelControlBenchmark.cs
@@ -13,7 +13,7 @@ public class LevelControlBenchmark
ILogger _off, _levelSwitchOff, _minLevel, _levelSwitch;
readonly LogEvent _event = Some.InformationEvent();
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_off = new LoggerConfiguration()
diff --git a/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs
index df0a7f7e2..8f3d03f76 100644
--- a/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs
+++ b/test/Serilog.PerformanceTests/LogContextEnrichmentBenchmark.cs
@@ -10,7 +10,7 @@ public class LogContextEnrichmentBenchmark
ILogger _bare, _enriched;
readonly LogEvent _event = Some.InformationEvent();
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_bare = new LoggerConfiguration()
diff --git a/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Cached.cs b/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Cached.cs
index 2382cec6c..b9300ae9f 100644
--- a/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Cached.cs
+++ b/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Cached.cs
@@ -25,7 +25,7 @@ public class MessageTemplateCacheBenchmark_Cached
[Params(1, -1)]
public int MaxDegreeOfParallelism { get; set; }
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_templateList = Enumerable.Range(0, Items).Select(x => $"{DefaultOutputTemplate}_{Guid.NewGuid()}").ToList();
diff --git a/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Leaking.cs b/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Leaking.cs
index 4acae343a..af8534649 100644
--- a/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Leaking.cs
+++ b/test/Serilog.PerformanceTests/MessageTemplateCacheBenchmark/MessageTemplateCacheBenchmark_Leaking.cs
@@ -25,7 +25,7 @@ public class MessageTemplateCacheBenchmark_Leaking
[Params(1, -1)]
public int MaxDegreeOfParallelism { get; set; }
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_templateList = Enumerable.Range(0, Items).Select(x => $"{DefaultOutputTemplate}_{Guid.NewGuid()}").ToList();
diff --git a/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs
index 02e5ab13e..18742cff8 100644
--- a/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs
+++ b/test/Serilog.PerformanceTests/MessageTemplateParsingBenchmark.cs
@@ -11,7 +11,7 @@ public class MessageTemplateParsingBenchmark
MessageTemplateParser _parser;
const string _DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}";
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_parser = new MessageTemplateParser();
diff --git a/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs b/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs
index 1c170d366..1d71e386c 100644
--- a/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs
+++ b/test/Serilog.PerformanceTests/NestedLoggerCreationBenchmark.cs
@@ -6,11 +6,12 @@ namespace Serilog.PerformanceTests
///
/// Tests the cost creating a nested logger.
///
+ [MemoryDiagnoser]
public class NestedLoggerCreationBenchmark
{
ILogger log;
- [Setup]
+ [GlobalSetup]
public void Setup()
{
log = new LoggerConfiguration()
@@ -18,6 +19,12 @@ public void Setup()
.CreateLogger();
}
+ [Benchmark]
+ public void ForContextInt()
+ {
+ log.ForContext("Number", 1);
+ }
+
[Benchmark]
public void ForContextString()
{
@@ -30,4 +37,4 @@ public void ForContextType()
log.ForContext();
}
}
-}
\ No newline at end of file
+}
diff --git a/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs
index 292472ecf..e5d23edff 100644
--- a/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs
+++ b/test/Serilog.PerformanceTests/NestedLoggerLatencyBenchmark.cs
@@ -12,7 +12,7 @@ public class NestedLoggerLatencyBenchmark
ILogger _log, _nested;
readonly LogEvent _event = Some.InformationEvent();
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_log = new LoggerConfiguration()
diff --git a/test/Serilog.PerformanceTests/PipelineBenchmark.cs b/test/Serilog.PerformanceTests/PipelineBenchmark.cs
index 631237377..47001b257 100644
--- a/test/Serilog.PerformanceTests/PipelineBenchmark.cs
+++ b/test/Serilog.PerformanceTests/PipelineBenchmark.cs
@@ -26,7 +26,7 @@ public class PipelineBenchmark
ILogger _log;
Exception _exception;
- [Setup]
+ [GlobalSetup]
public void Setup()
{
_exception = new Exception("An Error");
diff --git a/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj b/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj
index 03cb721d6..03551f55a 100644
--- a/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj
+++ b/test/Serilog.PerformanceTests/Serilog.PerformanceTests.csproj
@@ -1,6 +1,6 @@

- netcoreapp2.0;net46
+ netcoreapp2.2;net4.7.2
Serilog.PerformanceTests
../../assets/Serilog.snk
true
@@ -17,8 +17,8 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/test/Serilog.Tests/Context/LogContextTests.cs b/test/Serilog.Tests/Context/LogContextTests.cs
index 7c7f8f3c2..dd3521826 100644
--- a/test/Serilog.Tests/Context/LogContextTests.cs
+++ b/test/Serilog.Tests/Context/LogContextTests.cs
@@ -342,9 +342,9 @@ public async Task DisconnectRemoteObjectsAfterCrossDomainCallsOnDispose()
await Task.Delay(200);
- // This is intermittently 2 or 3, depending on the moods of the test runner;
+ // This is intermittently 2 or 3 (now, 4), depending on the moods of the test runner;
// I think "at least two" is what we're concerned about, here.
- Assert.InRange(tracker.DisconnectCount, 2, 3);
+ Assert.InRange(tracker.DisconnectCount, 2, 4);
void CallFromRemote() { }
}
diff --git a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs
index 192440e58..3a750b1fb 100644
--- a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs
+++ b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs
@@ -179,7 +179,7 @@ static IEnumerable Capture(string messageTemplate, params obje
var mt = new MessageTemplateParser().Parse(messageTemplate);
var binder = new PropertyBinder(
new PropertyValueConverter(10, 1000, 1000, Enumerable.Empty(), Enumerable.Empty(), false));
- return binder.ConstructProperties(mt, properties);
+ return binder.ConstructProperties(mt, properties).Select(p => new LogEventProperty(p.Name, p.Value));
}
}
}
diff --git a/test/Serilog.Tests/Serilog.Tests.csproj b/test/Serilog.Tests/Serilog.Tests.csproj
index 9ab406ea7..d1947d4ed 100644
--- a/test/Serilog.Tests/Serilog.Tests.csproj
+++ b/test/Serilog.Tests/Serilog.Tests.csproj
@@ -1,6 +1,6 @@

- netcoreapp2.0;net452;net46
+ netcoreapp2.2;net452;net46
Serilog.Tests
../../assets/Serilog.snk
true
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/test/Serilog.UwpTests/Serilog.UwpTests.csproj b/test/Serilog.UwpTests/Serilog.UwpTests.csproj
index 956847f18..eff70092f 100644
--- a/test/Serilog.UwpTests/Serilog.UwpTests.csproj
+++ b/test/Serilog.UwpTests/Serilog.UwpTests.csproj
@@ -11,8 +11,8 @@
Serilog.UwpTests
en-US
UAP
- 10.0.10586.0
- 10.0.10586.0
+ 10.0.17134.0
+ 10.0.17134.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
@@ -132,10 +132,10 @@
6.1.5
- 2.3.1
+ 2.4.1
- 2.3.1
+ 2.4.1