Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WithoutReflectionBasedDestructurer option #158

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Source/Serilog.Exceptions/Core/DestructuringOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public class DestructuringOptionsBuilder : IDestructuringOptions
/// </summary>
public int DestructuringDepth { get; private set; } = 10;

/// <summary>
/// Disable reflection based destruturer.
/// </summary>
public bool DisableReflectionBasedDestructurer { get; private set; } = false;

/// <summary>
/// Collection of destructurers that will be used to handle exception.
/// </summary>
Expand Down Expand Up @@ -149,5 +154,17 @@ public DestructuringOptionsBuilder WithDestructuringDepth(int destructuringDepth
this.DestructuringDepth = destructuringDepth;
return this;
}

/// <summary>
/// Disable reflection based destructurer.
/// You may want to disable this destructurer if you need full control
/// over the process of destructuring and want to provide all the destructurers yourself.
/// </summary>
/// <returns>Options builder for method chaining.</returns>
public DestructuringOptionsBuilder WithoutReflectionBasedDestructurer()
{
this.DisableReflectionBasedDestructurer = true;
return this;
}
}
}
}
29 changes: 20 additions & 9 deletions Source/Serilog.Exceptions/Core/ExceptionEnricher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,41 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)

if (logEvent.Exception != null)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
this.destructuringOptions.RootName,
this.DestructureException(logEvent.Exception),
true));
var dataDictionary = this.DestructureException(logEvent.Exception);

if (dataDictionary != null)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
this.destructuringOptions.RootName,
dataDictionary,
true));
}
}
}

private IReadOnlyDictionary<string, object> DestructureException(Exception exception)
{
var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter);

var exceptionType = exception.GetType();

if (this.destructurers.ContainsKey(exceptionType))
{
var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter);

var destructurer = this.destructurers[exceptionType];
destructurer.Destructure(exception, data, this.DestructureException);

return data.GetResultDictionary();
}
else
else if (!this.destructuringOptions.DisableReflectionBasedDestructurer)
{
var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter);

this.reflectionBasedDestructurer.Destructure(exception, data, this.DestructureException);

return data.GetResultDictionary();
}

return data.GetResultDictionary();
return null;
}
}
}
}
9 changes: 8 additions & 1 deletion Source/Serilog.Exceptions/Core/IDestructuringOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,12 @@ public interface IDestructuringOptions
/// Filter is applied to every property regardless of which destructurer was used.
/// </summary>
IExceptionPropertyFilter Filter { get; }

/// <summary>
/// Decides whether to disable reflection based destructurer.
/// You may want to disable this destructurer if you need full control
/// over the process of destructuring and want to provide all the destructurers yourself.
/// </summary>
bool DisableReflectionBasedDestructurer { get; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Serilog.Exceptions
public static class LoggerEnrichmentConfigurationExtensions
{
/// <summary>
/// Enrich logger output with a destuctured object containing exception's public properties. Default
/// Enrich logger output with a destructured object containing exception's public properties. Default
/// destructurers are registered. <see cref="Exception.StackTrace"/> and Exception.TargetSite
/// are omitted by the destructuring process because Serilog already attaches them to log event.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,39 @@ public void PassedFilter_IsCalledWithCorrectArguments()
filter.Received().ShouldPropertyBeFiltered(exception, "StackTrace", null);
}

[Fact]
public void WithoutReflectionBasedDestructurer_CustomExceptionIsNotLogged()
{
// Arrange
var exception = new ExceptionWithDictNonScalarKey();
var options = new DestructuringOptionsBuilder().WithoutReflectionBasedDestructurer();

// Act
var rootObject = LogAndDestructureException(exception, options);

// Assert
rootObject.Properties().Should().NotContain(x => x.Name == "Properties");
}

[Fact]
public void WithoutReflectionBasedDestructurerAndCustomRootName_StandardExceptionIsLogged()
{
// Arrange
var exception = new ArgumentException("ARG", "arg");
var options = new DestructuringOptionsBuilder()
.WithDefaultDestructurers()
.WithoutReflectionBasedDestructurer()
.WithRootName("CUSTOM-ROOT");

// Act
var rootObject = LogAndDestructureException(exception, options);

// Assert
var exceptionObject = ExtractExceptionDetails(rootObject, "CUSTOM-ROOT");
var paramObject = exceptionObject.Properties().Should().ContainSingle(x => x.Name == "ParamName").Which;
paramObject.Value.Should().BeOfType<JValue>().Which.Value.Should().Be("arg");
}

[Fact]
public void ArgumentException_WithStackTrace_ContainsStackTrace()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,23 @@ public static JArray ExtractInnerExceptionsProperty(JObject jObject)
return innerExceptionsValue;
}

public static JObject ExtractExceptionDetails(JObject jObject, string rootName = "ExceptionDetail")
public static JObject ExtractExceptionDetails(JObject rootObject, string rootName = "ExceptionDetail")
{
var propertiesProperty = Assert.Single(jObject.Properties(), x => x.Name == "Properties");
var propertiesObject = Assert.IsType<JObject>(propertiesProperty.Value);
var propertiesObject = ExtractPropertiesObject(rootObject);

var exceptionDetailProperty = Assert.Single(propertiesObject.Properties(), x => x.Name == rootName);
var exceptionDetailValue = Assert.IsType<JObject>(exceptionDetailProperty.Value);

return exceptionDetailValue;
}

public static JObject ExtractPropertiesObject(JObject rootObject)
{
var propertiesProperty = Assert.Single(rootObject.Properties(), x => x.Name == "Properties");
var propertiesObject = Assert.IsType<JObject>(propertiesProperty.Value);
return propertiesObject;
}

public static void Assert_ContainsPropertyWithValue(
JObject jObject,
string propertyKey,
Expand Down