diff --git a/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs
new file mode 100644
index 000000000..9381f7121
--- /dev/null
+++ b/src/Serilog/Configuration/LoggerAuditSinkConfiguration.cs
@@ -0,0 +1,106 @@
+// Copyright 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 System;
+using Serilog.Core;
+using Serilog.Events;
+
+namespace Serilog.Configuration
+{
+ ///
+ /// Controls sink configuration.
+ ///
+ public class LoggerAuditSinkConfiguration
+ {
+ LoggerSinkConfiguration _sinkConfiguration;
+
+ internal LoggerAuditSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration)
+ {
+ _sinkConfiguration = new LoggerSinkConfiguration(loggerConfiguration, addSink, applyInheritedConfiguration);
+ }
+
+ ///
+ /// Audit log events to the specified .
+ ///
+ /// The sink.
+ /// The minimum level for
+ /// events passed through the sink. Ignored when is specified.
+ /// A switch allowing the pass-through minimum level
+ /// to be changed at runtime.
+ /// Configuration object allowing method chaining.
+ public LoggerConfiguration Sink(
+ ILogEventSink logEventSink,
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
+ // ReSharper disable once MethodOverloadWithOptionalParameter
+ LoggingLevelSwitch levelSwitch = null)
+ {
+ return _sinkConfiguration.Sink(logEventSink, restrictedToMinimumLevel, levelSwitch);
+ }
+
+ ///
+ /// Audit log events to the specified .
+ ///
+ /// The sink.
+ /// The minimum level for
+ /// events passed through the sink. Ignored when is specified.
+ /// A switch allowing the pass-through minimum level
+ /// to be changed at runtime.
+ /// Configuration object allowing method chaining.
+ public LoggerConfiguration Sink(
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
+ LoggingLevelSwitch levelSwitch = null)
+ where TSink : ILogEventSink, new()
+ {
+ return _sinkConfiguration.Sink(restrictedToMinimumLevel, levelSwitch);
+ }
+
+ ///
+ /// Audit log events to a sub-logger, where further processing may occur. Events through
+ /// the sub-logger will be constrained by filters and enriched by enrichers that are
+ /// active in the parent. A sub-logger cannot be used to log at a more verbose level, but
+ /// a less verbose level is possible.
+ ///
+ /// An action that configures the sub-logger.
+ /// The minimum level for
+ /// events passed through the sink. Ignored when is specified.
+ /// A switch allowing the pass-through minimum level
+ /// to be changed at runtime.
+ /// Configuration object allowing method chaining.
+ public LoggerConfiguration Logger(
+ Action configureLogger,
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
+ LoggingLevelSwitch levelSwitch = null)
+ {
+ return _sinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch);
+ }
+
+ ///
+ /// Audit log events to a sub-logger, where further processing may occur. Events through
+ /// the sub-logger will be constrained by filters and enriched by enrichers that are
+ /// active in the parent. A sub-logger cannot be used to log at a more verbose level, but
+ /// a less verbose level is possible.
+ ///
+ /// The sub-logger. This will not be shut down automatically when the
+ /// parent logger is disposed.
+ /// The minimum level for
+ /// events passed through the sink.
+ /// Configuration object allowing method chaining.
+ public LoggerConfiguration Logger(
+ ILogger logger,
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum)
+ {
+ return _sinkConfiguration.Logger(logger, restrictedToMinimumLevel);
+ }
+ }
+}
diff --git a/src/Serilog/Configuration/LoggerSinkConfiguration.cs b/src/Serilog/Configuration/LoggerSinkConfiguration.cs
index f1b7e94a4..0552387cd 100644
--- a/src/Serilog/Configuration/LoggerSinkConfiguration.cs
+++ b/src/Serilog/Configuration/LoggerSinkConfiguration.cs
@@ -14,7 +14,6 @@
using System;
using System.ComponentModel;
-
using Serilog.Core;
using Serilog.Core.Sinks;
using Serilog.Debugging;
@@ -31,8 +30,6 @@ public class LoggerSinkConfiguration
readonly Action _addSink;
readonly Action _applyInheritedConfiguration;
- const string DefaultOutputTemplate = "{Timestamp} [{Level}] {Message}{NewLine}{Exception}";
-
internal LoggerSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration)
{
if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration));
@@ -51,7 +48,7 @@ internal LoggerSinkConfiguration(LoggerConfiguration loggerConfiguration, Action
/// events passed through the sink.
/// Configuration object allowing method chaining.
/// Provided for binary compatibility for earlier versions,
- /// should be removed in 2.0. Not marked obsolete because warnings
+ /// should be removed in 3.0. Not marked obsolete because warnings
/// would be syntactically annoying to avoid.
[EditorBrowsable(EditorBrowsableState.Never)]
public LoggerConfiguration Sink(
@@ -110,8 +107,6 @@ public LoggerConfiguration Sink(
return Sink(new TSink(), restrictedToMinimumLevel, levelSwitch);
}
-
-
///
/// Write log events to a sub-logger, where further processing may occur. Events through
/// the sub-logger will be constrained by filters and enriched by enrichers that are
diff --git a/src/Serilog/Core/Sinks/AggregateSink.cs b/src/Serilog/Core/Sinks/AggregateSink.cs
new file mode 100644
index 000000000..c43cd3889
--- /dev/null
+++ b/src/Serilog/Core/Sinks/AggregateSink.cs
@@ -0,0 +1,54 @@
+// Copyright 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 System;
+using System.Collections.Generic;
+using System.Linq;
+using Serilog.Debugging;
+using Serilog.Events;
+
+namespace Serilog.Core.Sinks
+{
+ class AggregateSink : ILogEventSink
+ {
+ readonly ILogEventSink[] _sinks;
+
+ public AggregateSink(IEnumerable sinks)
+ {
+ if (sinks == null) throw new ArgumentNullException(nameof(sinks));
+ _sinks = sinks.ToArray();
+ }
+
+ public void Emit(LogEvent logEvent)
+ {
+ List exceptions = null;
+ foreach (var sink in _sinks)
+ {
+ try
+ {
+ sink.Emit(logEvent);
+ }
+ catch (Exception ex)
+ {
+ SelfLog.WriteLine("Caught exception while emitting to sink {0}: {1}", sink, ex);
+ exceptions = exceptions ?? new List();
+ exceptions.Add(ex);
+ }
+ }
+
+ if (exceptions != null)
+ throw new AggregateException("Failed to emit a log event.", exceptions);
+ }
+ }
+}
diff --git a/src/Serilog/Core/Sinks/FilteringSink.cs b/src/Serilog/Core/Sinks/FilteringSink.cs
index 9a5d4102b..13a898491 100644
--- a/src/Serilog/Core/Sinks/FilteringSink.cs
+++ b/src/Serilog/Core/Sinks/FilteringSink.cs
@@ -23,13 +23,15 @@ namespace Serilog.Core.Sinks
class FilteringSink : ILogEventSink
{
readonly ILogEventSink _sink;
+ readonly bool _propagateExceptions;
readonly ILogEventFilter[] _filters;
- public FilteringSink(ILogEventSink sink, IEnumerable filters)
+ public FilteringSink(ILogEventSink sink, IEnumerable filters, bool propagateExceptions)
{
if (sink == null) throw new ArgumentNullException(nameof(sink));
if (filters == null) throw new ArgumentNullException(nameof(filters));
_sink = sink;
+ _propagateExceptions = propagateExceptions;
_filters = filters.ToArray();
}
@@ -47,7 +49,10 @@ public void Emit(LogEvent logEvent)
}
catch (Exception ex)
{
- SelfLog.WriteLine("Caught exception {0} while applying filters.", ex);
+ SelfLog.WriteLine("Caught exception while applying filters: {0}", ex);
+
+ if (_propagateExceptions)
+ throw;
}
}
}
diff --git a/src/Serilog/Core/Sinks/SafeAggregateSink.cs b/src/Serilog/Core/Sinks/SafeAggregateSink.cs
index 857917134..da8ad6505 100644
--- a/src/Serilog/Core/Sinks/SafeAggregateSink.cs
+++ b/src/Serilog/Core/Sinks/SafeAggregateSink.cs
@@ -14,6 +14,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Serilog.Debugging;
using Serilog.Events;
@@ -21,12 +22,12 @@ namespace Serilog.Core.Sinks
{
class SafeAggregateSink : ILogEventSink
{
- readonly IEnumerable _sinks;
+ readonly ILogEventSink[] _sinks;
public SafeAggregateSink(IEnumerable sinks)
{
if (sinks == null) throw new ArgumentNullException(nameof(sinks));
- _sinks = sinks;
+ _sinks = sinks.ToArray();
}
public void Emit(LogEvent logEvent)
@@ -39,10 +40,9 @@ public void Emit(LogEvent logEvent)
}
catch (Exception ex)
{
- SelfLog.WriteLine("Caught exception {0} while emitting to sink {1}.", ex, sink);
+ SelfLog.WriteLine("Caught exception while emitting to sink {0}: {1}", sink, ex);
}
}
}
}
}
-
diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs
index 3d8b0b646..764d937e2 100644
--- a/src/Serilog/LoggerConfiguration.cs
+++ b/src/Serilog/LoggerConfiguration.cs
@@ -30,6 +30,7 @@ namespace Serilog
public class LoggerConfiguration
{
readonly List _logEventSinks = new List();
+ readonly List _auditSinks = new List();
readonly List _enrichers = new List();
readonly List _filters = new List();
readonly List _additionalScalarTypes = new List();
@@ -40,22 +41,31 @@ public class LoggerConfiguration
int _maximumDestructuringDepth = 10;
bool _loggerCreated;
+ void ApplyInheritedConfiguration(LoggerConfiguration child)
+ {
+ if (_levelSwitch != null)
+ child.MinimumLevel.ControlledBy(_levelSwitch);
+ else
+ child.MinimumLevel.Is(_minimumLevel);
+ }
+
///
/// Configures the sinks that log events will be emitted to.
///
- public LoggerSinkConfiguration WriteTo
- {
- get
- {
- return new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), child =>
- {
- if (_levelSwitch != null)
- child.MinimumLevel.ControlledBy(_levelSwitch);
- else
- child.MinimumLevel.Is(_minimumLevel);
- });
- }
- }
+ public LoggerSinkConfiguration WriteTo => new LoggerSinkConfiguration(this, s => _logEventSinks.Add(s), ApplyInheritedConfiguration);
+
+ ///
+ /// Configures sinks for auditing, instead of regular (safe) logging. When auditing is used,
+ /// exceptions from sinks and any intermediate filters propagate back to the caller. Most callers
+ /// should use instead.
+ ///
+ ///
+ /// Not all sinks are compatible with transactional auditing requirements (many will use asynchronous
+ /// batching to improve write throughput and latency). Sinks need to opt-in to auditing support by
+ /// extending , though the generic
+ /// method allows any sink class to be adapted for auditing.
+ ///
+ public LoggerAuditSinkConfiguration AuditTo => new LoggerAuditSinkConfiguration(this, s => _auditSinks.Add(s), ApplyInheritedConfiguration);
///
/// Configures the minimum level at which events will be passed to sinks. If
@@ -129,10 +139,18 @@ public Logger CreateLogger()
ILogEventSink sink = new SafeAggregateSink(_logEventSinks);
+ var auditing = _auditSinks.Any();
+ if (auditing)
+ sink = new AggregateSink(new[] { sink }.Concat(_auditSinks));
+
if (_filters.Any())
- sink = new FilteringSink(sink, _filters);
+ {
+ // A throwing filter could drop an auditable event, so exceptions in filters must be propagated
+ // if auditing is used.
+ sink = new FilteringSink(sink, _filters, auditing);
+ }
- var converter = new PropertyValueConverter(_maximumDestructuringDepth, _additionalScalarTypes, _additionalDestructuringPolicies);
+ var converter = new PropertyValueConverter(_maximumDestructuringDepth, _additionalScalarTypes, _additionalDestructuringPolicies, auditing);
var processor = new MessageTemplateProcessor(converter);
ILogEventEnricher enricher;
diff --git a/src/Serilog/Parameters/PropertyValueConverter.cs b/src/Serilog/Parameters/PropertyValueConverter.cs
index beeabcac0..49bf9a825 100644
--- a/src/Serilog/Parameters/PropertyValueConverter.cs
+++ b/src/Serilog/Parameters/PropertyValueConverter.cs
@@ -46,14 +46,20 @@ partial class PropertyValueConverter : ILogEventPropertyFactory, ILogEventProper
readonly IDestructuringPolicy[] _destructuringPolicies;
readonly IScalarConversionPolicy[] _scalarConversionPolicies;
readonly int _maximumDestructuringDepth;
+ readonly bool _propagateExceptions;
- public PropertyValueConverter(int maximumDestructuringDepth, IEnumerable additionalScalarTypes, IEnumerable additionalDestructuringPolicies)
+ public PropertyValueConverter(
+ int maximumDestructuringDepth,
+ IEnumerable additionalScalarTypes,
+ IEnumerable additionalDestructuringPolicies,
+ bool propagateExceptions)
{
if (additionalScalarTypes == null) throw new ArgumentNullException(nameof(additionalScalarTypes));
if (additionalDestructuringPolicies == null) throw new ArgumentNullException(nameof(additionalDestructuringPolicies));
if (maximumDestructuringDepth < 0) throw new ArgumentOutOfRangeException(nameof(maximumDestructuringDepth));
_maximumDestructuringDepth = maximumDestructuringDepth;
+ _propagateExceptions = propagateExceptions;
_scalarConversionPolicies = new IScalarConversionPolicy[]
{
@@ -84,7 +90,19 @@ public LogEventPropertyValue CreatePropertyValue(object value, bool destructureO
public LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructuring)
{
- return CreatePropertyValue(value, destructuring, 1);
+ try
+ {
+ return CreatePropertyValue(value, destructuring, 1);
+ }
+ catch (Exception ex)
+ {
+ SelfLog.WriteLine("Exception caught while converting property value: {0}", ex);
+
+ if (_propagateExceptions)
+ throw;
+
+ return new ScalarValue("Capturing the property value threw an exception: " + ex.GetType().Name);
+ }
}
LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects, int depth)
@@ -109,7 +127,7 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
var limiter = new DepthLimiter(depth, _maximumDestructuringDepth, this);
foreach (var scalarConversionPolicy in _scalarConversionPolicies)
- {
+ {
ScalarValue converted;
if (scalarConversionPolicy.TryConvertToScalar(value, limiter, out converted))
return converted;
@@ -177,7 +195,7 @@ bool IsValidDictionaryKeyType(Type valueType)
valueType.GetTypeInfo().IsEnum;
}
- static IEnumerable GetProperties(object value, ILogEventPropertyValueFactory recursive)
+ IEnumerable GetProperties(object value, ILogEventPropertyValueFactory recursive)
{
foreach (var prop in value.GetType().GetPropertiesRecursive())
{
@@ -188,12 +206,18 @@ static IEnumerable GetProperties(object value, ILogEventProper
}
catch (TargetParameterCountException)
{
+ // These properties would ideally be ignored; since they never produce values they're not
+ // of concern to auditing and exceptions can be suppressed.
SelfLog.WriteLine("The property accessor {0} is a non-default indexer", prop);
continue;
}
catch (TargetInvocationException ex)
{
- SelfLog.WriteLine("The property accessor {0} threw exception {1}", prop, ex);
+ SelfLog.WriteLine("The property accessor {0} threw exception: {1}", prop, ex);
+
+ if (_propagateExceptions)
+ throw;
+
propValue = "The property accessor threw an exception: " + ex.InnerException.GetType().Name;
}
yield return new LogEventProperty(prop.Name, recursive.CreatePropertyValue(propValue, true));
diff --git a/src/Serilog/project.json b/src/Serilog/project.json
index c7bede7d0..692b5faf5 100644
--- a/src/Serilog/project.json
+++ b/src/Serilog/project.json
@@ -1,5 +1,5 @@
{
- "version": "2.1.1-*",
+ "version": "2.2.0-*",
"description": "Simple .NET logging with fully-structured events",
"authors": [ "Serilog Contributors" ],
diff --git a/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs b/test/Serilog.Tests/Core/LogEventPropertyCapturingTests.cs
index b4fb49184..b81834b43 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, Enumerable.Empty(), Enumerable.Empty()));
+ new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false));
return binder.ConstructProperties(mt, properties);
}
diff --git a/test/Serilog.Tests/Core/MessageTemplateTests.cs b/test/Serilog.Tests/Core/MessageTemplateTests.cs
index 804e4bc22..75bcee236 100644
--- a/test/Serilog.Tests/Core/MessageTemplateTests.cs
+++ b/test/Serilog.Tests/Core/MessageTemplateTests.cs
@@ -126,7 +126,7 @@ static string Render(string messageTemplate, params object[] properties)
static string Render(IFormatProvider formatProvider, string messageTemplate, params object[] properties)
{
var mt = new MessageTemplateParser().Parse(messageTemplate);
- var binder = new PropertyBinder(new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty()));
+ var binder = new PropertyBinder(new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false));
var props = binder.ConstructProperties(mt, properties);
var output = new StringBuilder();
var writer = new StringWriter(output);
diff --git a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs
index 8d5ee101f..170dd2fe2 100644
--- a/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs
+++ b/test/Serilog.Tests/Events/LogEventPropertyValueTests.cs
@@ -26,7 +26,8 @@ namespace Serilog.Tests.Events
{
public class LogEventPropertyValueTests
{
- readonly PropertyValueConverter _converter = new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty());
+ readonly PropertyValueConverter _converter =
+ new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false);
[Fact]
public void AnEnumIsConvertedToANonStringScalarValue()
diff --git a/test/Serilog.Tests/LoggerConfigurationTests.cs b/test/Serilog.Tests/LoggerConfigurationTests.cs
index a332d3c7b..a755bda26 100644
--- a/test/Serilog.Tests/LoggerConfigurationTests.cs
+++ b/test/Serilog.Tests/LoggerConfigurationTests.cs
@@ -363,5 +363,132 @@ public void LowerMinimumLevelOverridesArePropagated()
Assert.Equal(1, sink.Events.Count);
}
+
+ [Fact]
+ public void ExceptionsThrownBySinksAreNotPropagated()
+ {
+ var logger = new LoggerConfiguration()
+ .WriteTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); }))
+ .CreateLogger();
+
+ logger.Write(Some.InformationEvent());
+
+ Assert.True(true, "No exception reached the caller");
+ }
+
+ [Fact]
+ public void ExceptionsThrownBySinksAreNotPropagatedEvenWhenAuditingIsPresent()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new CollectingSink())
+ .WriteTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); }))
+ .CreateLogger();
+
+ logger.Write(Some.InformationEvent());
+
+ Assert.True(true, "No exception reached the caller");
+ }
+
+ [Fact]
+ public void ExceptionsThrownByFiltersAreNotPropagated()
+ {
+ var logger = new LoggerConfiguration()
+ .Filter.ByExcluding(e => { throw new Exception("Boom!"); })
+ .CreateLogger();
+
+ logger.Write(Some.InformationEvent());
+
+ Assert.True(true, "No exception reached the caller");
+ }
+
+ [Fact]
+ public void ExceptionsThrownByAuditSinksArePropagated()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); }))
+ .CreateLogger();
+
+ Assert.Throws(() => logger.Write(Some.InformationEvent()));
+ }
+
+ [Fact]
+ public void ExceptionsThrownByFiltersArePropagatedIfAuditingEnabled()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new DelegatingSink(e => { }))
+ .Filter.ByExcluding(e => { throw new Exception("Boom!"); })
+ .CreateLogger();
+
+ Assert.Throws(() => logger.Write(Some.InformationEvent()));
+ }
+
+ [Fact]
+ public void ExceptionsThrownByAuditSinksArePropagatedFromChildLoggers()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new DelegatingSink(e => { throw new Exception("Boom!"); }))
+ .CreateLogger();
+
+ Assert.Throws(() => logger
+ .ForContext()
+ .Write(Some.InformationEvent()));
+ }
+
+ class Value { }
+
+ [Fact]
+ public void ExceptionsThrownByDestructuringPoliciesAreNotPropagated()
+ {
+ var logger = new LoggerConfiguration()
+ .WriteTo.Sink(new CollectingSink())
+ .Destructure.ByTransforming(v => { throw new Exception("Boom!"); })
+ .CreateLogger();
+
+ logger.Information("{@Value}", new Value());
+
+ Assert.True(true, "No exception reached the caller");
+ }
+
+ [Fact]
+ public void ExceptionsThrownByDestructuringPoliciesArePropagatedIfAuditingEnabled()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new CollectingSink())
+ .Destructure.ByTransforming(v => { throw new Exception("Boom!"); })
+ .CreateLogger();
+
+ Assert.Throws(() => logger.Information("{@Value}", new Value()));
+ }
+
+ class ThrowingProperty
+ {
+ // ReSharper disable once UnusedMember.Local
+ public string Property
+ {
+ get { throw new Exception("Boom!"); }
+ }
+ }
+
+ [Fact]
+ public void ExceptionsThrownByPropertyAccessorsAreNotPropagated()
+ {
+ var logger = new LoggerConfiguration()
+ .WriteTo.Sink(new CollectingSink())
+ .CreateLogger();
+
+ logger.Information("{@Value}", new ThrowingProperty());
+
+ Assert.True(true, "No exception reached the caller");
+ }
+
+ [Fact]
+ public void ExceptionsThrownByPropertyAccessorsArePropagatedIfAuditingEnabled()
+ {
+ var logger = new LoggerConfiguration()
+ .AuditTo.Sink(new CollectingSink())
+ .CreateLogger();
+
+ Assert.Throws(() => logger.Information("{@Value}", new ThrowingProperty()));
+ }
}
-}
\ No newline at end of file
+}
diff --git a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs
index 438011981..354202eab 100644
--- a/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs
+++ b/test/Serilog.Tests/Parameters/PropertyValueConverterTests.cs
@@ -12,7 +12,8 @@ namespace Serilog.Tests.Parameters
{
public class PropertyValueConverterTests
{
- readonly PropertyValueConverter _converter = new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty());
+ readonly PropertyValueConverter _converter =
+ new PropertyValueConverter(10, Enumerable.Empty(), Enumerable.Empty(), false);
[Fact]
public void UnderDestructuringAByteArrayIsAScalarValue()