From 18253816f2b4b668eda1e327ae49567aac3dca92 Mon Sep 17 00:00:00 2001 From: Muhammad Rehan Saeed Date: Tue, 9 Nov 2021 10:26:02 +0000 Subject: [PATCH] Bump .editorconfig & use file scoped namespaces --- .editorconfig | 6 +- .../BenchmarkException.cs | 53 +- .../BenchmarkExceptionDestructurer.cs | 49 +- .../DestructuringBenchmark.cs | 115 +- .../ExceptionPropertiesBag.cs | 83 +- .../Serilog.Exceptions.Benchmark/Point.cs | 15 +- .../Serilog.Exceptions.Benchmark/Program.cs | 13 +- .../DbUpdateExceptionDestructurer.cs | 81 +- .../Destructurers/SqlExceptionDestructurer.cs | 61 +- .../Destructurers/ApiExceptionDestructurer.cs | 113 +- .../Destructurers/SqlExceptionDestructurer.cs | 61 +- .../Core/DestructuringOptionsBuilder.cs | 309 +++-- .../Core/ExceptionEnricher.cs | 157 ++- .../Core/ExceptionPropertiesBag.cs | 169 ++- .../Core/IDestructuringOptions.cs | 71 +- .../Core/IExceptionPropertiesBag.cs | 51 +- .../AggregateExceptionDestructurer.cs | 47 +- .../ArgumentExceptionDestructurer.cs | 49 +- ...ArgumentOutOfRangeExceptionDestructurer.cs | 47 +- .../Destructurers/ExceptionDestructurer.cs | 165 ++- .../Destructurers/IExceptionDestructurer.cs | 51 +- .../OperationCanceledExceptionDestructurer.cs | 75 +- .../ReflectionBasedDestructurer.cs | 565 +++++---- ...ReflectionTypeLoadExceptionDestructurer.cs | 79 +- .../SocketExceptionDestructurer.cs | 159 ++- .../TaskCanceledExceptionDestructurer.cs | 107 +- .../DictionaryExtensions.cs | 49 +- .../CompositeExceptionPropertyFilter.cs | 85 +- .../Filters/IExceptionPropertyFilter.cs | 31 +- .../IgnorePropertyByNameExceptionFilter.cs | 63 +- ...LoggerEnrichmentConfigurationExtensions.cs | 97 +- .../Reflection/ReflectionInfo.cs | 55 +- .../Reflection/ReflectionInfoExtractor.cs | 165 ++- .../Reflection/ReflectionPropertyInfo.cs | 155 ++- Source/Serilog.Exceptions/TypeExtensions.cs | 169 ++- .../AggregateExceptionDestructurerTest.cs | 37 +- .../ApiExceptionDestructurerTest.cs | 165 ++- .../ArgumentExceptionDestructurerTest.cs | 23 +- .../ArgumentNullExceptionDestructurerTest.cs | 23 +- ...mentOutOfRangeExceptionDestructurerTest.cs | 35 +- .../DbUpdateExceptionDestructurerTest.cs | 175 ++- .../ExceptionDestructurerTest.cs | 375 +++--- .../ExceptionPropertiesBagTest.cs | 189 ++- .../Destructurers/LogJsonOutputUtils.cs | 257 ++-- ...rationCanceledExceptionDestructurerTest.cs | 53 +- .../ReflectionBasedDestructurerTest.cs | 1037 ++++++++--------- .../SocketExceptionDestructurerTest.cs | 47 +- .../TaskCanceledExceptionDestructurerTest.cs | 189 ++- .../DictionaryExtensionsTest.cs | 51 +- .../CompositeExceptionPropertyFilterTest.cs | 137 ++- .../Reflection/ReflectionInfoExtractorTest.cs | 115 +- Tools/ExceptionFinderTool/Program.cs | 59 +- 52 files changed, 3269 insertions(+), 3318 deletions(-) diff --git a/.editorconfig b/.editorconfig index 4264c50d..a51fac03 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ -# Version: 3.0.0 (Using https://semver.org/) -# Updated: 2021-08-09 +# Version: 4.0.0 (Using https://semver.org/) +# Updated: 2021-10-12 # See https://github.com/RehanSaeed/EditorConfig/releases for release notes. # See https://github.com/RehanSaeed/EditorConfig for updates to this file. # See http://EditorConfig.org for more information about .editorconfig files. @@ -167,6 +167,8 @@ dotnet_diagnostic.IDE0063.severity = suggestion csharp_using_directive_placement = inside_namespace:warning # Modifier preferences csharp_prefer_static_local_function = true:warning +# Undocumented +csharp_style_namespace_declarations = file_scoped:warning ########################################## # Unnecessary Code Rules diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkException.cs b/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkException.cs index 8725d9f9..41348798 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkException.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkException.cs @@ -1,36 +1,35 @@ -namespace Serilog.Exceptions.Benchmark -{ - using System; - using System.Runtime.Serialization; +namespace Serilog.Exceptions.Benchmark; + +using System; +using System.Runtime.Serialization; - [Serializable] - public class BenchmarkException : Exception +[Serializable] +public class BenchmarkException : Exception +{ + public BenchmarkException() { - public BenchmarkException() - { - } + } - public BenchmarkException(string message) - : base(message) - { - } + public BenchmarkException(string message) + : base(message) + { + } - public BenchmarkException(string message, Exception inner) - : base(message, inner) - { - } + public BenchmarkException(string message, Exception inner) + : base(message, inner) + { + } - protected BenchmarkException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } + protected BenchmarkException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { + } - public string? ParamString { get; set; } + public string? ParamString { get; set; } - public int ParamInt { get; set; } + public int ParamInt { get; set; } - public Point? Point { get; set; } - } + public Point? Point { get; set; } } diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkExceptionDestructurer.cs b/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkExceptionDestructurer.cs index 1a2acc61..e8ee053b 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkExceptionDestructurer.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/BenchmarkExceptionDestructurer.cs @@ -1,37 +1,36 @@ -namespace Serilog.Exceptions.Benchmark +namespace Serilog.Exceptions.Benchmark; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; + +/// +/// A destructurer used in benchmarks. +/// +/// +public class BenchmarkExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; + /// + public override Type[] TargetTypes => new[] { typeof(BenchmarkException) }; - /// - /// A destructurer used in benchmarks. - /// - /// - public class BenchmarkExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(BenchmarkException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var benchmarkException = (BenchmarkException)exception; - propertiesBag.AddProperty("ParamString", benchmarkException.ParamString); - propertiesBag.AddProperty("ParamInt", benchmarkException.ParamInt); - propertiesBag.AddProperty("Point", new Dictionary + var benchmarkException = (BenchmarkException)exception; + propertiesBag.AddProperty("ParamString", benchmarkException.ParamString); + propertiesBag.AddProperty("ParamInt", benchmarkException.ParamInt); + propertiesBag.AddProperty("Point", new Dictionary { { "X", benchmarkException.Point?.X }, { "Y", benchmarkException.Point?.Y }, }); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/DestructuringBenchmark.cs b/Benchmarks/Serilog.Exceptions.Benchmark/DestructuringBenchmark.cs index e7854bfc..50462e2e 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/DestructuringBenchmark.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/DestructuringBenchmark.cs @@ -1,74 +1,73 @@ -namespace Serilog.Exceptions.Benchmark +namespace Serilog.Exceptions.Benchmark; + +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using Serilog.Exceptions.Destructurers; + +[KeepBenchmarkFiles] +[MemoryDiagnoser] +[MinColumn] +[MaxColumn] +[HtmlExporter] +[CsvMeasurementsExporter] +[RPlotExporter] +[SimpleJob(RuntimeMoniker.Net60)] +[SimpleJob(RuntimeMoniker.Net472)] +public class DestructuringBenchmark { - using System; - using System.Collections.Generic; - using BenchmarkDotNet.Attributes; - using BenchmarkDotNet.Jobs; - using Serilog.Exceptions.Destructurers; + private readonly ReflectionBasedDestructurer reflectionBasedDestructurer = new(10); + private readonly BenchmarkExceptionDestructurer benchmarkExceptionDestructurer = new(); + private BenchmarkException benchmarkException = default!; - [KeepBenchmarkFiles] - [MemoryDiagnoser] - [MinColumn] - [MaxColumn] - [HtmlExporter] - [CsvMeasurementsExporter] - [RPlotExporter] - [SimpleJob(RuntimeMoniker.Net60)] - [SimpleJob(RuntimeMoniker.Net472)] - public class DestructuringBenchmark + [GlobalSetup] + public void Setup() { - private readonly ReflectionBasedDestructurer reflectionBasedDestructurer = new(10); - private readonly BenchmarkExceptionDestructurer benchmarkExceptionDestructurer = new(); - private BenchmarkException benchmarkException = default!; - - [GlobalSetup] - public void Setup() + try { - try - { - throw new BenchmarkException() - { - ParamInt = 123, - ParamString = "some param value", - Point = new Point() { X = 666, Y = 777 }, - }; - } - catch (BenchmarkException ex) + throw new BenchmarkException() { - this.benchmarkException = ex; - } + ParamInt = 123, + ParamString = "some param value", + Point = new Point() { X = 666, Y = 777 }, + }; } - - public IReadOnlyDictionary DestructureUsingReflectionDestructurer(Exception ex) + catch (BenchmarkException ex) { - var bag = new ExceptionPropertiesBag(ex); + this.benchmarkException = ex; + } + } - this.reflectionBasedDestructurer.Destructure( - ex, - bag, - _ => new Dictionary()); + public IReadOnlyDictionary DestructureUsingReflectionDestructurer(Exception ex) + { + var bag = new ExceptionPropertiesBag(ex); - return bag.GetResultDictionary(); - } + this.reflectionBasedDestructurer.Destructure( + ex, + bag, + _ => new Dictionary()); - [Benchmark] - public IReadOnlyDictionary ReflectionDestructurer() => - this.DestructureUsingReflectionDestructurer(this.benchmarkException); + return bag.GetResultDictionary(); + } - public IReadOnlyDictionary DestructureUsingCustomDestructurer(Exception ex) - { - var bag = new ExceptionPropertiesBag(ex); + [Benchmark] + public IReadOnlyDictionary ReflectionDestructurer() => + this.DestructureUsingReflectionDestructurer(this.benchmarkException); - this.benchmarkExceptionDestructurer.Destructure( - ex, - bag, - _ => new Dictionary()); + public IReadOnlyDictionary DestructureUsingCustomDestructurer(Exception ex) + { + var bag = new ExceptionPropertiesBag(ex); - return bag.GetResultDictionary(); - } + this.benchmarkExceptionDestructurer.Destructure( + ex, + bag, + _ => new Dictionary()); - [Benchmark] - public IReadOnlyDictionary CustomDestructurer() => - this.DestructureUsingCustomDestructurer(this.benchmarkException); + return bag.GetResultDictionary(); } + + [Benchmark] + public IReadOnlyDictionary CustomDestructurer() => + this.DestructureUsingCustomDestructurer(this.benchmarkException); } diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/ExceptionPropertiesBag.cs b/Benchmarks/Serilog.Exceptions.Benchmark/ExceptionPropertiesBag.cs index 744a09df..cf8e544c 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/ExceptionPropertiesBag.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/ExceptionPropertiesBag.cs @@ -1,60 +1,59 @@ -namespace Serilog.Exceptions.Benchmark +namespace Serilog.Exceptions.Benchmark; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Filters; + +internal class ExceptionPropertiesBag : IExceptionPropertiesBag { - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Filters; + private readonly Exception exception; + private readonly IExceptionPropertyFilter? filter; + private readonly Dictionary properties = new(); + + // We keep a note on whether the results were collected to be sure that + // after that there are no changes. This is the application of fail-fast principle. + private bool resultsCollected; - internal class ExceptionPropertiesBag : IExceptionPropertiesBag + public ExceptionPropertiesBag(Exception exception, IExceptionPropertyFilter? filter = null) { - private readonly Exception exception; - private readonly IExceptionPropertyFilter? filter; - private readonly Dictionary properties = new(); + this.exception = exception ?? throw new ArgumentNullException(nameof(exception)); + this.filter = filter; + } - // We keep a note on whether the results were collected to be sure that - // after that there are no changes. This is the application of fail-fast principle. - private bool resultsCollected; + public IReadOnlyDictionary GetResultDictionary() + { + this.resultsCollected = true; + return this.properties; + } - public ExceptionPropertiesBag(Exception exception, IExceptionPropertyFilter? filter = null) + public void AddProperty(string key, object? value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(key); +#else + if (key is null) { - this.exception = exception ?? throw new ArgumentNullException(nameof(exception)); - this.filter = filter; + throw new ArgumentNullException(nameof(key)); } +#endif - public IReadOnlyDictionary GetResultDictionary() + if (this.resultsCollected) { - this.resultsCollected = true; - return this.properties; + throw new InvalidOperationException( + $"Cannot add exception property '{key}' to bag, after results were already collected"); } - public void AddProperty(string key, object? value) + if (this.filter is not null) { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(key); -#else - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } -#endif - - if (this.resultsCollected) + if (this.filter.ShouldPropertyBeFiltered(this.exception, key, value)) { - throw new InvalidOperationException( - $"Cannot add exception property '{key}' to bag, after results were already collected"); + return; } - - if (this.filter is not null) - { - if (this.filter.ShouldPropertyBeFiltered(this.exception, key, value)) - { - return; - } - } - - this.properties.Add(key, value); } - public bool ContainsProperty(string key) => this.properties.ContainsKey(key); + this.properties.Add(key, value); } + + public bool ContainsProperty(string key) => this.properties.ContainsKey(key); } diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/Point.cs b/Benchmarks/Serilog.Exceptions.Benchmark/Point.cs index 5bee0181..dced5c3d 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/Point.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/Point.cs @@ -1,15 +1,14 @@ -namespace Serilog.Exceptions.Benchmark +namespace Serilog.Exceptions.Benchmark; + +public class Point { - public class Point - { - public Point() => this.Z = 3; + public Point() => this.Z = 3; - public int X { get; set; } + public int X { get; set; } - public int Y { get; set; } + public int Y { get; set; } #pragma warning disable IDE0052 // Remove unread private members - private int Z { get; set; } + private int Z { get; set; } #pragma warning restore IDE0052 // Remove unread private members - } } diff --git a/Benchmarks/Serilog.Exceptions.Benchmark/Program.cs b/Benchmarks/Serilog.Exceptions.Benchmark/Program.cs index eba2fe14..9168a5ae 100644 --- a/Benchmarks/Serilog.Exceptions.Benchmark/Program.cs +++ b/Benchmarks/Serilog.Exceptions.Benchmark/Program.cs @@ -1,9 +1,8 @@ -namespace Serilog.Exceptions.Benchmark -{ - using BenchmarkDotNet.Running; +namespace Serilog.Exceptions.Benchmark; + +using BenchmarkDotNet.Running; - public static class Program - { - public static void Main() => BenchmarkRunner.Run(); - } +public static class Program +{ + public static void Main() => BenchmarkRunner.Run(); } diff --git a/Source/Serilog.Exceptions.EntityFrameworkCore/Destructurers/DbUpdateExceptionDestructurer.cs b/Source/Serilog.Exceptions.EntityFrameworkCore/Destructurers/DbUpdateExceptionDestructurer.cs index 905773d1..966f12ea 100644 --- a/Source/Serilog.Exceptions.EntityFrameworkCore/Destructurers/DbUpdateExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions.EntityFrameworkCore/Destructurers/DbUpdateExceptionDestructurer.cs @@ -1,49 +1,48 @@ -namespace Serilog.Exceptions.EntityFrameworkCore.Destructurers +namespace Serilog.Exceptions.EntityFrameworkCore.Destructurers; + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; + +/// +/// A destructurer for . +/// +/// +public class DbUpdateExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Linq; - using Microsoft.EntityFrameworkCore; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; + /// + public override Type[] TargetTypes => new[] { typeof(DbUpdateException), typeof(DbUpdateConcurrencyException) }; - /// - /// A destructurer for . - /// - /// - public class DbUpdateExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(DbUpdateException), typeof(DbUpdateConcurrencyException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var dbUpdateException = (DbUpdateException)exception; - var entriesValue = dbUpdateException.Entries? - .Select( - e => new - { - EntryProperties = e.Properties.Select( - p => new - { - PropertyName = p.Metadata.Name, - p.OriginalValue, - p.CurrentValue, - p.IsTemporary, - p.IsModified, - }), - e.State, - }) - .ToList(); - propertiesBag.AddProperty(nameof(DbUpdateException.Entries), entriesValue); + var dbUpdateException = (DbUpdateException)exception; + var entriesValue = dbUpdateException.Entries? + .Select( + e => new + { + EntryProperties = e.Properties.Select( + p => new + { + PropertyName = p.Metadata.Name, + p.OriginalValue, + p.CurrentValue, + p.IsTemporary, + p.IsModified, + }), + e.State, + }) + .ToList(); + propertiesBag.AddProperty(nameof(DbUpdateException.Entries), entriesValue); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions.MsSqlServer/Destructurers/SqlExceptionDestructurer.cs b/Source/Serilog.Exceptions.MsSqlServer/Destructurers/SqlExceptionDestructurer.cs index 1bcf4925..136e4d21 100644 --- a/Source/Serilog.Exceptions.MsSqlServer/Destructurers/SqlExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions.MsSqlServer/Destructurers/SqlExceptionDestructurer.cs @@ -1,39 +1,38 @@ -namespace Serilog.Exceptions.MsSqlServer.Destructurers +namespace Serilog.Exceptions.MsSqlServer.Destructurers; + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Data.SqlClient; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; + +/// +/// A destructurer for . +/// +/// +public class SqlExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Linq; - using Microsoft.Data.SqlClient; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; + /// + public override Type[] TargetTypes => new[] { typeof(SqlException) }; - /// - /// A destructurer for . - /// - /// - public class SqlExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(SqlException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var sqlException = (SqlException)exception; - propertiesBag.AddProperty(nameof(SqlException.ClientConnectionId), sqlException.ClientConnectionId); - propertiesBag.AddProperty(nameof(SqlException.Class), sqlException.Class); - propertiesBag.AddProperty(nameof(SqlException.LineNumber), sqlException.LineNumber); - propertiesBag.AddProperty(nameof(SqlException.Number), sqlException.Number); - propertiesBag.AddProperty(nameof(SqlException.Server), sqlException.Server); - propertiesBag.AddProperty(nameof(SqlException.State), sqlException.State); - propertiesBag.AddProperty(nameof(SqlException.Errors), sqlException.Errors.Cast().ToArray()); + var sqlException = (SqlException)exception; + propertiesBag.AddProperty(nameof(SqlException.ClientConnectionId), sqlException.ClientConnectionId); + propertiesBag.AddProperty(nameof(SqlException.Class), sqlException.Class); + propertiesBag.AddProperty(nameof(SqlException.LineNumber), sqlException.LineNumber); + propertiesBag.AddProperty(nameof(SqlException.Number), sqlException.Number); + propertiesBag.AddProperty(nameof(SqlException.Server), sqlException.Server); + propertiesBag.AddProperty(nameof(SqlException.State), sqlException.State); + propertiesBag.AddProperty(nameof(SqlException.Errors), sqlException.Errors.Cast().ToArray()); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions.Refit/Destructurers/ApiExceptionDestructurer.cs b/Source/Serilog.Exceptions.Refit/Destructurers/ApiExceptionDestructurer.cs index bf02fed7..079fd60e 100644 --- a/Source/Serilog.Exceptions.Refit/Destructurers/ApiExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions.Refit/Destructurers/ApiExceptionDestructurer.cs @@ -1,77 +1,76 @@ -namespace Serilog.Exceptions.Refit.Destructurers +namespace Serilog.Exceptions.Refit.Destructurers; + +using System; +using System.Collections.Generic; +using global::Refit; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; + +/// +/// A destructurer for the Refit . +/// +/// +public class ApiExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using global::Refit; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; + private readonly bool destructureCommonExceptionProperties; + private readonly bool destructureHttpContent; /// - /// A destructurer for the Refit . + /// Initializes a new instance of the class. /// - /// - public class ApiExceptionDestructurer : ExceptionDestructurer + /// Destructure common public Exception properties or not. + /// Destructure the HTTP body. This is left optional due to possible security and log size concerns. + public ApiExceptionDestructurer(bool destructureCommonExceptionProperties = true, bool destructureHttpContent = false) { - private readonly bool destructureCommonExceptionProperties; - private readonly bool destructureHttpContent; + this.destructureCommonExceptionProperties = destructureCommonExceptionProperties; + this.destructureHttpContent = destructureHttpContent; + } - /// - /// Initializes a new instance of the class. - /// - /// Destructure common public Exception properties or not. - /// Destructure the HTTP body. This is left optional due to possible security and log size concerns. - public ApiExceptionDestructurer(bool destructureCommonExceptionProperties = true, bool destructureHttpContent = false) + /// + public override Type[] TargetTypes => new[] { typeof(ApiException) }; + + /// + public override void Destructure(Exception exception, IExceptionPropertiesBag propertiesBag, Func?> destructureException) + { + if (this.destructureCommonExceptionProperties) { - this.destructureCommonExceptionProperties = destructureCommonExceptionProperties; - this.destructureHttpContent = destructureHttpContent; + base.Destructure(exception, propertiesBag, destructureException); } - - /// - public override Type[] TargetTypes => new[] { typeof(ApiException) }; - - /// - public override void Destructure(Exception exception, IExceptionPropertiesBag propertiesBag, Func?> destructureException) + else { - if (this.destructureCommonExceptionProperties) - { - base.Destructure(exception, propertiesBag, destructureException); - } - else - { - // Argument checks are usually done in - // but as we didn't call this method we need to do the checks here. + // Argument checks are usually done in + // but as we didn't call this method we need to do the checks here. #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(exception); - ArgumentNullException.ThrowIfNull(propertiesBag); - ArgumentNullException.ThrowIfNull(destructureException); + ArgumentNullException.ThrowIfNull(exception); + ArgumentNullException.ThrowIfNull(propertiesBag); + ArgumentNullException.ThrowIfNull(destructureException); #else - if (exception is null) - { - throw new ArgumentNullException(nameof(exception)); - } - - if (propertiesBag is null) - { - throw new ArgumentNullException(nameof(propertiesBag)); - } + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } - if (destructureException is null) - { - throw new ArgumentNullException(nameof(destructureException)); - } -#endif + if (propertiesBag is null) + { + throw new ArgumentNullException(nameof(propertiesBag)); } -#pragma warning disable CA1062 // Validate arguments of public methods - var apiException = (ApiException)exception; - if (this.destructureHttpContent) + if (destructureException is null) { - propertiesBag.AddProperty(nameof(ApiException.Content), apiException.Content); + throw new ArgumentNullException(nameof(destructureException)); } +#endif + } - propertiesBag.AddProperty(nameof(ApiException.Uri), apiException.Uri); - propertiesBag.AddProperty(nameof(ApiException.StatusCode), apiException.StatusCode); -#pragma warning restore CA1062 // Validate arguments of public methods +#pragma warning disable CA1062 // Validate arguments of public methods + var apiException = (ApiException)exception; + if (this.destructureHttpContent) + { + propertiesBag.AddProperty(nameof(ApiException.Content), apiException.Content); } + + propertiesBag.AddProperty(nameof(ApiException.Uri), apiException.Uri); + propertiesBag.AddProperty(nameof(ApiException.StatusCode), apiException.StatusCode); +#pragma warning restore CA1062 // Validate arguments of public methods } } diff --git a/Source/Serilog.Exceptions.SqlServer/Destructurers/SqlExceptionDestructurer.cs b/Source/Serilog.Exceptions.SqlServer/Destructurers/SqlExceptionDestructurer.cs index 3a8de991..f903c469 100644 --- a/Source/Serilog.Exceptions.SqlServer/Destructurers/SqlExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions.SqlServer/Destructurers/SqlExceptionDestructurer.cs @@ -1,39 +1,38 @@ -namespace Serilog.Exceptions.SqlServer.Destructurers +namespace Serilog.Exceptions.SqlServer.Destructurers; + +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; + +/// +/// A destructurer for . +/// +/// +public class SqlExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Data.SqlClient; - using System.Linq; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; + /// + public override Type[] TargetTypes => new[] { typeof(SqlException) }; - /// - /// A destructurer for . - /// - /// - public class SqlExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(SqlException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var sqlException = (SqlException)exception; - propertiesBag.AddProperty(nameof(SqlException.ClientConnectionId), sqlException.ClientConnectionId); - propertiesBag.AddProperty(nameof(SqlException.Class), sqlException.Class); - propertiesBag.AddProperty(nameof(SqlException.LineNumber), sqlException.LineNumber); - propertiesBag.AddProperty(nameof(SqlException.Number), sqlException.Number); - propertiesBag.AddProperty(nameof(SqlException.Server), sqlException.Server); - propertiesBag.AddProperty(nameof(SqlException.State), sqlException.State); - propertiesBag.AddProperty(nameof(SqlException.Errors), sqlException.Errors.Cast().ToArray()); + var sqlException = (SqlException)exception; + propertiesBag.AddProperty(nameof(SqlException.ClientConnectionId), sqlException.ClientConnectionId); + propertiesBag.AddProperty(nameof(SqlException.Class), sqlException.Class); + propertiesBag.AddProperty(nameof(SqlException.LineNumber), sqlException.LineNumber); + propertiesBag.AddProperty(nameof(SqlException.Number), sqlException.Number); + propertiesBag.AddProperty(nameof(SqlException.Server), sqlException.Server); + propertiesBag.AddProperty(nameof(SqlException.State), sqlException.State); + propertiesBag.AddProperty(nameof(SqlException.Errors), sqlException.Errors.Cast().ToArray()); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions/Core/DestructuringOptionsBuilder.cs b/Source/Serilog.Exceptions/Core/DestructuringOptionsBuilder.cs index 100cf487..12460026 100644 --- a/Source/Serilog.Exceptions/Core/DestructuringOptionsBuilder.cs +++ b/Source/Serilog.Exceptions/Core/DestructuringOptionsBuilder.cs @@ -1,185 +1,184 @@ -namespace Serilog.Exceptions.Core +namespace Serilog.Exceptions.Core; + +using System; +using System.Collections.Generic; +using System.Globalization; +using Serilog.Events; +using Serilog.Exceptions.Destructurers; +using Serilog.Exceptions.Filters; + +/// +/// Accumulates destructuring options to be used by the . +/// +public class DestructuringOptionsBuilder : IDestructuringOptions { - using System; - using System.Collections.Generic; - using System.Globalization; - using Serilog.Events; - using Serilog.Exceptions.Destructurers; - using Serilog.Exceptions.Filters; - /// - /// Accumulates destructuring options to be used by the . + /// Default set of destructurers. Destructurers cover all of the exceptions from standard library. /// - public class DestructuringOptionsBuilder : IDestructuringOptions + public static readonly IExceptionDestructurer[] DefaultDestructurers = { - /// - /// Default set of destructurers. Destructurers cover all of the exceptions from standard library. - /// - public static readonly IExceptionDestructurer[] DefaultDestructurers = - { - new ExceptionDestructurer(), - new ArgumentExceptionDestructurer(), - new ArgumentOutOfRangeExceptionDestructurer(), - new AggregateExceptionDestructurer(), - new ReflectionTypeLoadExceptionDestructurer(), - new OperationCanceledExceptionDestructurer(), - new TaskCanceledExceptionDestructurer(), - new SocketExceptionDestructurer(), - }; - - /// - /// Filter that ignores and Exception.TargetSite properties. - /// Usually, they can be safely ignored, because Serilog attaches them tog already. - /// - public static readonly IExceptionPropertyFilter IgnoreStackTraceAndTargetSiteExceptionFilter = + new ExceptionDestructurer(), + new ArgumentExceptionDestructurer(), + new ArgumentOutOfRangeExceptionDestructurer(), + new AggregateExceptionDestructurer(), + new ReflectionTypeLoadExceptionDestructurer(), + new OperationCanceledExceptionDestructurer(), + new TaskCanceledExceptionDestructurer(), + new SocketExceptionDestructurer(), + }; + + /// + /// Filter that ignores and Exception.TargetSite properties. + /// Usually, they can be safely ignored, because Serilog attaches them tog already. + /// + public static readonly IExceptionPropertyFilter IgnoreStackTraceAndTargetSiteExceptionFilter = #if NET461 || NET472 new IgnorePropertyByNameExceptionFilter( - nameof(Exception.StackTrace), - nameof(Exception.TargetSite)); + nameof(Exception.StackTrace), + nameof(Exception.TargetSite)); #else new IgnorePropertyByNameExceptionFilter( - nameof(Exception.StackTrace)); + nameof(Exception.StackTrace)); #endif - private readonly List destructurers = new(); - - /// - /// Gets the name of the property which value will be filled with destructured exception. - /// - public string RootName { get; private set; } = "ExceptionDetail"; - - /// - /// Gets the maximum depth of destructuring to which reflection destructurer will proceed. - /// - public int DestructuringDepth { get; private set; } = 10; - - /// - /// Gets a value indicating whether to disable the reflection based destructurer. - /// - public bool DisableReflectionBasedDestructurer { get; private set; } - - /// - /// Gets a collection of destructurers that will be used to handle exception. - /// - public IEnumerable Destructurers => this.destructurers; - - /// - /// Gets a global filter, that will be applied to every destructured property just before it is added to the result. - /// - public IExceptionPropertyFilter? Filter { get; private set; } - - /// - /// Accumulates destructurers to be used by . - /// - /// Collection of destructurers. - /// Options builder for method chaining. - public DestructuringOptionsBuilder WithDestructurers(IEnumerable destructurers) - { + private readonly List destructurers = new(); + + /// + /// Gets the name of the property which value will be filled with destructured exception. + /// + public string RootName { get; private set; } = "ExceptionDetail"; + + /// + /// Gets the maximum depth of destructuring to which reflection destructurer will proceed. + /// + public int DestructuringDepth { get; private set; } = 10; + + /// + /// Gets a value indicating whether to disable the reflection based destructurer. + /// + public bool DisableReflectionBasedDestructurer { get; private set; } + + /// + /// Gets a collection of destructurers that will be used to handle exception. + /// + public IEnumerable Destructurers => this.destructurers; + + /// + /// Gets a global filter, that will be applied to every destructured property just before it is added to the result. + /// + public IExceptionPropertyFilter? Filter { get; private set; } + + /// + /// Accumulates destructurers to be used by . + /// + /// Collection of destructurers. + /// Options builder for method chaining. + public DestructuringOptionsBuilder WithDestructurers(IEnumerable destructurers) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(destructurers); + ArgumentNullException.ThrowIfNull(destructurers); #else - if (destructurers is null) - { - throw new ArgumentNullException(nameof(destructurers)); - } + if (destructurers is null) + { + throw new ArgumentNullException(nameof(destructurers)); + } #endif - this.destructurers.AddRange(destructurers); - return this; - } + this.destructurers.AddRange(destructurers); + return this; + } + + /// + /// Adds destructurers for a known set of exceptions from standard library. + /// + /// Options builder for method chaining. + public DestructuringOptionsBuilder WithDefaultDestructurers() => + this.WithDestructurers(DefaultDestructurers); - /// - /// Adds destructurers for a known set of exceptions from standard library. - /// - /// Options builder for method chaining. - public DestructuringOptionsBuilder WithDefaultDestructurers() => - this.WithDestructurers(DefaultDestructurers); - - /// - /// Sets a filter that will be used by . Only one filter can be set, second - /// invocation of this method throws . - /// - /// The filter. - /// Options builder for method chaining. - public DestructuringOptionsBuilder WithFilter(IExceptionPropertyFilter filter) + /// + /// Sets a filter that will be used by . Only one filter can be set, second + /// invocation of this method throws . + /// + /// The filter. + /// Options builder for method chaining. + public DestructuringOptionsBuilder WithFilter(IExceptionPropertyFilter filter) + { + if (this.Filter is not null) { - if (this.Filter is not null) - { - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, Resources.FilterAlreadySet, nameof(CompositeExceptionPropertyFilter))); - } - - this.Filter = filter; - return this; + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, Resources.FilterAlreadySet, nameof(CompositeExceptionPropertyFilter))); } - /// - /// Sets a filter that will be used by . The filter ignores - /// and Exception.TargetSite properties. Only one filter can - /// be set, second invocation of this method throws . - /// - /// Options builder for method chaining. - public DestructuringOptionsBuilder WithIgnoreStackTraceAndTargetSiteExceptionFilter() => - this.WithFilter(IgnoreStackTraceAndTargetSiteExceptionFilter); - - /// - /// Sets a property name that will be used by . - /// - /// The name of root property. - /// Options builder for method chaining. - /// Name cannot be null or empty. - public DestructuringOptionsBuilder WithRootName(string rootName) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(rootName); -#else - if (rootName is null) - { - throw new ArgumentNullException(nameof(rootName)); - } -#endif + this.Filter = filter; + return this; + } - if (rootName.Length == 0) - { - throw new ArgumentException(Resources.CannotBeEmpty, nameof(rootName)); - } + /// + /// Sets a filter that will be used by . The filter ignores + /// and Exception.TargetSite properties. Only one filter can + /// be set, second invocation of this method throws . + /// + /// Options builder for method chaining. + public DestructuringOptionsBuilder WithIgnoreStackTraceAndTargetSiteExceptionFilter() => + this.WithFilter(IgnoreStackTraceAndTargetSiteExceptionFilter); - this.RootName = rootName; - return this; + /// + /// Sets a property name that will be used by . + /// + /// The name of root property. + /// Options builder for method chaining. + /// Name cannot be null or empty. + public DestructuringOptionsBuilder WithRootName(string rootName) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(rootName); +#else + if (rootName is null) + { + throw new ArgumentNullException(nameof(rootName)); } +#endif - /// - /// Sets a maximum destructuring depth that will reach during destructuring of - /// unknown exception type. - /// - /// Maximum depth, must be positive. - /// Options builder for method chaining. - /// Given depth must be positive. - public DestructuringOptionsBuilder WithDestructuringDepth(int destructuringDepth) + if (rootName.Length == 0) { - if (destructuringDepth <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(destructuringDepth), - destructuringDepth, - Resources.DestructuringDepthMustBeMoreThanZero); - } - - this.DestructuringDepth = destructuringDepth; - return this; + throw new ArgumentException(Resources.CannotBeEmpty, nameof(rootName)); } - /// - /// 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. - /// - /// Options builder for method chaining. - public DestructuringOptionsBuilder WithoutReflectionBasedDestructurer() + this.RootName = rootName; + return this; + } + + /// + /// Sets a maximum destructuring depth that will reach during destructuring of + /// unknown exception type. + /// + /// Maximum depth, must be positive. + /// Options builder for method chaining. + /// Given depth must be positive. + public DestructuringOptionsBuilder WithDestructuringDepth(int destructuringDepth) + { + if (destructuringDepth <= 0) { - this.DisableReflectionBasedDestructurer = true; - return this; + throw new ArgumentOutOfRangeException( + nameof(destructuringDepth), + destructuringDepth, + Resources.DestructuringDepthMustBeMoreThanZero); } + + this.DestructuringDepth = destructuringDepth; + return this; + } + + /// + /// 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. + /// + /// Options builder for method chaining. + public DestructuringOptionsBuilder WithoutReflectionBasedDestructurer() + { + this.DisableReflectionBasedDestructurer = true; + return this; } } diff --git a/Source/Serilog.Exceptions/Core/ExceptionEnricher.cs b/Source/Serilog.Exceptions/Core/ExceptionEnricher.cs index dafbab66..1452f3af 100644 --- a/Source/Serilog.Exceptions/Core/ExceptionEnricher.cs +++ b/Source/Serilog.Exceptions/Core/ExceptionEnricher.cs @@ -1,107 +1,106 @@ -namespace Serilog.Exceptions.Core +namespace Serilog.Exceptions.Core; + +using System; +using System.Collections.Generic; +using Serilog.Core; +using Serilog.Events; +using Serilog.Exceptions.Destructurers; + +/// +/// Enrich a with details about an if present. +/// +public sealed class ExceptionEnricher : ILogEventEnricher { - using System; - using System.Collections.Generic; - using Serilog.Core; - using Serilog.Events; - using Serilog.Exceptions.Destructurers; + private readonly IExceptionDestructurer reflectionBasedDestructurer; + private readonly Dictionary destructurers; + private readonly IDestructuringOptions destructuringOptions; /// - /// Enrich a with details about an if present. + /// Initializes a new instance of the class. /// - public sealed class ExceptionEnricher : ILogEventEnricher + public ExceptionEnricher() + : this(new DestructuringOptionsBuilder().WithDefaultDestructurers()) { - private readonly IExceptionDestructurer reflectionBasedDestructurer; - private readonly Dictionary destructurers; - private readonly IDestructuringOptions destructuringOptions; - - /// - /// Initializes a new instance of the class. - /// - public ExceptionEnricher() - : this(new DestructuringOptionsBuilder().WithDefaultDestructurers()) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The destructuring options, cannot be null. - public ExceptionEnricher(IDestructuringOptions destructuringOptions) - { - this.destructuringOptions = destructuringOptions ?? throw new ArgumentNullException(nameof(destructuringOptions)); - this.reflectionBasedDestructurer = new ReflectionBasedDestructurer(destructuringOptions.DestructuringDepth); + /// + /// Initializes a new instance of the class. + /// + /// The destructuring options, cannot be null. + public ExceptionEnricher(IDestructuringOptions destructuringOptions) + { + this.destructuringOptions = destructuringOptions ?? throw new ArgumentNullException(nameof(destructuringOptions)); + this.reflectionBasedDestructurer = new ReflectionBasedDestructurer(destructuringOptions.DestructuringDepth); - this.destructurers = new Dictionary(); - foreach (var destructurer in this.destructuringOptions.Destructurers) + this.destructurers = new Dictionary(); + foreach (var destructurer in this.destructuringOptions.Destructurers) + { + foreach (var targetType in destructurer.TargetTypes) { - foreach (var targetType in destructurer.TargetTypes) - { - this.destructurers.Add(targetType, destructurer); - } + this.destructurers.Add(targetType, destructurer); } } + } - /// - /// Enriches with a destructured exception's properties. If the exception is not - /// present, no action is taken. - /// - /// Log event that will be enriched. - /// The property factory. - public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) - { + /// + /// Enriches with a destructured exception's properties. If the exception is not + /// present, no action is taken. + /// + /// Log event that will be enriched. + /// The property factory. + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(logEvent); - ArgumentNullException.ThrowIfNull(propertyFactory); + ArgumentNullException.ThrowIfNull(logEvent); + ArgumentNullException.ThrowIfNull(propertyFactory); #else - if (logEvent is null) - { - throw new ArgumentNullException(nameof(logEvent)); - } + if (logEvent is null) + { + throw new ArgumentNullException(nameof(logEvent)); + } - if (propertyFactory is null) - { - throw new ArgumentNullException(nameof(propertyFactory)); - } + if (propertyFactory is null) + { + throw new ArgumentNullException(nameof(propertyFactory)); + } #endif - if (logEvent.Exception is not null) + if (logEvent.Exception is not null) + { + var dataDictionary = this.DestructureException(logEvent.Exception); + + if (dataDictionary is not null) { - var dataDictionary = this.DestructureException(logEvent.Exception); - - if (dataDictionary is not null) - { - logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty( - this.destructuringOptions.RootName, - dataDictionary, - true)); - } + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty( + this.destructuringOptions.RootName, + dataDictionary, + true)); } } + } - private IReadOnlyDictionary? DestructureException(Exception exception) - { - var exceptionType = exception.GetType(); - - if (this.destructurers.ContainsKey(exceptionType)) - { - var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter); + private IReadOnlyDictionary? DestructureException(Exception exception) + { + var exceptionType = exception.GetType(); - var destructurer = this.destructurers[exceptionType]; - destructurer.Destructure(exception, data, this.DestructureException); + if (this.destructurers.ContainsKey(exceptionType)) + { + var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter); - return data.GetResultDictionary(); - } - else if (!this.destructuringOptions.DisableReflectionBasedDestructurer) - { - var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter); + var destructurer = this.destructurers[exceptionType]; + destructurer.Destructure(exception, data, this.DestructureException); - this.reflectionBasedDestructurer.Destructure(exception, data, this.DestructureException); + return data.GetResultDictionary(); + } + else if (!this.destructuringOptions.DisableReflectionBasedDestructurer) + { + var data = new ExceptionPropertiesBag(exception, this.destructuringOptions.Filter); - return data.GetResultDictionary(); - } + this.reflectionBasedDestructurer.Destructure(exception, data, this.DestructureException); - return null; + return data.GetResultDictionary(); } + + return null; } } diff --git a/Source/Serilog.Exceptions/Core/ExceptionPropertiesBag.cs b/Source/Serilog.Exceptions/Core/ExceptionPropertiesBag.cs index a23f7a0b..adf32153 100644 --- a/Source/Serilog.Exceptions/Core/ExceptionPropertiesBag.cs +++ b/Source/Serilog.Exceptions/Core/ExceptionPropertiesBag.cs @@ -1,113 +1,112 @@ -namespace Serilog.Exceptions.Core +namespace Serilog.Exceptions.Core; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Filters; + +/// +internal class ExceptionPropertiesBag : IExceptionPropertiesBag { - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Filters; + /// + /// In theory there should not be any properties with same names passed + /// but just as a defensive and robustness measure, we allow small amount + /// so that library can keep working in case of minor error (or unexpected + /// scenario) on the properties provider side. + /// + private const int AcceptableNumberOfSameNameProperties = 5; + private readonly Exception exception; + private readonly IExceptionPropertyFilter? filter; + private readonly Dictionary properties = new(); - /// - internal class ExceptionPropertiesBag : IExceptionPropertiesBag + /// + /// We keep a note on whether the results were collected to be sure that after that there are no changes. This + /// is the application of fail-fast principle. + /// + private bool resultsCollected; + + /// + /// Initializes a new instance of the class. + /// + /// The exception which properties will be added to the bag. + /// Filter that should be applied to each property just before adding it to the bag. + public ExceptionPropertiesBag(Exception exception, IExceptionPropertyFilter? filter = null) { - /// - /// In theory there should not be any properties with same names passed - /// but just as a defensive and robustness measure, we allow small amount - /// so that library can keep working in case of minor error (or unexpected - /// scenario) on the properties provider side. - /// - private const int AcceptableNumberOfSameNameProperties = 5; - private readonly Exception exception; - private readonly IExceptionPropertyFilter? filter; - private readonly Dictionary properties = new(); + this.exception = exception ?? throw new ArgumentNullException(nameof(exception)); + this.filter = filter; + } - /// - /// We keep a note on whether the results were collected to be sure that after that there are no changes. This - /// is the application of fail-fast principle. - /// - private bool resultsCollected; + /// + public IReadOnlyDictionary GetResultDictionary() + { + this.resultsCollected = true; + return this.properties; + } - /// - /// Initializes a new instance of the class. - /// - /// The exception which properties will be added to the bag. - /// Filter that should be applied to each property just before adding it to the bag. - public ExceptionPropertiesBag(Exception exception, IExceptionPropertyFilter? filter = null) + /// + public void AddProperty(string key, object? value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(key); +#else + if (key is null) { - this.exception = exception ?? throw new ArgumentNullException(nameof(exception)); - this.filter = filter; + throw new ArgumentNullException(nameof(key)); } +#endif - /// - public IReadOnlyDictionary GetResultDictionary() + if (this.resultsCollected) { - this.resultsCollected = true; - return this.properties; + throw new InvalidOperationException($"Cannot add exception property '{key}' to bag, after results were already collected"); } - /// - public void AddProperty(string key, object? value) + if (this.filter is not null) { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(key); -#else - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } -#endif - - if (this.resultsCollected) + if (this.filter.ShouldPropertyBeFiltered(this.exception, key, value)) { - throw new InvalidOperationException($"Cannot add exception property '{key}' to bag, after results were already collected"); + return; } - - if (this.filter is not null) - { - if (this.filter.ShouldPropertyBeFiltered(this.exception, key, value)) - { - return; - } - } - - this.AddPairToProperties(key, value); } - /// - public bool ContainsProperty(string key) => this.properties.ContainsKey(key); + this.AddPairToProperties(key, value); + } - private static string GetReplacementKey(string key) => key + "$"; + /// + public bool ContainsProperty(string key) => this.properties.ContainsKey(key); - /// - /// We want to be as robust as possible - /// so even in case of multiple properties with the same name - /// we want to at least try carrying on and keep working. - /// - private void AddPairToProperties(string key, object? value) - { + private static string GetReplacementKey(string key) => key + "$"; + + /// + /// We want to be as robust as possible + /// so even in case of multiple properties with the same name + /// we want to at least try carrying on and keep working. + /// + private void AddPairToProperties(string key, object? value) + { #if NET5_0_OR_GREATER - var i = 0; - while (!this.properties.TryAdd(key, value) && i < AcceptableNumberOfSameNameProperties) - { - key = GetReplacementKey(key); - i++; - } + var i = 0; + while (!this.properties.TryAdd(key, value) && i < AcceptableNumberOfSameNameProperties) + { + key = GetReplacementKey(key); + i++; + } #else - key = this.MakeSureKeyIsUnique(key); + key = this.MakeSureKeyIsUnique(key); - this.properties.Add(key, value); + this.properties.Add(key, value); #endif - } + } #if !NET5_0_OR_GREATER - private string MakeSureKeyIsUnique(string key) + private string MakeSureKeyIsUnique(string key) + { + var i = 0; + while (this.properties.ContainsKey(key) && i < AcceptableNumberOfSameNameProperties) { - var i = 0; - while (this.properties.ContainsKey(key) && i < AcceptableNumberOfSameNameProperties) - { - key = GetReplacementKey(key); - i++; - } - - return key; + key = GetReplacementKey(key); + i++; } -#endif + + return key; } +#endif } diff --git a/Source/Serilog.Exceptions/Core/IDestructuringOptions.cs b/Source/Serilog.Exceptions/Core/IDestructuringOptions.cs index fc0063ba..fbaeec93 100644 --- a/Source/Serilog.Exceptions/Core/IDestructuringOptions.cs +++ b/Source/Serilog.Exceptions/Core/IDestructuringOptions.cs @@ -1,44 +1,43 @@ -namespace Serilog.Exceptions.Core -{ - using System.Collections.Generic; - using Serilog.Exceptions.Destructurers; - using Serilog.Exceptions.Filters; +namespace Serilog.Exceptions.Core; + +using System.Collections.Generic; +using Serilog.Exceptions.Destructurers; +using Serilog.Exceptions.Filters; +/// +/// Represents all the configuration options user can specify to influence the destructuring process. +/// +public interface IDestructuringOptions +{ /// - /// Represents all the configuration options user can specify to influence the destructuring process. + /// Gets the name of the key dictionary to which destructured exception will be assigned. Default is + /// "ExceptionDetail". /// - public interface IDestructuringOptions - { - /// - /// Gets the name of the key dictionary to which destructured exception will be assigned. Default is - /// "ExceptionDetail". - /// - string RootName { get; } + string RootName { get; } - /// - /// Gets the depth at which reflection based destructurer will stop recursive process of children destructuring. - /// Default is 10. - /// - int DestructuringDepth { get; } + /// + /// Gets the depth at which reflection based destructurer will stop recursive process of children destructuring. + /// Default is 10. + /// + int DestructuringDepth { get; } - /// - /// Gets the collection of destructurers that will be used to destructure incoming exceptions. If none of the - /// destructurers can handle given type of exception, a generic, reflection-based destructurer will be used. - /// - IEnumerable Destructurers { get; } + /// + /// Gets the collection of destructurers that will be used to destructure incoming exceptions. If none of the + /// destructurers can handle given type of exception, a generic, reflection-based destructurer will be used. + /// + IEnumerable Destructurers { get; } - /// - /// Gets the optional filter that will evaluate and possibly reject each destructured property just before - /// they are about to be written to a result structure. If no filter is set no properties are going to be - /// rejected. Filter is applied to every property regardless of which destructurer was used. - /// - IExceptionPropertyFilter? Filter { get; } + /// + /// Gets the optional filter that will evaluate and possibly reject each destructured property just before + /// they are about to be written to a result structure. If no filter is set no properties are going to be + /// rejected. Filter is applied to every property regardless of which destructurer was used. + /// + IExceptionPropertyFilter? Filter { get; } - /// - /// Gets a value indicating whether to disable the 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. - /// - bool DisableReflectionBasedDestructurer { get; } - } + /// + /// Gets a value indicating whether to disable the 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. + /// + bool DisableReflectionBasedDestructurer { get; } } diff --git a/Source/Serilog.Exceptions/Core/IExceptionPropertiesBag.cs b/Source/Serilog.Exceptions/Core/IExceptionPropertiesBag.cs index 42772325..6651b710 100644 --- a/Source/Serilog.Exceptions/Core/IExceptionPropertiesBag.cs +++ b/Source/Serilog.Exceptions/Core/IExceptionPropertiesBag.cs @@ -1,32 +1,31 @@ -namespace Serilog.Exceptions.Core -{ - using System.Collections.Generic; +namespace Serilog.Exceptions.Core; + +using System.Collections.Generic; +/// +/// Container for all properties of single exception instance. All properties must be added before result +/// dictionary is requested. +/// +public interface IExceptionPropertiesBag +{ /// - /// Container for all properties of single exception instance. All properties must be added before result - /// dictionary is requested. + /// Results should be collected only once, after all the properties were added using + /// method. /// - public interface IExceptionPropertiesBag - { - /// - /// Results should be collected only once, after all the properties were added using - /// method. - /// - /// Dictionary with all the properties names and values that were added. - IReadOnlyDictionary GetResultDictionary(); + /// Dictionary with all the properties names and values that were added. + IReadOnlyDictionary GetResultDictionary(); - /// - /// Adds a property to the bag. - /// - /// The key. - /// The value. - void AddProperty(string key, object? value); + /// + /// Adds a property to the bag. + /// + /// The key. + /// The value. + void AddProperty(string key, object? value); - /// - /// Returns true if given key is already present in the bag. - /// - /// The key. - /// True if given key is already present. - bool ContainsProperty(string key); - } + /// + /// Returns true if given key is already present in the bag. + /// + /// The key. + /// True if given key is already present. + bool ContainsProperty(string key); } diff --git a/Source/Serilog.Exceptions/Destructurers/AggregateExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/AggregateExceptionDestructurer.cs index b7c0cf92..3a196189 100644 --- a/Source/Serilog.Exceptions/Destructurers/AggregateExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/AggregateExceptionDestructurer.cs @@ -1,32 +1,31 @@ -namespace Serilog.Exceptions.Destructurers +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using System.Linq; +using Serilog.Exceptions.Core; + +/// +/// Destructurer for . +/// +public class AggregateExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Linq; - using Serilog.Exceptions.Core; + /// + public override Type[] TargetTypes => new[] { typeof(AggregateException) }; - /// - /// Destructurer for . - /// - public class AggregateExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(AggregateException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var aggregateException = (AggregateException)exception; - propertiesBag.AddProperty( - nameof(AggregateException.InnerExceptions), - aggregateException.InnerExceptions.Select(destructureException).ToList()); + var aggregateException = (AggregateException)exception; + propertiesBag.AddProperty( + nameof(AggregateException.InnerExceptions), + aggregateException.InnerExceptions.Select(destructureException).ToList()); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions/Destructurers/ArgumentExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/ArgumentExceptionDestructurer.cs index 065de1a6..6aa6801d 100644 --- a/Source/Serilog.Exceptions/Destructurers/ArgumentExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/ArgumentExceptionDestructurer.cs @@ -1,33 +1,32 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; - /// - /// Destructurer for . - /// - public class ArgumentExceptionDestructurer : ExceptionDestructurer +/// +/// Destructurer for . +/// +public class ArgumentExceptionDestructurer : ExceptionDestructurer +{ + /// + public override Type[] TargetTypes => new[] { - /// - public override Type[] TargetTypes => new[] - { - typeof(ArgumentException), - typeof(ArgumentNullException), - }; + typeof(ArgumentException), + typeof(ArgumentNullException), + }; - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var argumentException = (ArgumentException)exception; - propertiesBag.AddProperty(nameof(ArgumentException.ParamName), argumentException.ParamName); + var argumentException = (ArgumentException)exception; + propertiesBag.AddProperty(nameof(ArgumentException.ParamName), argumentException.ParamName); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions/Destructurers/ArgumentOutOfRangeExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/ArgumentOutOfRangeExceptionDestructurer.cs index d5716a20..883c364a 100644 --- a/Source/Serilog.Exceptions/Destructurers/ArgumentOutOfRangeExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/ArgumentOutOfRangeExceptionDestructurer.cs @@ -1,32 +1,31 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; - /// - /// Destructurer for . - /// - public class ArgumentOutOfRangeExceptionDestructurer : ArgumentExceptionDestructurer +/// +/// Destructurer for . +/// +public class ArgumentOutOfRangeExceptionDestructurer : ArgumentExceptionDestructurer +{ + /// + public override Type[] TargetTypes => new[] { - /// - public override Type[] TargetTypes => new[] - { - typeof(ArgumentOutOfRangeException), - }; + typeof(ArgumentOutOfRangeException), + }; - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var argumentException = (ArgumentOutOfRangeException)exception; - propertiesBag.AddProperty(nameof(ArgumentOutOfRangeException.ActualValue), argumentException.ActualValue); + var argumentException = (ArgumentOutOfRangeException)exception; + propertiesBag.AddProperty(nameof(ArgumentOutOfRangeException.ActualValue), argumentException.ActualValue); #pragma warning restore CA1062 // Validate arguments of public methods - } } } diff --git a/Source/Serilog.Exceptions/Destructurers/ExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/ExceptionDestructurer.cs index c96d97fa..f9dbd737 100644 --- a/Source/Serilog.Exceptions/Destructurers/ExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/ExceptionDestructurer.cs @@ -1,26 +1,26 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; +/// +/// Base class for more specific destructurers. +/// It destructures all the standard properties that every has. +/// +public class ExceptionDestructurer : IExceptionDestructurer +{ /// - /// Base class for more specific destructurers. - /// It destructures all the standard properties that every has. + /// Gets a collection of exceptions types from standard library that do not have any custom property, + /// so they can be destructured using generic exception destructurer. /// - public class ExceptionDestructurer : IExceptionDestructurer - { - /// - /// Gets a collection of exceptions types from standard library that do not have any custom property, - /// so they can be destructured using generic exception destructurer. - /// #pragma warning disable CA1819 // Properties should not return arrays - public virtual Type[] TargetTypes + public virtual Type[] TargetTypes #pragma warning restore CA1819 // Properties should not return arrays + { + get { - get - { - var targetTypes = new List + var targetTypes = new List { #pragma warning disable IDE0001 // Simplify Names #if NET461 || NET472 @@ -201,90 +201,89 @@ public virtual Type[] TargetTypes }; #pragma warning restore IDE0001 // Simplify Names - return targetTypes.ToArray(); - } + return targetTypes.ToArray(); } + } - /// - public virtual void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { + /// + public virtual void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(exception); - ArgumentNullException.ThrowIfNull(propertiesBag); - ArgumentNullException.ThrowIfNull(destructureException); + ArgumentNullException.ThrowIfNull(exception); + ArgumentNullException.ThrowIfNull(propertiesBag); + ArgumentNullException.ThrowIfNull(destructureException); #else - if (exception is null) - { - throw new ArgumentNullException(nameof(exception)); - } + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } - if (propertiesBag is null) - { - throw new ArgumentNullException(nameof(propertiesBag)); - } + if (propertiesBag is null) + { + throw new ArgumentNullException(nameof(propertiesBag)); + } - if (destructureException is null) - { - throw new ArgumentNullException(nameof(destructureException)); - } + if (destructureException is null) + { + throw new ArgumentNullException(nameof(destructureException)); + } #endif - propertiesBag.AddProperty("Type", exception.GetType().FullName); + propertiesBag.AddProperty("Type", exception.GetType().FullName); - DestructureCommonExceptionProperties( - exception, - propertiesBag, - destructureException, - data => data.ToStringObjectDictionary()); - } + DestructureCommonExceptionProperties( + exception, + propertiesBag, + destructureException, + data => data.ToStringObjectDictionary()); + } - /// - /// Destructures public properties of . - /// Omits less frequently used ones if they are null. - /// - /// The exception that will be destructured. - /// The bag when destructured properties will be put. - /// Function that can be used to destructure inner exceptions if there are any. - /// Injected function for destructuring . - internal static void DestructureCommonExceptionProperties( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> innerDestructure, - Func destructureDataProperty) + /// + /// Destructures public properties of . + /// Omits less frequently used ones if they are null. + /// + /// The exception that will be destructured. + /// The bag when destructured properties will be put. + /// Function that can be used to destructure inner exceptions if there are any. + /// Injected function for destructuring . + internal static void DestructureCommonExceptionProperties( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> innerDestructure, + Func destructureDataProperty) + { + if (exception.Data.Count != 0) { - if (exception.Data.Count != 0) - { - propertiesBag.AddProperty(nameof(Exception.Data), destructureDataProperty(exception.Data)); - } + propertiesBag.AddProperty(nameof(Exception.Data), destructureDataProperty(exception.Data)); + } - if (!string.IsNullOrEmpty(exception.HelpLink)) - { - propertiesBag.AddProperty(nameof(Exception.HelpLink), exception.HelpLink); - } + if (!string.IsNullOrEmpty(exception.HelpLink)) + { + propertiesBag.AddProperty(nameof(Exception.HelpLink), exception.HelpLink); + } - if (exception.HResult != 0) - { - propertiesBag.AddProperty(nameof(Exception.HResult), exception.HResult); - } + if (exception.HResult != 0) + { + propertiesBag.AddProperty(nameof(Exception.HResult), exception.HResult); + } - propertiesBag.AddProperty(nameof(Exception.Message), exception.Message); - propertiesBag.AddProperty(nameof(Exception.Source), exception.Source); - propertiesBag.AddProperty(nameof(Exception.StackTrace), exception.StackTrace); + propertiesBag.AddProperty(nameof(Exception.Message), exception.Message); + propertiesBag.AddProperty(nameof(Exception.Source), exception.Source); + propertiesBag.AddProperty(nameof(Exception.StackTrace), exception.StackTrace); #if !NETSTANDARD1_3 - if (exception.TargetSite is not null) - { - propertiesBag.AddProperty(nameof(Exception.TargetSite), exception.TargetSite.ToString()); - } + if (exception.TargetSite is not null) + { + propertiesBag.AddProperty(nameof(Exception.TargetSite), exception.TargetSite.ToString()); + } #endif - if (exception.InnerException is not null) - { - propertiesBag.AddProperty(nameof(Exception.InnerException), innerDestructure(exception.InnerException)); - } + if (exception.InnerException is not null) + { + propertiesBag.AddProperty(nameof(Exception.InnerException), innerDestructure(exception.InnerException)); } } } diff --git a/Source/Serilog.Exceptions/Destructurers/IExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/IExceptionDestructurer.cs index ced4a8d0..e672a8ad 100644 --- a/Source/Serilog.Exceptions/Destructurers/IExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/IExceptionDestructurer.cs @@ -1,33 +1,32 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using Serilog.Exceptions.Core; +/// +/// Interface that all exception destructurers that want to be registered must implement. Exception destructurer +/// must explicitly declare which types it can destructure using property. +/// +public interface IExceptionDestructurer +{ /// - /// Interface that all exception destructurers that want to be registered must implement. Exception destructurer - /// must explicitly declare which types it can destructure using property. + /// Gets a collection of types of exception that the destructurer can handle. /// - public interface IExceptionDestructurer - { - /// - /// Gets a collection of types of exception that the destructurer can handle. - /// #pragma warning disable CA1819 // Properties should not return arrays - Type[] TargetTypes { get; } + Type[] TargetTypes { get; } #pragma warning restore CA1819 // Properties should not return arrays - /// - /// Destructures given . It's properties are to be put in - /// . - /// - /// The exception that will be destructured. - /// The bag when destructured properties will be put. - /// Function that can be used to destructure inner exceptions if there are - /// any. - void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException); - } + /// + /// Destructures given . It's properties are to be put in + /// . + /// + /// The exception that will be destructured. + /// The bag when destructured properties will be put. + /// Function that can be used to destructure inner exceptions if there are + /// any. + void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException); } diff --git a/Source/Serilog.Exceptions/Destructurers/OperationCanceledExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/OperationCanceledExceptionDestructurer.cs index a09dcce9..9e8a350c 100644 --- a/Source/Serilog.Exceptions/Destructurers/OperationCanceledExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/OperationCanceledExceptionDestructurer.cs @@ -1,48 +1,47 @@ -namespace Serilog.Exceptions.Destructurers +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using System.Threading; +using Serilog.Exceptions.Core; + +/// +/// Destructurer for . +/// +public class OperationCanceledExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Threading; - using Serilog.Exceptions.Core; + private const string CancellationTokenTrue = "CancellationRequested: true"; + private const string CancellationTokenFalse = "CancellationRequested: false"; - /// - /// Destructurer for . - /// - public class OperationCanceledExceptionDestructurer : ExceptionDestructurer + private static readonly Type[] TargetExceptionTypes = { - private const string CancellationTokenTrue = "CancellationRequested: true"; - private const string CancellationTokenFalse = "CancellationRequested: false"; - - private static readonly Type[] TargetExceptionTypes = - { - typeof(OperationCanceledException), - }; + typeof(OperationCanceledException), + }; - /// - public override Type[] TargetTypes => TargetExceptionTypes; + /// + public override Type[] TargetTypes => TargetExceptionTypes; - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var operationCancelledException = (OperationCanceledException)exception; - propertiesBag.AddProperty( - nameof(OperationCanceledException.CancellationToken), - DestructureCancellationToken(operationCancelledException.CancellationToken)); + var operationCancelledException = (OperationCanceledException)exception; + propertiesBag.AddProperty( + nameof(OperationCanceledException.CancellationToken), + DestructureCancellationToken(operationCancelledException.CancellationToken)); #pragma warning restore CA1062 // Validate arguments of public methods - } - - /// - /// Destructures the cancellation token. - /// - /// The cancellation token. - /// The destructured cancellation token. - internal static object DestructureCancellationToken(in CancellationToken cancellationToken) => - cancellationToken.IsCancellationRequested ? CancellationTokenTrue : CancellationTokenFalse; } + + /// + /// Destructures the cancellation token. + /// + /// The cancellation token. + /// The destructured cancellation token. + internal static object DestructureCancellationToken(in CancellationToken cancellationToken) => + cancellationToken.IsCancellationRequested ? CancellationTokenTrue : CancellationTokenFalse; } diff --git a/Source/Serilog.Exceptions/Destructurers/ReflectionBasedDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/ReflectionBasedDestructurer.cs index 7df31da2..ca19e9f0 100644 --- a/Source/Serilog.Exceptions/Destructurers/ReflectionBasedDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/ReflectionBasedDestructurer.cs @@ -1,379 +1,378 @@ -namespace Serilog.Exceptions.Destructurers +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Reflection; + +/// +/// Destructures exceptions by gathering all public non-indexer properties using reflection and then dynamically +/// retrieving their values. This class can handle every exception including those with circular references and +/// throwing properties. Additionally, a "Type" property is added to let the user know exact type of destructured +/// exception. +/// +public class ReflectionBasedDestructurer : IExceptionDestructurer { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Reflection; - using System.Threading; - using System.Threading.Tasks; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Reflection; + private const string IdLabel = "$id"; + private const string RefLabel = "$ref"; + private const string CyclicReferenceMessage = "Cyclic reference"; + private const string IQueryableDestructureSkippedMessage = "IQueryable skipped"; + private readonly int destructuringDepth; + private readonly ReflectionInfoExtractor reflectionInfoExtractor = new(); /// - /// Destructures exceptions by gathering all public non-indexer properties using reflection and then dynamically - /// retrieving their values. This class can handle every exception including those with circular references and - /// throwing properties. Additionally, a "Type" property is added to let the user know exact type of destructured - /// exception. + /// Initializes a new instance of the class. /// - public class ReflectionBasedDestructurer : IExceptionDestructurer + /// Maximum depth to which the destructurer will go when destructuring exception + /// object graph. + public ReflectionBasedDestructurer(int destructuringDepth) { - private const string IdLabel = "$id"; - private const string RefLabel = "$ref"; - private const string CyclicReferenceMessage = "Cyclic reference"; - private const string IQueryableDestructureSkippedMessage = "IQueryable skipped"; - private readonly int destructuringDepth; - private readonly ReflectionInfoExtractor reflectionInfoExtractor = new(); - - /// - /// Initializes a new instance of the class. - /// - /// Maximum depth to which the destructurer will go when destructuring exception - /// object graph. - public ReflectionBasedDestructurer(int destructuringDepth) + if (destructuringDepth <= 0) { - if (destructuringDepth <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(destructuringDepth), - destructuringDepth, - Resources.DestructuringDepthMustBeMoreThanZero); - } - - this.destructuringDepth = destructuringDepth; + throw new ArgumentOutOfRangeException( + nameof(destructuringDepth), + destructuringDepth, + Resources.DestructuringDepthMustBeMoreThanZero); } - /// + this.destructuringDepth = destructuringDepth; + } + + /// #pragma warning disable CA1819 // Properties should not return arrays - public Type[] TargetTypes => new[] { typeof(Exception) }; + public Type[] TargetTypes => new[] { typeof(Exception) }; #pragma warning restore CA1819 // Properties should not return arrays - /// - public void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { + /// + public void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(exception); - ArgumentNullException.ThrowIfNull(propertiesBag); - ArgumentNullException.ThrowIfNull(destructureException); + ArgumentNullException.ThrowIfNull(exception); + ArgumentNullException.ThrowIfNull(propertiesBag); + ArgumentNullException.ThrowIfNull(destructureException); #else - if (exception is null) - { - throw new ArgumentNullException(nameof(exception)); - } + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } - if (propertiesBag is null) - { - throw new ArgumentNullException(nameof(propertiesBag)); - } + if (propertiesBag is null) + { + throw new ArgumentNullException(nameof(propertiesBag)); + } - if (destructureException is null) - { - throw new ArgumentNullException(nameof(destructureException)); - } + if (destructureException is null) + { + throw new ArgumentNullException(nameof(destructureException)); + } #endif - var nextCyclicRefId = 1; - var destructuredObjects = new Dictionary>(); + var nextCyclicRefId = 1; + var destructuredObjects = new Dictionary>(); - ExceptionDestructurer.DestructureCommonExceptionProperties( - exception, - propertiesBag, - destructureException, - data => this.DestructureValueDictionary(data, 1, destructuredObjects, ref nextCyclicRefId)); + ExceptionDestructurer.DestructureCommonExceptionProperties( + exception, + propertiesBag, + destructureException, + data => this.DestructureValueDictionary(data, 1, destructuredObjects, ref nextCyclicRefId)); - var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(exception.GetType()); + var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(exception.GetType()); - this.AppendProperties( - exception, - reflectionInfo.PropertiesExceptBaseOnes, - propertiesBag.AddProperty, - destructuredObjects, - level: 0, - nextCyclicRefId: ref nextCyclicRefId); + this.AppendProperties( + exception, + reflectionInfo.PropertiesExceptBaseOnes, + propertiesBag.AddProperty, + destructuredObjects, + level: 0, + nextCyclicRefId: ref nextCyclicRefId); - AppendTypeIfPossible(propertiesBag, exception.GetType()); - } + AppendTypeIfPossible(propertiesBag, exception.GetType()); + } - private static string? GetOrGenerateRefId(ref int nextCyclicRefId, IDictionary destructuredObject) + private static string? GetOrGenerateRefId(ref int nextCyclicRefId, IDictionary destructuredObject) + { + string? refId; + if (destructuredObject.ContainsKey(IdLabel)) { - string? refId; - if (destructuredObject.ContainsKey(IdLabel)) - { - refId = (string?)destructuredObject[IdLabel]; - } - else - { - var id = nextCyclicRefId; - nextCyclicRefId++; - refId = id.ToString(CultureInfo.InvariantCulture); - destructuredObject[IdLabel] = refId; - } - - return refId; + refId = (string?)destructuredObject[IdLabel]; + } + else + { + var id = nextCyclicRefId; + nextCyclicRefId++; + refId = id.ToString(CultureInfo.InvariantCulture); + destructuredObject[IdLabel] = refId; } - private static object DestructureUri(Uri value) => value.ToString(); + return refId; + } + + private static object DestructureUri(Uri value) => value.ToString(); - private static void AppendTypeIfPossible(IExceptionPropertiesBag propertiesBag, Type valueType) + private static void AppendTypeIfPossible(IExceptionPropertiesBag propertiesBag, Type valueType) + { + if (propertiesBag.ContainsProperty("Type")) { - if (propertiesBag.ContainsProperty("Type")) + if (!propertiesBag.ContainsProperty("$Type")) { - if (!propertiesBag.ContainsProperty("$Type")) - { - propertiesBag.AddProperty("$Type", valueType.FullName); - } - else - { - // If both "Type" and "$Type" are present - // we just give up appending exception type - } + propertiesBag.AddProperty("$Type", valueType.FullName); } else { - propertiesBag.AddProperty("Type", valueType.FullName); + // If both "Type" and "$Type" are present + // we just give up appending exception type } } + else + { + propertiesBag.AddProperty("Type", valueType.FullName); + } + } - private void AppendProperties( - object value, - ReflectionPropertyInfo[] reflectionPropertyInfos, - Action addPropertyAction, - IDictionary> destructuredObjects, - int level, - ref int nextCyclicRefId) + private void AppendProperties( + object value, + ReflectionPropertyInfo[] reflectionPropertyInfos, + Action addPropertyAction, + IDictionary> destructuredObjects, + int level, + ref int nextCyclicRefId) + { + foreach (var property in reflectionPropertyInfos) { - foreach (var property in reflectionPropertyInfos) + try { - try - { - var valueToBeDestructured = property.Getter(value); - var localNextCyclicRefId = nextCyclicRefId; - var destructuredValue = this.DestructureValue( - valueToBeDestructured, - level + 1, - destructuredObjects, - ref localNextCyclicRefId); - nextCyclicRefId = localNextCyclicRefId; - addPropertyAction(property.Name, destructuredValue); - } - catch (TargetInvocationException targetInvocationException) + var valueToBeDestructured = property.Getter(value); + var localNextCyclicRefId = nextCyclicRefId; + var destructuredValue = this.DestructureValue( + valueToBeDestructured, + level + 1, + destructuredObjects, + ref localNextCyclicRefId); + nextCyclicRefId = localNextCyclicRefId; + addPropertyAction(property.Name, destructuredValue); + } + catch (TargetInvocationException targetInvocationException) + { + var innerException = targetInvocationException.InnerException; + if (innerException is not null) { - var innerException = targetInvocationException.InnerException; - if (innerException is not null) - { - addPropertyAction(property.Name, $"threw {innerException.GetType().FullName}: {innerException.Message}"); - } + addPropertyAction(property.Name, $"threw {innerException.GetType().FullName}: {innerException.Message}"); } + } #pragma warning disable CA1031 // Do not catch general exception types - catch (Exception exception) - { - addPropertyAction(property.Name, $"threw {exception.GetType().FullName}: {exception.Message}"); - } -#pragma warning restore CA1031 // Do not catch general exception types + catch (Exception exception) + { + addPropertyAction(property.Name, $"threw {exception.GetType().FullName}: {exception.Message}"); } +#pragma warning restore CA1031 // Do not catch general exception types } + } - private object? DestructureValue( - object? value, - int level, - IDictionary> destructuredObjects, - ref int nextCyclicRefId) + private object? DestructureValue( + object? value, + int level, + IDictionary> destructuredObjects, + ref int nextCyclicRefId) + { + if (value is null) { - if (value is null) - { - return null; - } - - var valueType = value.GetType(); - var valueTypeInfo = valueType.GetTypeInfo(); + return null; + } - if (value is MemberInfo) - { - return value; - } + var valueType = value.GetType(); + var valueTypeInfo = valueType.GetTypeInfo(); - if (valueType.GetTypeCode() != TypeCode.Object || valueTypeInfo.IsValueType) - { - return value; - } + if (value is MemberInfo) + { + return value; + } - if (level > this.destructuringDepth) - { - return value; - } + if (valueType.GetTypeCode() != TypeCode.Object || valueTypeInfo.IsValueType) + { + return value; + } - if (value is IDictionary dictionary) - { - return this.DestructureValueDictionary(dictionary, level, destructuredObjects, ref nextCyclicRefId); - } + if (level > this.destructuringDepth) + { + return value; + } - if (value is IQueryable queryable) - { - return IQueryableDestructureSkippedMessage; - } - else if (value is IEnumerable enumerable) - { - return this.DestructureValueEnumerable(enumerable, level, destructuredObjects, ref nextCyclicRefId); - } + if (value is IDictionary dictionary) + { + return this.DestructureValueDictionary(dictionary, level, destructuredObjects, ref nextCyclicRefId); + } - if (value is Uri uri) - { - return DestructureUri(uri); - } + if (value is IQueryable queryable) + { + return IQueryableDestructureSkippedMessage; + } + else if (value is IEnumerable enumerable) + { + return this.DestructureValueEnumerable(enumerable, level, destructuredObjects, ref nextCyclicRefId); + } - if (value is CancellationToken ct) - { - return OperationCanceledExceptionDestructurer.DestructureCancellationToken(ct); - } + if (value is Uri uri) + { + return DestructureUri(uri); + } - if (value is Task task) - { - return this.DestructureTask(task, level, destructuredObjects, ref nextCyclicRefId); - } + if (value is CancellationToken ct) + { + return OperationCanceledExceptionDestructurer.DestructureCancellationToken(ct); + } - return this.DestructureObject(value, valueType, level, destructuredObjects, ref nextCyclicRefId); + if (value is Task task) + { + return this.DestructureTask(task, level, destructuredObjects, ref nextCyclicRefId); } - private object DestructureValueEnumerable( - IEnumerable value, - int level, - IDictionary> destructuredObjects, - ref int nextCyclicRefId) + return this.DestructureObject(value, valueType, level, destructuredObjects, ref nextCyclicRefId); + } + + private object DestructureValueEnumerable( + IEnumerable value, + int level, + IDictionary> destructuredObjects, + ref int nextCyclicRefId) + { + if (destructuredObjects.ContainsKey(value)) { - if (destructuredObjects.ContainsKey(value)) - { - return new Dictionary + return new Dictionary { { RefLabel, CyclicReferenceMessage }, }; - } - - destructuredObjects.Add(value, new Dictionary()); + } - var resultList = new List(); - foreach (var o in value.Cast()) - { - resultList.Add(this.DestructureValue(o, level + 1, destructuredObjects, ref nextCyclicRefId)); - } + destructuredObjects.Add(value, new Dictionary()); - return resultList; + var resultList = new List(); + foreach (var o in value.Cast()) + { + resultList.Add(this.DestructureValue(o, level + 1, destructuredObjects, ref nextCyclicRefId)); } - private object DestructureValueDictionary( - IDictionary value, - int level, - IDictionary> destructuredObjects, - ref int nextCyclicRefId) + return resultList; + } + + private object DestructureValueDictionary( + IDictionary value, + int level, + IDictionary> destructuredObjects, + ref int nextCyclicRefId) + { + if (destructuredObjects.ContainsKey(value)) { - if (destructuredObjects.ContainsKey(value)) - { - var destructuredObject = destructuredObjects[value]; - var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredObject); + var destructuredObject = destructuredObjects[value]; + var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredObject); - return new Dictionary + return new Dictionary { { RefLabel, refId }, }; - } - - var destructuredDictionary = value.ToStringObjectDictionary(); - destructuredObjects.Add(value, destructuredDictionary); + } - foreach (var kvp in destructuredDictionary.ToDictionary(k => k.Key, v => v.Value)) - { - destructuredDictionary[kvp.Key] = - this.DestructureValue(kvp.Value, level + 1, destructuredObjects, ref nextCyclicRefId); - } + var destructuredDictionary = value.ToStringObjectDictionary(); + destructuredObjects.Add(value, destructuredDictionary); - return destructuredDictionary; + foreach (var kvp in destructuredDictionary.ToDictionary(k => k.Key, v => v.Value)) + { + destructuredDictionary[kvp.Key] = + this.DestructureValue(kvp.Value, level + 1, destructuredObjects, ref nextCyclicRefId); } - private IDictionary DestructureObject( - object value, - Type valueType, - int level, - IDictionary> destructuredObjects, - ref int nextCyclicRefId) + return destructuredDictionary; + } + + private IDictionary DestructureObject( + object value, + Type valueType, + int level, + IDictionary> destructuredObjects, + ref int nextCyclicRefId) + { + if (destructuredObjects.ContainsKey(value)) { - if (destructuredObjects.ContainsKey(value)) - { - var destructuredObject = destructuredObjects[value]; - var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredObject); + var destructuredObject = destructuredObjects[value]; + var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredObject); - return new Dictionary() + return new Dictionary() { { RefLabel, refId }, }; - } + } - var values = new Dictionary(); - destructuredObjects.Add(value, values); + var values = new Dictionary(); + destructuredObjects.Add(value, values); - var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(valueType); + var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(valueType); - foreach (var property in reflectionInfo.Properties) + foreach (var property in reflectionInfo.Properties) + { + try { - try - { - var valueToBeDestructured = property.Getter(value); - var destructuredValue = this.DestructureValue( - valueToBeDestructured, - level + 1, - destructuredObjects, - ref nextCyclicRefId); - values.Add(property.Name, destructuredValue); - } - catch (TargetInvocationException targetInvocationException) + var valueToBeDestructured = property.Getter(value); + var destructuredValue = this.DestructureValue( + valueToBeDestructured, + level + 1, + destructuredObjects, + ref nextCyclicRefId); + values.Add(property.Name, destructuredValue); + } + catch (TargetInvocationException targetInvocationException) + { + var innerException = targetInvocationException.InnerException; + if (innerException is not null) { - var innerException = targetInvocationException.InnerException; - if (innerException is not null) - { - values.Add(property.Name, $"threw {innerException.GetType().FullName}: {innerException.Message}"); - } + values.Add(property.Name, $"threw {innerException.GetType().FullName}: {innerException.Message}"); } + } #pragma warning disable CA1031 // Do not catch general exception types - catch (Exception exception) - { - values.Add(property.Name, $"threw {exception.GetType().FullName}: {exception.Message}"); - } -#pragma warning restore CA1031 // Do not catch general exception types + catch (Exception exception) + { + values.Add(property.Name, $"threw {exception.GetType().FullName}: {exception.Message}"); } - - return values; +#pragma warning restore CA1031 // Do not catch general exception types } - private object DestructureTask( - Task task, - int level, - IDictionary> destructuredObjects, - ref int nextCyclicRefId) + return values; + } + + private object DestructureTask( + Task task, + int level, + IDictionary> destructuredObjects, + ref int nextCyclicRefId) + { + if (destructuredObjects.TryGetValue(task, out var destructuredTask)) { - if (destructuredObjects.TryGetValue(task, out var destructuredTask)) - { - var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredTask); + var refId = GetOrGenerateRefId(ref nextCyclicRefId, destructuredTask); - return new SortedList() + return new SortedList() { { RefLabel, refId }, }; - } - - var values = new SortedList(); - destructuredObjects.Add(task, values); + } - values[nameof(Task.Id)] = task.Id; - values[nameof(Task.Status)] = task.Status.ToString("G"); - values[nameof(Task.CreationOptions)] = task.CreationOptions.ToString("F"); - if (task.IsFaulted) - { - values[nameof(Task.Exception)] = this.DestructureValue(task.Exception, level, destructuredObjects, ref nextCyclicRefId); - } + var values = new SortedList(); + destructuredObjects.Add(task, values); - return values; + values[nameof(Task.Id)] = task.Id; + values[nameof(Task.Status)] = task.Status.ToString("G"); + values[nameof(Task.CreationOptions)] = task.CreationOptions.ToString("F"); + if (task.IsFaulted) + { + values[nameof(Task.Exception)] = this.DestructureValue(task.Exception, level, destructuredObjects, ref nextCyclicRefId); } + + return values; } } diff --git a/Source/Serilog.Exceptions/Destructurers/ReflectionTypeLoadExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/ReflectionTypeLoadExceptionDestructurer.cs index d99d4529..dfe3ed20 100644 --- a/Source/Serilog.Exceptions/Destructurers/ReflectionTypeLoadExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/ReflectionTypeLoadExceptionDestructurer.cs @@ -1,55 +1,54 @@ -namespace Serilog.Exceptions.Destructurers +namespace Serilog.Exceptions.Destructurers; + +using System; +using System.Collections.Generic; +using System.Reflection; +using Serilog.Exceptions.Core; + +/// +/// Destructurer for . +/// +public class ReflectionTypeLoadExceptionDestructurer : ExceptionDestructurer { - using System; - using System.Collections.Generic; - using System.Reflection; - using Serilog.Exceptions.Core; + /// + public override Type[] TargetTypes => new[] { typeof(ReflectionTypeLoadException) }; - /// - /// Destructurer for . - /// - public class ReflectionTypeLoadExceptionDestructurer : ExceptionDestructurer + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) { - /// - public override Type[] TargetTypes => new[] { typeof(ReflectionTypeLoadException) }; - - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var reflectionTypeLoadException = (ReflectionTypeLoadException)exception; - if (reflectionTypeLoadException.LoaderExceptions is not null) - { - propertiesBag.AddProperty( - nameof(ReflectionTypeLoadException.LoaderExceptions), - GetLoaderExceptionsValue(reflectionTypeLoadException.LoaderExceptions, destructureException)); - } -#pragma warning restore CA1062 // Validate arguments of public methods + var reflectionTypeLoadException = (ReflectionTypeLoadException)exception; + if (reflectionTypeLoadException.LoaderExceptions is not null) + { + propertiesBag.AddProperty( + nameof(ReflectionTypeLoadException.LoaderExceptions), + GetLoaderExceptionsValue(reflectionTypeLoadException.LoaderExceptions, destructureException)); } +#pragma warning restore CA1062 // Validate arguments of public methods + } - private static List> GetLoaderExceptionsValue( - Exception?[] exceptions, - Func?> destructureException) + private static List> GetLoaderExceptionsValue( + Exception?[] exceptions, + Func?> destructureException) + { + var loaderExceptionValues = new List>(); + foreach (var exception in exceptions) { - var loaderExceptionValues = new List>(); - foreach (var exception in exceptions) + if (exception is not null) { - if (exception is not null) + var dictionary = destructureException(exception); + if (dictionary is not null) { - var dictionary = destructureException(exception); - if (dictionary is not null) - { - loaderExceptionValues.Add(dictionary); - } + loaderExceptionValues.Add(dictionary); } } - - return loaderExceptionValues; } + + return loaderExceptionValues; } } diff --git a/Source/Serilog.Exceptions/Destructurers/SocketExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/SocketExceptionDestructurer.cs index 7ac3d4a6..6a3da8f2 100644 --- a/Source/Serilog.Exceptions/Destructurers/SocketExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/SocketExceptionDestructurer.cs @@ -1,90 +1,89 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using System.Net.Sockets; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; - /// - /// Destructurer for . - /// - public class SocketExceptionDestructurer : ExceptionDestructurer - { - // obtained from https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socketerror - private static readonly IReadOnlyDictionary SocketErrorDocumentationBySocketError = - new Dictionary - { - { SocketError.AccessDenied, "An attempt was made to access a Socket in a way that is forbidden by its access permissions." }, - { SocketError.AddressAlreadyInUse, "Only one use of an address is normally permitted." }, - { SocketError.AddressFamilyNotSupported, "The address family specified is not supported. This error is returned if the IPv6 address family was specified and the IPv6 stack is not installed on the local machine. This error is returned if the IPv4 address family was specified and the IPv4 stack is not installed on the local machine." }, - { SocketError.AddressNotAvailable, "The selected IP address is not valid in this context." }, - { SocketError.AlreadyInProgress, "The nonblocking Socket already has an operation in progress." }, - { SocketError.ConnectionAborted, "The connection was aborted by the .NET Framework or the underlying socket provider." }, - { SocketError.ConnectionRefused, "The remote host is actively refusing a connection." }, - { SocketError.ConnectionReset, "The connection was reset by the remote peer." }, - { SocketError.DestinationAddressRequired, "A required address was omitted from an operation on a Socket." }, - { SocketError.Disconnecting, "A graceful shutdown is in progress." }, - { SocketError.Fault, "An invalid pointer address was detected by the underlying socket provider." }, - { SocketError.HostDown, "The operation failed because the remote host is down." }, - { SocketError.HostNotFound, "No such host is known. The name is not an official host name or alias." }, - { SocketError.HostUnreachable, "There is no network route to the specified host." }, - { SocketError.InProgress, "A blocking operation is in progress." }, - { SocketError.Interrupted, "A blocking Socket call was canceled." }, - { SocketError.InvalidArgument, "An invalid argument was supplied to a Socket member." }, - { SocketError.IOPending, "The application has initiated an overlapped operation that cannot be completed immediately." }, - { SocketError.IsConnected, "The Socket is already connected." }, - { SocketError.MessageSize, "The datagram is too long." }, - { SocketError.NetworkDown, "The network is not available." }, - { SocketError.NetworkReset, "The application tried to set KeepAlive on a connection that has already timed out." }, - { SocketError.NetworkUnreachable, "No route to the remote host exists." }, - { SocketError.NoBufferSpaceAvailable, "No free buffer space is available for a Socket operation." }, - { SocketError.NoData, "The requested name or IP address was not found on the name server." }, - { SocketError.NoRecovery, "The error is unrecoverable or the requested database cannot be located." }, - { SocketError.NotConnected, "The application tried to send or receive data, and the Socket is not connected." }, - { SocketError.NotInitialized, "The underlying socket provider has not been initialized." }, - { SocketError.NotSocket, "A Socket operation was attempted on a non-socket." }, - { SocketError.OperationAborted, "The overlapped operation was aborted due to the closure of the Socket." }, - { SocketError.OperationNotSupported, "The address family is not supported by the protocol family." }, - { SocketError.ProcessLimit, "Too many processes are using the underlying socket provider." }, - { SocketError.ProtocolFamilyNotSupported, "The protocol family is not implemented or has not been configured." }, - { SocketError.ProtocolNotSupported, "The protocol is not implemented or has not been configured." }, - { SocketError.ProtocolOption, "An unknown, invalid, or unsupported option or level was used with a Socket." }, - { SocketError.ProtocolType, "The protocol type is incorrect for this Socket." }, - { SocketError.Shutdown, "A request to send or receive data was disallowed because the Socket has already been closed." }, - { SocketError.SocketError, "An unspecified Socket error has occurred." }, - { SocketError.SocketNotSupported, "The support for the specified socket type does not exist in this address family." }, - { SocketError.Success, "The Socket operation succeeded." }, - { SocketError.SystemNotReady, "The network subsystem is unavailable." }, - { SocketError.TimedOut, "The connection attempt timed out, or the connected host has failed to respond." }, - { SocketError.TooManyOpenSockets, "There are too many open sockets in the underlying socket provider." }, - { SocketError.TryAgain, "The name of the host could not be resolved. Try again later." }, - { SocketError.TypeNotFound, "The specified class was not found." }, - { SocketError.VersionNotSupported, "The version of the underlying socket provider is out of range." }, - { SocketError.WouldBlock, "An operation on a nonblocking socket cannot be completed immediately." }, - }; +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using Serilog.Exceptions.Core; - /// - public override Type[] TargetTypes => new[] +/// +/// Destructurer for . +/// +public class SocketExceptionDestructurer : ExceptionDestructurer +{ + // obtained from https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socketerror + private static readonly IReadOnlyDictionary SocketErrorDocumentationBySocketError = + new Dictionary { - typeof(SocketException), + { SocketError.AccessDenied, "An attempt was made to access a Socket in a way that is forbidden by its access permissions." }, + { SocketError.AddressAlreadyInUse, "Only one use of an address is normally permitted." }, + { SocketError.AddressFamilyNotSupported, "The address family specified is not supported. This error is returned if the IPv6 address family was specified and the IPv6 stack is not installed on the local machine. This error is returned if the IPv4 address family was specified and the IPv4 stack is not installed on the local machine." }, + { SocketError.AddressNotAvailable, "The selected IP address is not valid in this context." }, + { SocketError.AlreadyInProgress, "The nonblocking Socket already has an operation in progress." }, + { SocketError.ConnectionAborted, "The connection was aborted by the .NET Framework or the underlying socket provider." }, + { SocketError.ConnectionRefused, "The remote host is actively refusing a connection." }, + { SocketError.ConnectionReset, "The connection was reset by the remote peer." }, + { SocketError.DestinationAddressRequired, "A required address was omitted from an operation on a Socket." }, + { SocketError.Disconnecting, "A graceful shutdown is in progress." }, + { SocketError.Fault, "An invalid pointer address was detected by the underlying socket provider." }, + { SocketError.HostDown, "The operation failed because the remote host is down." }, + { SocketError.HostNotFound, "No such host is known. The name is not an official host name or alias." }, + { SocketError.HostUnreachable, "There is no network route to the specified host." }, + { SocketError.InProgress, "A blocking operation is in progress." }, + { SocketError.Interrupted, "A blocking Socket call was canceled." }, + { SocketError.InvalidArgument, "An invalid argument was supplied to a Socket member." }, + { SocketError.IOPending, "The application has initiated an overlapped operation that cannot be completed immediately." }, + { SocketError.IsConnected, "The Socket is already connected." }, + { SocketError.MessageSize, "The datagram is too long." }, + { SocketError.NetworkDown, "The network is not available." }, + { SocketError.NetworkReset, "The application tried to set KeepAlive on a connection that has already timed out." }, + { SocketError.NetworkUnreachable, "No route to the remote host exists." }, + { SocketError.NoBufferSpaceAvailable, "No free buffer space is available for a Socket operation." }, + { SocketError.NoData, "The requested name or IP address was not found on the name server." }, + { SocketError.NoRecovery, "The error is unrecoverable or the requested database cannot be located." }, + { SocketError.NotConnected, "The application tried to send or receive data, and the Socket is not connected." }, + { SocketError.NotInitialized, "The underlying socket provider has not been initialized." }, + { SocketError.NotSocket, "A Socket operation was attempted on a non-socket." }, + { SocketError.OperationAborted, "The overlapped operation was aborted due to the closure of the Socket." }, + { SocketError.OperationNotSupported, "The address family is not supported by the protocol family." }, + { SocketError.ProcessLimit, "Too many processes are using the underlying socket provider." }, + { SocketError.ProtocolFamilyNotSupported, "The protocol family is not implemented or has not been configured." }, + { SocketError.ProtocolNotSupported, "The protocol is not implemented or has not been configured." }, + { SocketError.ProtocolOption, "An unknown, invalid, or unsupported option or level was used with a Socket." }, + { SocketError.ProtocolType, "The protocol type is incorrect for this Socket." }, + { SocketError.Shutdown, "A request to send or receive data was disallowed because the Socket has already been closed." }, + { SocketError.SocketError, "An unspecified Socket error has occurred." }, + { SocketError.SocketNotSupported, "The support for the specified socket type does not exist in this address family." }, + { SocketError.Success, "The Socket operation succeeded." }, + { SocketError.SystemNotReady, "The network subsystem is unavailable." }, + { SocketError.TimedOut, "The connection attempt timed out, or the connected host has failed to respond." }, + { SocketError.TooManyOpenSockets, "There are too many open sockets in the underlying socket provider." }, + { SocketError.TryAgain, "The name of the host could not be resolved. Try again later." }, + { SocketError.TypeNotFound, "The specified class was not found." }, + { SocketError.VersionNotSupported, "The version of the underlying socket provider is out of range." }, + { SocketError.WouldBlock, "An operation on a nonblocking socket cannot be completed immediately." }, }; - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { - base.Destructure(exception, propertiesBag, destructureException); + /// + public override Type[] TargetTypes => new[] + { + typeof(SocketException), + }; + + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { + base.Destructure(exception, propertiesBag, destructureException); #pragma warning disable CA1062 // Validate arguments of public methods - var socketException = (SocketException)exception; - propertiesBag.AddProperty(nameof(SocketException.SocketErrorCode), socketException.SocketErrorCode); - if (SocketErrorDocumentationBySocketError.TryGetValue(socketException.SocketErrorCode, out var documentation)) - { - propertiesBag.AddProperty(nameof(SocketException.SocketErrorCode) + "Message", documentation); - } -#pragma warning restore CA1062 // Validate arguments of public methods + var socketException = (SocketException)exception; + propertiesBag.AddProperty(nameof(SocketException.SocketErrorCode), socketException.SocketErrorCode); + if (SocketErrorDocumentationBySocketError.TryGetValue(socketException.SocketErrorCode, out var documentation)) + { + propertiesBag.AddProperty(nameof(SocketException.SocketErrorCode) + "Message", documentation); } +#pragma warning restore CA1062 // Validate arguments of public methods } } diff --git a/Source/Serilog.Exceptions/Destructurers/TaskCanceledExceptionDestructurer.cs b/Source/Serilog.Exceptions/Destructurers/TaskCanceledExceptionDestructurer.cs index 921dded0..f17dfb9b 100644 --- a/Source/Serilog.Exceptions/Destructurers/TaskCanceledExceptionDestructurer.cs +++ b/Source/Serilog.Exceptions/Destructurers/TaskCanceledExceptionDestructurer.cs @@ -1,74 +1,73 @@ -namespace Serilog.Exceptions.Destructurers -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions.Destructurers; - /// - /// Destructurer for . - /// - public class TaskCanceledExceptionDestructurer : OperationCanceledExceptionDestructurer +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Serilog.Exceptions.Core; + +/// +/// Destructurer for . +/// +public class TaskCanceledExceptionDestructurer : OperationCanceledExceptionDestructurer +{ + private static readonly Type[] TargetExceptionTypes = { - private static readonly Type[] TargetExceptionTypes = - { - typeof(TaskCanceledException), - }; + typeof(TaskCanceledException), + }; - /// - public override Type[] TargetTypes => TargetExceptionTypes; + /// + public override Type[] TargetTypes => TargetExceptionTypes; - /// - public override void Destructure( - Exception exception, - IExceptionPropertiesBag propertiesBag, - Func?> destructureException) - { + /// + public override void Destructure( + Exception exception, + IExceptionPropertiesBag propertiesBag, + Func?> destructureException) + { #pragma warning disable CA1062 // Validate arguments of public methods - base.Destructure(exception, propertiesBag, destructureException); + base.Destructure(exception, propertiesBag, destructureException); - var taskCancelledException = (TaskCanceledException)exception; - propertiesBag.AddProperty( - nameof(TaskCanceledException.Task), - DestructureTask(taskCancelledException.Task, destructureException)); + var taskCancelledException = (TaskCanceledException)exception; + propertiesBag.AddProperty( + nameof(TaskCanceledException.Task), + DestructureTask(taskCancelledException.Task, destructureException)); #pragma warning restore CA1062 // Validate arguments of public methods - } + } - /// - /// Destructures the specified task. - /// - /// The task. - /// The inner destructure. - /// The destructured task. - internal static object DestructureTask( - Task? task, - Func?> innerDestructure) + /// + /// Destructures the specified task. + /// + /// The task. + /// The inner destructure. + /// The destructured task. + internal static object DestructureTask( + Task? task, + Func?> innerDestructure) + { + if (task is null) { - if (task is null) - { - return "null"; - } + return "null"; + } - var taskStatus = task.Status.ToString("G"); - var taskCreationOptions = task.CreationOptions.ToString("F"); - - if (task.IsFaulted && task.Exception is not null) - { - return new - { - task.Id, - Status = taskStatus, - CreationOptions = taskCreationOptions, - Exception = innerDestructure(task.Exception), - }; - } + var taskStatus = task.Status.ToString("G"); + var taskCreationOptions = task.CreationOptions.ToString("F"); + if (task.IsFaulted && task.Exception is not null) + { return new { task.Id, Status = taskStatus, CreationOptions = taskCreationOptions, + Exception = innerDestructure(task.Exception), }; } + + return new + { + task.Id, + Status = taskStatus, + CreationOptions = taskCreationOptions, + }; } } diff --git a/Source/Serilog.Exceptions/DictionaryExtensions.cs b/Source/Serilog.Exceptions/DictionaryExtensions.cs index 408e7b46..4fe59215 100644 --- a/Source/Serilog.Exceptions/DictionaryExtensions.cs +++ b/Source/Serilog.Exceptions/DictionaryExtensions.cs @@ -1,37 +1,36 @@ -namespace Serilog.Exceptions -{ - using System.Collections; - using System.Collections.Generic; +namespace Serilog.Exceptions; + +using System.Collections; +using System.Collections.Generic; +/// +/// Helper extension methods for specific dictionary operations. +/// +internal static class DictionaryExtensions +{ /// - /// Helper extension methods for specific dictionary operations. + /// Converts a dictionary to another one with string-ified keys. /// - internal static class DictionaryExtensions + /// The input dictionary. + /// A dictionary with string-ified keys. + public static Dictionary ToStringObjectDictionary(this IDictionary dictionary) { - /// - /// Converts a dictionary to another one with string-ified keys. - /// - /// The input dictionary. - /// A dictionary with string-ified keys. - public static Dictionary ToStringObjectDictionary(this IDictionary dictionary) - { - var result = new Dictionary(dictionary.Count); + var result = new Dictionary(dictionary.Count); - foreach (var key in dictionary.Keys) + foreach (var key in dictionary.Keys) + { + if (key is not null) { - if (key is not null) - { - var keyString = key.ToString(); - var value = dictionary[key]; + var keyString = key.ToString(); + var value = dictionary[key]; - if (keyString is not null) - { - result.Add(keyString, value); - } + if (keyString is not null) + { + result.Add(keyString, value); } } - - return result; } + + return result; } } diff --git a/Source/Serilog.Exceptions/Filters/CompositeExceptionPropertyFilter.cs b/Source/Serilog.Exceptions/Filters/CompositeExceptionPropertyFilter.cs index 32708663..2ee571bf 100644 --- a/Source/Serilog.Exceptions/Filters/CompositeExceptionPropertyFilter.cs +++ b/Source/Serilog.Exceptions/Filters/CompositeExceptionPropertyFilter.cs @@ -1,63 +1,62 @@ -namespace Serilog.Exceptions.Filters +namespace Serilog.Exceptions.Filters; + +using System; + +/// +/// Abstraction over collection of filters that filters property is any of given filters alone would filter it. +/// This is equivalent to OR over a set of booleans. Executes filters in the order they were passed to a +/// constructor. +/// +public class CompositeExceptionPropertyFilter : IExceptionPropertyFilter { - using System; + private readonly IExceptionPropertyFilter[] filters; /// - /// Abstraction over collection of filters that filters property is any of given filters alone would filter it. - /// This is equivalent to OR over a set of booleans. Executes filters in the order they were passed to a - /// constructor. + /// Initializes a new instance of the class. /// - public class CompositeExceptionPropertyFilter : IExceptionPropertyFilter + /// The filters. + /// filters was null. + /// filters was empty or filter at index {i} is null. + public CompositeExceptionPropertyFilter(params IExceptionPropertyFilter[] filters) { - private readonly IExceptionPropertyFilter[] filters; - - /// - /// Initializes a new instance of the class. - /// - /// The filters. - /// filters was null. - /// filters was empty or filter at index {i} is null. - public CompositeExceptionPropertyFilter(params IExceptionPropertyFilter[] filters) - { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(filters); + ArgumentNullException.ThrowIfNull(filters); #else - if (filters is null) - { - throw new ArgumentNullException(nameof(filters)); - } + if (filters is null) + { + throw new ArgumentNullException(nameof(filters)); + } #endif - if (filters.Length == 0) - { - throw new ArgumentException(Resources.CannotBeEmpty, nameof(filters)); - } + if (filters.Length == 0) + { + throw new ArgumentException(Resources.CannotBeEmpty, nameof(filters)); + } - for (var i = 0; i < filters.Length; ++i) + for (var i = 0; i < filters.Length; ++i) + { + if (filters[i] is null) { - if (filters[i] is null) - { - throw new ArgumentException( - $"Cannot create composite exception properties filter, because filter at index {i} is null", - nameof(filters)); - } + throw new ArgumentException( + $"Cannot create composite exception properties filter, because filter at index {i} is null", + nameof(filters)); } - - this.filters = filters; } - /// - public bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value) + this.filters = filters; + } + + /// + public bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value) + { + for (var i = 0; i < this.filters.Length; ++i) { - for (var i = 0; i < this.filters.Length; ++i) + if (this.filters[i].ShouldPropertyBeFiltered(exception, propertyName, value)) { - if (this.filters[i].ShouldPropertyBeFiltered(exception, propertyName, value)) - { - return true; - } + return true; } - - return false; } + + return false; } } diff --git a/Source/Serilog.Exceptions/Filters/IExceptionPropertyFilter.cs b/Source/Serilog.Exceptions/Filters/IExceptionPropertyFilter.cs index a43e92c7..1d57af3b 100644 --- a/Source/Serilog.Exceptions/Filters/IExceptionPropertyFilter.cs +++ b/Source/Serilog.Exceptions/Filters/IExceptionPropertyFilter.cs @@ -1,20 +1,19 @@ -namespace Serilog.Exceptions.Filters -{ - using System; +namespace Serilog.Exceptions.Filters; + +using System; +/// +/// Interface used for filtering exception properties. Filtering process is global, each property of every +/// exception will go through a configured exception property filter. +/// +public interface IExceptionPropertyFilter +{ /// - /// Interface used for filtering exception properties. Filtering process is global, each property of every - /// exception will go through a configured exception property filter. + /// Called after the property was discovered and destructured but just before it is added to results. /// - public interface IExceptionPropertyFilter - { - /// - /// Called after the property was discovered and destructured but just before it is added to results. - /// - /// Exception for which properties are filtered. - /// Name of the property. - /// Destructured value of the property. - /// Boolean flag indicating whether property will be rejected. - bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value); - } + /// Exception for which properties are filtered. + /// Name of the property. + /// Destructured value of the property. + /// Boolean flag indicating whether property will be rejected. + bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value); } diff --git a/Source/Serilog.Exceptions/Filters/IgnorePropertyByNameExceptionFilter.cs b/Source/Serilog.Exceptions/Filters/IgnorePropertyByNameExceptionFilter.cs index 5fc1a21f..f1639804 100644 --- a/Source/Serilog.Exceptions/Filters/IgnorePropertyByNameExceptionFilter.cs +++ b/Source/Serilog.Exceptions/Filters/IgnorePropertyByNameExceptionFilter.cs @@ -1,44 +1,43 @@ -namespace Serilog.Exceptions.Filters -{ - using System; +namespace Serilog.Exceptions.Filters; + +using System; +/// +/// Filters the exception properties based only on their name. If exception property matches any of provided +/// property names, exception property is ignored altogether. Comparison method is exact case-sensitive. +/// +public class IgnorePropertyByNameExceptionFilter : IExceptionPropertyFilter +{ /// - /// Filters the exception properties based only on their name. If exception property matches any of provided - /// property names, exception property is ignored altogether. Comparison method is exact case-sensitive. + /// The usage of array instead of HashSet is dictated by the assumption + /// that there will be only small number of properties to ignore and for such + /// case array is much faster than HashSet. /// - public class IgnorePropertyByNameExceptionFilter : IExceptionPropertyFilter - { - /// - /// The usage of array instead of HashSet is dictated by the assumption - /// that there will be only small number of properties to ignore and for such - /// case array is much faster than HashSet. - /// - private readonly string[] propertiesToIgnore; + private readonly string[] propertiesToIgnore; - /// - /// Initializes a new instance of the class. - /// - /// The properties to ignore. - public IgnorePropertyByNameExceptionFilter(params string[] propertiesToIgnore) => - this.propertiesToIgnore = propertiesToIgnore; + /// + /// Initializes a new instance of the class. + /// + /// The properties to ignore. + public IgnorePropertyByNameExceptionFilter(params string[] propertiesToIgnore) => + this.propertiesToIgnore = propertiesToIgnore; - /// - public bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value) + /// + public bool ShouldPropertyBeFiltered(Exception exception, string propertyName, object? value) + { + if (this.propertiesToIgnore is null) { - if (this.propertiesToIgnore is null) - { - return false; - } + return false; + } - for (var i = 0; i < this.propertiesToIgnore.Length; i++) + for (var i = 0; i < this.propertiesToIgnore.Length; i++) + { + if (this.propertiesToIgnore[i].Equals(propertyName, StringComparison.Ordinal)) { - if (this.propertiesToIgnore[i].Equals(propertyName, StringComparison.Ordinal)) - { - return true; - } + return true; } - - return false; } + + return false; } } diff --git a/Source/Serilog.Exceptions/LoggerEnrichmentConfigurationExtensions.cs b/Source/Serilog.Exceptions/LoggerEnrichmentConfigurationExtensions.cs index 316663c9..cf6d7381 100644 --- a/Source/Serilog.Exceptions/LoggerEnrichmentConfigurationExtensions.cs +++ b/Source/Serilog.Exceptions/LoggerEnrichmentConfigurationExtensions.cs @@ -1,64 +1,63 @@ -namespace Serilog.Exceptions -{ - using System; - using Serilog.Configuration; - using Serilog.Core; - using Serilog.Exceptions.Core; +namespace Serilog.Exceptions; + +using System; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Exceptions.Core; +/// +/// Serilog logger enrichment extension methods. +/// +public static class LoggerEnrichmentConfigurationExtensions +{ /// - /// Serilog logger enrichment extension methods. + /// Enrich logger output with a destructured object containing exception's public properties. Default + /// destructurers are registered. and Exception.TargetSite + /// are omitted by the destructuring process because Serilog already attaches them to log event. /// - public static class LoggerEnrichmentConfigurationExtensions + /// The enrichment configuration. + /// Configuration object allowing method chaining. + public static LoggerConfiguration WithExceptionDetails( + this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) { - /// - /// Enrich logger output with a destructured object containing exception's public properties. Default - /// destructurers are registered. and Exception.TargetSite - /// are omitted by the destructuring process because Serilog already attaches them to log event. - /// - /// The enrichment configuration. - /// Configuration object allowing method chaining. - public static LoggerConfiguration WithExceptionDetails( - this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) - { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(loggerEnrichmentConfiguration); + ArgumentNullException.ThrowIfNull(loggerEnrichmentConfiguration); #else - if (loggerEnrichmentConfiguration is null) - { - throw new ArgumentNullException(nameof(loggerEnrichmentConfiguration)); - } + if (loggerEnrichmentConfiguration is null) + { + throw new ArgumentNullException(nameof(loggerEnrichmentConfiguration)); + } #endif - var options = new DestructuringOptionsBuilder() - .WithDefaultDestructurers() - .WithIgnoreStackTraceAndTargetSiteExceptionFilter(); - var logEventEnricher = new ExceptionEnricher(options); - return loggerEnrichmentConfiguration.With(logEventEnricher); - } + var options = new DestructuringOptionsBuilder() + .WithDefaultDestructurers() + .WithIgnoreStackTraceAndTargetSiteExceptionFilter(); + var logEventEnricher = new ExceptionEnricher(options); + return loggerEnrichmentConfiguration.With(logEventEnricher); + } - /// - /// Enrich logger output with a destuctured object containing exception's public properties. - /// - /// The enrichment configuration. - /// - /// Options that will influence the process of destructuring exception's properties into result object. - /// - /// Configuration object allowing method chaining. - public static LoggerConfiguration WithExceptionDetails( - this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, - IDestructuringOptions destructuringOptions) - { + /// + /// Enrich logger output with a destuctured object containing exception's public properties. + /// + /// The enrichment configuration. + /// + /// Options that will influence the process of destructuring exception's properties into result object. + /// + /// Configuration object allowing method chaining. + public static LoggerConfiguration WithExceptionDetails( + this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, + IDestructuringOptions destructuringOptions) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(loggerEnrichmentConfiguration); + ArgumentNullException.ThrowIfNull(loggerEnrichmentConfiguration); #else - if (loggerEnrichmentConfiguration is null) - { - throw new ArgumentNullException(nameof(loggerEnrichmentConfiguration)); - } + if (loggerEnrichmentConfiguration is null) + { + throw new ArgumentNullException(nameof(loggerEnrichmentConfiguration)); + } #endif - ILogEventEnricher enricher = new ExceptionEnricher(destructuringOptions); - return loggerEnrichmentConfiguration.With(enricher); - } + ILogEventEnricher enricher = new ExceptionEnricher(destructuringOptions); + return loggerEnrichmentConfiguration.With(enricher); } } diff --git a/Source/Serilog.Exceptions/Reflection/ReflectionInfo.cs b/Source/Serilog.Exceptions/Reflection/ReflectionInfo.cs index 29fac7ea..cb126141 100644 --- a/Source/Serilog.Exceptions/Reflection/ReflectionInfo.cs +++ b/Source/Serilog.Exceptions/Reflection/ReflectionInfo.cs @@ -1,35 +1,34 @@ -namespace Serilog.Exceptions.Reflection -{ - using System; +namespace Serilog.Exceptions.Reflection; + +using System; +/// +/// Contains metadata information about a type +/// useful for destructuring process. +/// +internal class ReflectionInfo +{ /// - /// Contains metadata information about a type - /// useful for destructuring process. + /// Initializes a new instance of the class. /// - internal class ReflectionInfo + /// All properties for a type. + /// All properties except of properties which are handled separately. + public ReflectionInfo( + ReflectionPropertyInfo[] properties, + ReflectionPropertyInfo[] propertiesExceptBaseOnes) { - /// - /// Initializes a new instance of the class. - /// - /// All properties for a type. - /// All properties except of properties which are handled separately. - public ReflectionInfo( - ReflectionPropertyInfo[] properties, - ReflectionPropertyInfo[] propertiesExceptBaseOnes) - { - this.Properties = properties; - this.PropertiesExceptBaseOnes = propertiesExceptBaseOnes; - } + this.Properties = properties; + this.PropertiesExceptBaseOnes = propertiesExceptBaseOnes; + } - /// - /// Gets information about all properties to be destructured. - /// - public ReflectionPropertyInfo[] Properties { get; } + /// + /// Gets information about all properties to be destructured. + /// + public ReflectionPropertyInfo[] Properties { get; } - /// - /// Gets information about properties other than base exception properties - /// which are handled separately. - /// - public ReflectionPropertyInfo[] PropertiesExceptBaseOnes { get; } - } + /// + /// Gets information about properties other than base exception properties + /// which are handled separately. + /// + public ReflectionPropertyInfo[] PropertiesExceptBaseOnes { get; } } diff --git a/Source/Serilog.Exceptions/Reflection/ReflectionInfoExtractor.cs b/Source/Serilog.Exceptions/Reflection/ReflectionInfoExtractor.cs index 48741973..90c68c45 100644 --- a/Source/Serilog.Exceptions/Reflection/ReflectionInfoExtractor.cs +++ b/Source/Serilog.Exceptions/Reflection/ReflectionInfoExtractor.cs @@ -1,110 +1,109 @@ -namespace Serilog.Exceptions.Reflection +namespace Serilog.Exceptions.Reflection; + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +/// +/// Utility class that analyzes type using reflection and provides +/// information about properties to be used in destructuring process. +/// +internal class ReflectionInfoExtractor { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; + private readonly ConcurrentDictionary reflectionInfoCache = new(); + private readonly IList baseExceptionPropertiesForDestructuring; /// - /// Utility class that analyzes type using reflection and provides - /// information about properties to be used in destructuring process. + /// Initializes a new instance of the class. /// - internal class ReflectionInfoExtractor - { - private readonly ConcurrentDictionary reflectionInfoCache = new(); - private readonly IList baseExceptionPropertiesForDestructuring; - - /// - /// Initializes a new instance of the class. - /// - public ReflectionInfoExtractor() => this.baseExceptionPropertiesForDestructuring = GetExceptionPropertiesForDestructuring(typeof(Exception)); + public ReflectionInfoExtractor() => this.baseExceptionPropertiesForDestructuring = GetExceptionPropertiesForDestructuring(typeof(Exception)); - /// - /// Gets reflection info for relevant properties of "/> - /// from cache or by generating it if they are not yet present in cache. - /// - /// The type for which properties are to be analyzed. - /// The reflection info for relevant properties of . - public ReflectionInfo GetOrCreateReflectionInfo(Type valueType) => this.reflectionInfoCache.GetOrAdd(valueType, valueFactory: this.GenerateReflectionInfoForType); + /// + /// Gets reflection info for relevant properties of "/> + /// from cache or by generating it if they are not yet present in cache. + /// + /// The type for which properties are to be analyzed. + /// The reflection info for relevant properties of . + public ReflectionInfo GetOrCreateReflectionInfo(Type valueType) => this.reflectionInfoCache.GetOrAdd(valueType, valueFactory: this.GenerateReflectionInfoForType); - private static Func GenerateFastGetterForProperty(Type type, PropertyInfo property) - { - var objParam = Expression.Parameter(typeof(object), "num"); - var typedObj = Expression.Convert(objParam, type); - var memberExpression = Expression.Property(typedObj, property); - var typedResult = Expression.Convert(memberExpression, typeof(object)); - var resultLambda = Expression.Lambda>(typedResult, objParam); - return resultLambda.Compile(); - } + private static Func GenerateFastGetterForProperty(Type type, PropertyInfo property) + { + var objParam = Expression.Parameter(typeof(object), "num"); + var typedObj = Expression.Convert(objParam, type); + var memberExpression = Expression.Property(typedObj, property); + var typedResult = Expression.Convert(memberExpression, typeof(object)); + var resultLambda = Expression.Lambda>(typedResult, objParam); + return resultLambda.Compile(); + } - private static List GetExceptionPropertiesForDestructuring(Type valueType) - { - var allProperties = valueType - .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => x.CanRead && x.GetIndexParameters().Length == 0) - .ToList(); + private static List GetExceptionPropertiesForDestructuring(Type valueType) + { + var allProperties = valueType + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.CanRead && x.GetIndexParameters().Length == 0) + .ToList(); - return allProperties; - } + return allProperties; + } - private static void MarkRedefinedPropertiesWithFullName(ReflectionPropertyInfo[] propertyInfos) + private static void MarkRedefinedPropertiesWithFullName(ReflectionPropertyInfo[] propertyInfos) + { + // First, prepare a dictionary of properties grouped by name + var groupedByName = new Dictionary>(); + foreach (var propertyInfo in propertyInfos) { - // First, prepare a dictionary of properties grouped by name - var groupedByName = new Dictionary>(); - foreach (var propertyInfo in propertyInfos) + if (groupedByName.ContainsKey(propertyInfo.Name)) { - if (groupedByName.ContainsKey(propertyInfo.Name)) - { - groupedByName[propertyInfo.Name].Add(propertyInfo); - } - else - { - groupedByName[propertyInfo.Name] = new List { propertyInfo }; - } + groupedByName[propertyInfo.Name].Add(propertyInfo); } - - // Then, fix groups that have more than one property in it - // It means that there is a name uniqueness conflict which needs to be resolved - foreach (var nameAndProperties in groupedByName) + else { - var properties = nameAndProperties.Value; - if (properties.Count > 1) - { - FixGroupOfPropertiesWithConflictingNames(properties); - } + groupedByName[propertyInfo.Name] = new List { propertyInfo }; } } - private static void FixGroupOfPropertiesWithConflictingNames(List properties) + // Then, fix groups that have more than one property in it + // It means that there is a name uniqueness conflict which needs to be resolved + foreach (var nameAndProperties in groupedByName) { - // Very simplistic approach, just check each pair separately. - // The implementation has O(N^2) complexity but in practice - // N will be extremely rarely other than 2. - foreach (var property in properties) + var properties = nameAndProperties.Value; + if (properties.Count > 1) { - foreach (var otherProperty in properties) - { - property.MarkNameWithTypeNameIfRedefinesThatProperty(otherProperty); - } + FixGroupOfPropertiesWithConflictingNames(properties); } } + } - private ReflectionInfo GenerateReflectionInfoForType(Type valueType) + private static void FixGroupOfPropertiesWithConflictingNames(List properties) + { + // Very simplistic approach, just check each pair separately. + // The implementation has O(N^2) complexity but in practice + // N will be extremely rarely other than 2. + foreach (var property in properties) { - var properties = GetExceptionPropertiesForDestructuring(valueType); - var propertyInfos = properties - .Select(p => new ReflectionPropertyInfo(p.Name, p.DeclaringType, GenerateFastGetterForProperty(valueType, p))) - .ToArray(); + foreach (var otherProperty in properties) + { + property.MarkNameWithTypeNameIfRedefinesThatProperty(otherProperty); + } + } + } - MarkRedefinedPropertiesWithFullName(propertyInfos); + private ReflectionInfo GenerateReflectionInfoForType(Type valueType) + { + var properties = GetExceptionPropertiesForDestructuring(valueType); + var propertyInfos = properties + .Select(p => new ReflectionPropertyInfo(p.Name, p.DeclaringType, GenerateFastGetterForProperty(valueType, p))) + .ToArray(); - var propertiesInfosExceptBaseOnes = propertyInfos - .Where(p => this.baseExceptionPropertiesForDestructuring.All(bp => bp.Name != p.Name)) - .ToArray(); + MarkRedefinedPropertiesWithFullName(propertyInfos); - return new ReflectionInfo(propertyInfos, propertiesInfosExceptBaseOnes); - } + var propertiesInfosExceptBaseOnes = propertyInfos + .Where(p => this.baseExceptionPropertiesForDestructuring.All(bp => bp.Name != p.Name)) + .ToArray(); + + return new ReflectionInfo(propertyInfos, propertiesInfosExceptBaseOnes); } } diff --git a/Source/Serilog.Exceptions/Reflection/ReflectionPropertyInfo.cs b/Source/Serilog.Exceptions/Reflection/ReflectionPropertyInfo.cs index 8177de68..e623f72e 100644 --- a/Source/Serilog.Exceptions/Reflection/ReflectionPropertyInfo.cs +++ b/Source/Serilog.Exceptions/Reflection/ReflectionPropertyInfo.cs @@ -1,105 +1,104 @@ -namespace Serilog.Exceptions.Reflection -{ - using System; +namespace Serilog.Exceptions.Reflection; + +using System; #if NETSTANDARD1_3 - using System.Reflection; +using System.Reflection; #endif +/// +/// Class that holds reflection information about a single property. +/// +internal class ReflectionPropertyInfo +{ /// - /// Class that holds reflection information about a single property. + /// Marker that represents a decision whether to extend property name + /// with type name of declaring type, to avoid name uniqueness conflicts. /// - internal class ReflectionPropertyInfo - { - /// - /// Marker that represents a decision whether to extend property name - /// with type name of declaring type, to avoid name uniqueness conflicts. - /// - private bool markedWithTypeName; + private bool markedWithTypeName; - /// - /// Initializes a new instance of the class. - /// - /// The of the property without type name. - /// The type which declares the property. - /// Runtime function that can extract value of the property from object. - public ReflectionPropertyInfo(string name, Type? declaringType, Func getter) - { - this.Name = name; - this.DeclaringType = declaringType; - this.Getter = getter; - } + /// + /// Initializes a new instance of the class. + /// + /// The of the property without type name. + /// The type which declares the property. + /// Runtime function that can extract value of the property from object. + public ReflectionPropertyInfo(string name, Type? declaringType, Func getter) + { + this.Name = name; + this.DeclaringType = declaringType; + this.Getter = getter; + } - /// - /// Gets name of the property. - /// It includes type name if name conflicts with other property of derived class. - /// - public string Name { get; private set; } + /// + /// Gets name of the property. + /// It includes type name if name conflicts with other property of derived class. + /// + public string Name { get; private set; } - /// - /// Gets the type that declared the property. - /// - public Type? DeclaringType { get; } + /// + /// Gets the type that declared the property. + /// + public Type? DeclaringType { get; } - /// - /// Gets a function that extracts value of the property from an object. - /// - public Func Getter { get; } + /// + /// Gets a function that extracts value of the property from an object. + /// + public Func Getter { get; } - /// - /// Idempotent action that extends property name with a type name - /// of its declaring type to avoid name uniqueness conflict. - /// - public void MarkNameWithTypeName() + /// + /// Idempotent action that extends property name with a type name + /// of its declaring type to avoid name uniqueness conflict. + /// + public void MarkNameWithTypeName() + { + if (!this.markedWithTypeName) { - if (!this.markedWithTypeName) - { - this.markedWithTypeName = true; - this.Name = $"{this.DeclaringType?.Name}.{this.Name}"; - } + this.markedWithTypeName = true; + this.Name = $"{this.DeclaringType?.Name}.{this.Name}"; } + } - /// - /// Conditionally marks property with its declaring type name to avoid - /// name uniqueness conflict. - /// - /// Other property info that is be compared to detect name uniqueness conflict. - public void MarkNameWithTypeNameIfRedefinesThatProperty(ReflectionPropertyInfo otherPropertyInfo) + /// + /// Conditionally marks property with its declaring type name to avoid + /// name uniqueness conflict. + /// + /// Other property info that is be compared to detect name uniqueness conflict. + public void MarkNameWithTypeNameIfRedefinesThatProperty(ReflectionPropertyInfo otherPropertyInfo) + { + if (otherPropertyInfo == this) { - if (otherPropertyInfo == this) - { - return; - } + return; + } - if (this.DeclaringType == null || otherPropertyInfo?.DeclaringType == null) - { - return; - } + if (this.DeclaringType == null || otherPropertyInfo?.DeclaringType == null) + { + return; + } - if (otherPropertyInfo?.Name != this.Name) - { - return; - } + if (otherPropertyInfo?.Name != this.Name) + { + return; + } - if (this.markedWithTypeName) - { - return; - } + if (this.markedWithTypeName) + { + return; + } - var shouldMarkWithTypeName = IsSubTypeOf( - otherPropertyInfo.DeclaringType, - this.DeclaringType); + var shouldMarkWithTypeName = IsSubTypeOf( + otherPropertyInfo.DeclaringType, + this.DeclaringType); - if (shouldMarkWithTypeName) - { - this.MarkNameWithTypeName(); - } + if (shouldMarkWithTypeName) + { + this.MarkNameWithTypeName(); } + } - private static bool IsSubTypeOf(Type possibleSubType, Type possibleBaseType) => + private static bool IsSubTypeOf(Type possibleSubType, Type possibleBaseType) => #if NETSTANDARD1_3 possibleBaseType.GetTypeInfo().IsSubclassOf(possibleBaseType); #else possibleSubType.IsSubclassOf(possibleBaseType); #endif - } } diff --git a/Source/Serilog.Exceptions/TypeExtensions.cs b/Source/Serilog.Exceptions/TypeExtensions.cs index 85cdb480..741ed326 100644 --- a/Source/Serilog.Exceptions/TypeExtensions.cs +++ b/Source/Serilog.Exceptions/TypeExtensions.cs @@ -1,97 +1,96 @@ -namespace Serilog.Exceptions -{ - using System; +namespace Serilog.Exceptions; + +using System; #if NETSTANDARD1_3 - using System.Reflection; +using System.Reflection; #endif +/// +/// Helper methods for class. +/// +internal static class TypeExtensions +{ /// - /// Helper methods for class. + /// Helper method that return type codes. Fills a missing method in netstandard1.3. /// - internal static class TypeExtensions - { - /// - /// Helper method that return type codes. Fills a missing method in netstandard1.3. - /// - /// The input type. - /// The type code. + /// The input type. + /// The type code. #if NETSTANDARD1_3 - public static TypeCode GetTypeCode(this Type type) + public static TypeCode GetTypeCode(this Type type) + { + if (type is null) { - if (type is null) - { - return TypeCode.Empty; - } - - if (type == typeof(bool)) - { - return TypeCode.Boolean; - } - else if (type == typeof(char)) - { - return TypeCode.Char; - } - else if (type == typeof(sbyte)) - { - return TypeCode.SByte; - } - else if (type == typeof(byte)) - { - return TypeCode.Byte; - } - else if (type == typeof(short)) - { - return TypeCode.Int16; - } - else if (type == typeof(ushort)) - { - return TypeCode.UInt16; - } - else if (type == typeof(int)) - { - return TypeCode.Int32; - } - else if (type == typeof(uint)) - { - return TypeCode.UInt32; - } - else if (type == typeof(long)) - { - return TypeCode.Int64; - } - else if (type == typeof(ulong)) - { - return TypeCode.UInt64; - } - else if (type == typeof(float)) - { - return TypeCode.Single; - } - else if (type == typeof(double)) - { - return TypeCode.Double; - } - else if (type == typeof(decimal)) - { - return TypeCode.Decimal; - } - else if (type == typeof(DateTime)) - { - return TypeCode.DateTime; - } - else if (type == typeof(string)) - { - return TypeCode.String; - } - else if (type.GetTypeInfo().IsEnum) - { - return TypeCode.Int32; - } + return TypeCode.Empty; + } - return TypeCode.Object; + if (type == typeof(bool)) + { + return TypeCode.Boolean; } + else if (type == typeof(char)) + { + return TypeCode.Char; + } + else if (type == typeof(sbyte)) + { + return TypeCode.SByte; + } + else if (type == typeof(byte)) + { + return TypeCode.Byte; + } + else if (type == typeof(short)) + { + return TypeCode.Int16; + } + else if (type == typeof(ushort)) + { + return TypeCode.UInt16; + } + else if (type == typeof(int)) + { + return TypeCode.Int32; + } + else if (type == typeof(uint)) + { + return TypeCode.UInt32; + } + else if (type == typeof(long)) + { + return TypeCode.Int64; + } + else if (type == typeof(ulong)) + { + return TypeCode.UInt64; + } + else if (type == typeof(float)) + { + return TypeCode.Single; + } + else if (type == typeof(double)) + { + return TypeCode.Double; + } + else if (type == typeof(decimal)) + { + return TypeCode.Decimal; + } + else if (type == typeof(DateTime)) + { + return TypeCode.DateTime; + } + else if (type == typeof(string)) + { + return TypeCode.String; + } + else if (type.GetTypeInfo().IsEnum) + { + return TypeCode.Int32; + } + + return TypeCode.Object; + } #else - public static TypeCode GetTypeCode(this Type type) => Type.GetTypeCode(type); + public static TypeCode GetTypeCode(this Type type) => Type.GetTypeCode(type); #endif - } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/AggregateExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/AggregateExceptionDestructurerTest.cs index 3ae65ca8..cfc66550 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/AggregateExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/AggregateExceptionDestructurerTest.cs @@ -1,27 +1,26 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using Newtonsoft.Json.Linq; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using Newtonsoft.Json.Linq; +using Xunit; +using static LogJsonOutputUtils; - public class AggregateExceptionDestructurerTest +public class AggregateExceptionDestructurerTest +{ + [Fact] + public void AggregateException_WithTwoArgumentExceptions_TheyAreSerializedInInnerExceptionsProperty() { - [Fact] - public void AggregateException_WithTwoArgumentExceptions_TheyAreSerializedInInnerExceptionsProperty() - { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var argumentException1 = new ArgumentException("MSG1", "testParamName1"); - var argumentException2 = new ArgumentException("MSG1", "testParamName2"); + var argumentException1 = new ArgumentException("MSG1", "testParamName1"); + var argumentException2 = new ArgumentException("MSG1", "testParamName2"); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - var aggregateException = new AggregateException(argumentException1, argumentException2); + var aggregateException = new AggregateException(argumentException1, argumentException2); - var rootObject = LogAndDestructureException(aggregateException); - var innerExceptions = ExtractInnerExceptionsProperty(rootObject); + var rootObject = LogAndDestructureException(aggregateException); + var innerExceptions = ExtractInnerExceptionsProperty(rootObject); - Assert.Equal(2, innerExceptions.Count); - Assert_ContainsPropertyWithValue(Assert.IsType(innerExceptions[0]), "ParamName", "testParamName1"); - Assert_ContainsPropertyWithValue(Assert.IsType(innerExceptions[1]), "ParamName", "testParamName2"); - } + Assert.Equal(2, innerExceptions.Count); + Assert_ContainsPropertyWithValue(Assert.IsType(innerExceptions[0]), "ParamName", "testParamName1"); + Assert_ContainsPropertyWithValue(Assert.IsType(innerExceptions[1]), "ParamName", "testParamName2"); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ApiExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ApiExceptionDestructurerTest.cs index 4c374af8..d488b0f6 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ApiExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ApiExceptionDestructurerTest.cs @@ -1,100 +1,99 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using System.Net; - using System.Net.Http; - using System.Net.Http.Json; - using System.Threading.Tasks; - using global::Refit; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Refit.Destructurers; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using global::Refit; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Refit.Destructurers; +using Xunit; +using static LogJsonOutputUtils; - public class ApiExceptionDestructurerTest +public class ApiExceptionDestructurerTest +{ + [Fact] + public async Task ApiException_HttpStatusCodeIsLoggedAsPropertyAsync() { - [Fact] - public async Task ApiException_HttpStatusCodeIsLoggedAsPropertyAsync() - { - using var message = new HttpRequestMessage(HttpMethod.Get, new Uri("https://foobar.com")); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + using var message = new HttpRequestMessage(HttpMethod.Get, new Uri("https://foobar.com")); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options); - } + Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options); + } - [Fact] - public async Task ApiException_UriIsLoggedAsPropertyAsync() - { - var requestUri = new Uri("https://foobar.com"); - using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + [Fact] + public async Task ApiException_UriIsLoggedAsPropertyAsync() + { + var requestUri = new Uri("https://foobar.com"); + using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Uri), requestUri.ToString(), options); - } + Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Uri), requestUri.ToString(), options); + } - [Fact] - public async Task ApiException_ByDefaultContentIsNotLoggedAsPropertyAsync() - { - var requestUri = new Uri("https://foobar.com"); - using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); - response.Content = JsonContent.Create("hello"); + [Fact] + public async Task ApiException_ByDefaultContentIsNotLoggedAsPropertyAsync() + { + var requestUri = new Uri("https://foobar.com"); + using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); + response.Content = JsonContent.Create("hello"); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(ApiException.Content), options); - } + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(ApiException.Content), options); + } - [Fact] - public async Task ApiException_WhenSpecifiedContentIsLoggedAsPropertyAsync() - { - var requestUri = new Uri("https://foobar.com"); - using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureHttpContent: true) }); - response.Content = JsonContent.Create("hello"); + [Fact] + public async Task ApiException_WhenSpecifiedContentIsLoggedAsPropertyAsync() + { + var requestUri = new Uri("https://foobar.com"); + using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureHttpContent: true) }); + response.Content = JsonContent.Create("hello"); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Content), "\"hello\"", options); - } + Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Content), "\"hello\"", options); + } - [Fact] - public async Task ApiException_ByDefaultCommonPropertiesLoggedAsPropertiesAsync() - { - var requestUri = new Uri("https://foobar.com"); - using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + [Fact] + public async Task ApiException_ByDefaultCommonPropertiesLoggedAsPropertiesAsync() + { + var requestUri = new Uri("https://foobar.com"); + using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - // No need to test all properties, just a handful is sufficient - Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.StackTrace), apiException.StackTrace, options); - Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.Message), apiException.Message, options); - Test_LoggedExceptionContainsProperty(apiException, nameof(Type), apiException.GetType().ToString(), options); - } + // No need to test all properties, just a handful is sufficient + Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.StackTrace), apiException.StackTrace, options); + Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.Message), apiException.Message, options); + Test_LoggedExceptionContainsProperty(apiException, nameof(Type), apiException.GetType().ToString(), options); + } - [Fact] - public async Task ApiException_WhenSpecifiedCommonPropertiesNotLoggedAsPropertiesAsync() - { - var requestUri = new Uri("https://foobar.com"); - using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); - using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureCommonExceptionProperties: false) }); - var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); + [Fact] + public async Task ApiException_WhenSpecifiedCommonPropertiesNotLoggedAsPropertiesAsync() + { + var requestUri = new Uri("https://foobar.com"); + using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); + using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureCommonExceptionProperties: false) }); + var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.StackTrace), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Message), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.InnerException), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HelpLink), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Data), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HResult), options); - Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Type), options); - } + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.StackTrace), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Message), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.InnerException), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HelpLink), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Data), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HResult), options); + Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Type), options); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentExceptionDestructurerTest.cs index 5ad432aa..520d082f 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentExceptionDestructurerTest.cs @@ -1,18 +1,17 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using Xunit; +using static LogJsonOutputUtils; - public class ArgumentExceptionDestructurerTest +public class ArgumentExceptionDestructurerTest +{ + [Fact] + public void ArgumentException_ParamNameIsAttachedAsProperty() { - [Fact] - public void ArgumentException_ParamNameIsAttachedAsProperty() - { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var argumentException = new ArgumentException("MSG", "testParamName"); + var argumentException = new ArgumentException("MSG", "testParamName"); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); - } + Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentNullExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentNullExceptionDestructurerTest.cs index a8c809c6..560c8dda 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentNullExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentNullExceptionDestructurerTest.cs @@ -1,18 +1,17 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using Xunit; +using static LogJsonOutputUtils; - public class ArgumentNullExceptionDestructurerTest +public class ArgumentNullExceptionDestructurerTest +{ + [Fact] + public void ArgumentNullException_ParamNameIsAttachedAsProperty() { - [Fact] - public void ArgumentNullException_ParamNameIsAttachedAsProperty() - { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var argumentException = new ArgumentNullException("testParamName", "MSG"); + var argumentException = new ArgumentNullException("testParamName", "MSG"); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); - } + Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentOutOfRangeExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentOutOfRangeExceptionDestructurerTest.cs index 3aa0d397..3e4867e8 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentOutOfRangeExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ArgumentOutOfRangeExceptionDestructurerTest.cs @@ -1,27 +1,26 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using Xunit; +using static LogJsonOutputUtils; - public class ArgumentOutOfRangeExceptionDestructurerTest +public class ArgumentOutOfRangeExceptionDestructurerTest +{ + [Fact] + public void ArgumentOfOutRangeException_ParamNameIsAttachedAsProperty() { - [Fact] - public void ArgumentOfOutRangeException_ParamNameIsAttachedAsProperty() - { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var argumentException = new ArgumentOutOfRangeException("testParamName"); + var argumentException = new ArgumentOutOfRangeException("testParamName"); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); - } + Test_LoggedExceptionContainsProperty(argumentException, "ParamName", "testParamName"); + } - [Fact] - public void ArgumentOfOutRangeException_ActualValueIsAttachedAsProperty() - { + [Fact] + public void ArgumentOfOutRangeException_ActualValueIsAttachedAsProperty() + { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var argumentException = new ArgumentOutOfRangeException("testParamName", "ACTUAL_VALUE", "MSG"); + var argumentException = new ArgumentOutOfRangeException("testParamName", "ACTUAL_VALUE", "MSG"); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - Test_LoggedExceptionContainsProperty(argumentException, "ActualValue", "ACTUAL_VALUE"); - } + Test_LoggedExceptionContainsProperty(argumentException, "ActualValue", "ACTUAL_VALUE"); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/DbUpdateExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/DbUpdateExceptionDestructurerTest.cs index 8f0c3cd3..1f8d3b74 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/DbUpdateExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/DbUpdateExceptionDestructurerTest.cs @@ -1,89 +1,89 @@ -namespace Serilog.Exceptions.Test.Destructurers +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.EntityFrameworkCore.Destructurers; +using Serilog.Formatting.Json; +using Xunit; +using static LogJsonOutputUtils; + +public class DbUpdateExceptionDestructurerTest { - using System; - using System.Collections.Generic; - using System.IO; - using Microsoft.EntityFrameworkCore; - using Microsoft.EntityFrameworkCore.ChangeTracking; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.EntityFrameworkCore.Destructurers; - using Serilog.Formatting.Json; - using Xunit; - using static LogJsonOutputUtils; - - public class DbUpdateExceptionDestructurerTest + [Fact] + public void WithoutDbUpdateExceptionDestructurerShouldLogDbValues() { - [Fact] - public void WithoutDbUpdateExceptionDestructurerShouldLogDbValues() + // Arrange + var jsonWriter = new StringWriter(); + ILogger logger = new LoggerConfiguration() + .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder().WithDefaultDestructurers()) + .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) + .CreateLogger(); + using var ctx = new TestContext(); + ctx.Database.EnsureDeleted(); + ctx.Database.EnsureCreated(); + + var entry = ctx.Add(new User { - // Arrange - var jsonWriter = new StringWriter(); - ILogger logger = new LoggerConfiguration() - .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder().WithDefaultDestructurers()) - .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) - .CreateLogger(); - using var ctx = new TestContext(); - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - - var entry = ctx.Add(new User - { - UserId = Guid.NewGuid().ToString(), - }); - - // Act - logger.Error(new TestDbUpdateException("DbUpdate Error", entry), "Error"); - - // Assert - var writtenJson = jsonWriter.ToString(); - Assert.True(writtenJson.Contains(TestContext.UserIdIDoNotWantToSee, StringComparison.Ordinal) || - writtenJson.Contains("\"Users\":\"threw System.TypeInitializationException", StringComparison.Ordinal)); - } + UserId = Guid.NewGuid().ToString(), + }); - [Fact] - public void WithDbUpdateExceptionDestructurerShouldNotLogDbValues() - { - // Arrange - var jsonWriter = new StringWriter(); - ILogger logger = new LoggerConfiguration() - .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder().WithDefaultDestructurers().WithDestructurers(new[] { new TestDbUpdateExceptionDestructurer() })) - .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) - .CreateLogger(); - using var ctx = new TestContext(); - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - - var entry = ctx.Add(new User - { - UserId = Guid.NewGuid().ToString(), - }); - - // Act - logger.Error(new TestDbUpdateException("DbUpdate Error", entry), "Error"); - - // Assert - var writtenJson = jsonWriter.ToString(); - Assert.DoesNotContain(TestContext.UserIdIDoNotWantToSee, writtenJson, StringComparison.Ordinal); - } + // Act + logger.Error(new TestDbUpdateException("DbUpdate Error", entry), "Error"); - internal class User - { - public string? UserId { get; set; } - } + // Assert + var writtenJson = jsonWriter.ToString(); + Assert.True(writtenJson.Contains(TestContext.UserIdIDoNotWantToSee, StringComparison.Ordinal) || + writtenJson.Contains("\"Users\":\"threw System.TypeInitializationException", StringComparison.Ordinal)); + } - internal class TestContext : DbContext + [Fact] + public void WithDbUpdateExceptionDestructurerShouldNotLogDbValues() + { + // Arrange + var jsonWriter = new StringWriter(); + ILogger logger = new LoggerConfiguration() + .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder().WithDefaultDestructurers().WithDestructurers(new[] { new TestDbUpdateExceptionDestructurer() })) + .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) + .CreateLogger(); + using var ctx = new TestContext(); + ctx.Database.EnsureDeleted(); + ctx.Database.EnsureCreated(); + + var entry = ctx.Add(new User { - public const string UserIdIDoNotWantToSee = "I Don't Want To See You"; + UserId = Guid.NewGuid().ToString(), + }); + + // Act + logger.Error(new TestDbUpdateException("DbUpdate Error", entry), "Error"); + + // Assert + var writtenJson = jsonWriter.ToString(); + Assert.DoesNotContain(TestContext.UserIdIDoNotWantToSee, writtenJson, StringComparison.Ordinal); + } + + internal class User + { + public string? UserId { get; set; } + } + + internal class TestContext : DbContext + { + public const string UserIdIDoNotWantToSee = "I Don't Want To See You"; - public DbSet? Users { get; set; } + public DbSet? Users { get; set; } - public string CustomData { get; set; } = UserIdIDoNotWantToSee; + public string CustomData { get; set; } = UserIdIDoNotWantToSee; - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(databaseName: "TestDebUpdateException"); + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(databaseName: "TestDebUpdateException"); - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - var users = new List + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + var users = new List { new User { @@ -95,23 +95,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) }, }; - modelBuilder.Entity().HasData(users); - } + modelBuilder.Entity().HasData(users); } + } - internal class TestDbUpdateException : DbUpdateException - { - private readonly IReadOnlyList entityEntries; + internal class TestDbUpdateException : DbUpdateException + { + private readonly IReadOnlyList entityEntries; - public TestDbUpdateException(string message, EntityEntry entityEntry) - : base(message, (Exception)null!) => this.entityEntries = new List { entityEntry }.AsReadOnly(); + public TestDbUpdateException(string message, EntityEntry entityEntry) + : base(message, (Exception)null!) => this.entityEntries = new List { entityEntry }.AsReadOnly(); - public override IReadOnlyList Entries => this.entityEntries; - } + public override IReadOnlyList Entries => this.entityEntries; + } - internal class TestDbUpdateExceptionDestructurer : DbUpdateExceptionDestructurer - { - public override Type[] TargetTypes => new[] { typeof(TestDbUpdateException) }; - } + internal class TestDbUpdateExceptionDestructurer : DbUpdateExceptionDestructurer + { + public override Type[] TargetTypes => new[] { typeof(TestDbUpdateException) }; } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionDestructurerTest.cs index ec4a9075..94b29e7f 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionDestructurerTest.cs @@ -1,238 +1,237 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using FluentAssertions; - using Microsoft.EntityFrameworkCore; - using Moq; - using Newtonsoft.Json.Linq; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Filters; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.Collections.Generic; +using System.Globalization; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Moq; +using Newtonsoft.Json.Linq; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Filters; +using Xunit; +using static LogJsonOutputUtils; #pragma warning disable CA2208 // Instantiate argument exceptions correctly - public class ExceptionDestructurerTest +public class ExceptionDestructurerTest +{ + [Fact] + public void ArgumentException_ContainsMessage() { - [Fact] - public void ArgumentException_ContainsMessage() - { - var applicationException = new ArgumentException("MSG"); - Test_LoggedExceptionContainsProperty(applicationException, "Message", "MSG"); - } + var applicationException = new ArgumentException("MSG"); + Test_LoggedExceptionContainsProperty(applicationException, "Message", "MSG"); + } - [Fact] - public void ArgumentException_ContainsHelpLink() - { - var applicationException = new ArgumentException() { HelpLink = "HELP LINK" }; - Test_LoggedExceptionContainsProperty(applicationException, "HelpLink", "HELP LINK"); - } + [Fact] + public void ArgumentException_ContainsHelpLink() + { + var applicationException = new ArgumentException() { HelpLink = "HELP LINK" }; + Test_LoggedExceptionContainsProperty(applicationException, "HelpLink", "HELP LINK"); + } - [Fact] - public void ArgumentException_ContainsSource() - { - var applicationException = new ArgumentException() { Source = "SOURCE" }; - Test_LoggedExceptionContainsProperty(applicationException, "Source", "SOURCE"); - } + [Fact] + public void ArgumentException_ContainsSource() + { + var applicationException = new ArgumentException() { Source = "SOURCE" }; + Test_LoggedExceptionContainsProperty(applicationException, "Source", "SOURCE"); + } - [Fact] - public void ArgumentException_WithoutStackTrace_ContainsNullStackTrace() - { - var applicationException = new ArgumentException(); - Test_LoggedExceptionContainsProperty(applicationException, "StackTrace", null!); - } + [Fact] + public void ArgumentException_WithoutStackTrace_ContainsNullStackTrace() + { + var applicationException = new ArgumentException(); + Test_LoggedExceptionContainsProperty(applicationException, "StackTrace", null!); + } - [Fact] - public void ArgumentException_ContainsData() - { - var applicationException = new ArgumentException(); - applicationException.Data["SOMEKEY"] = "SOMEVALUE"; + [Fact] + public void ArgumentException_ContainsData() + { + var applicationException = new ArgumentException(); + applicationException.Data["SOMEKEY"] = "SOMEVALUE"; - var rootObject = LogAndDestructureException(applicationException); - var exceptionDetail = ExtractExceptionDetails(rootObject); + var rootObject = LogAndDestructureException(applicationException); + var exceptionDetail = ExtractExceptionDetails(rootObject); - var dataProperty = Assert.Single(exceptionDetail.Properties(), x => x.Name == "Data"); - var dataObject = Assert.IsType(dataProperty.Value); + var dataProperty = Assert.Single(exceptionDetail.Properties(), x => x.Name == "Data"); + var dataObject = Assert.IsType(dataProperty.Value); - var someKeyProperty = Assert.Single(dataObject.Properties(), x => x.Name == "SOMEKEY"); - var someKeyValue = Assert.IsType(someKeyProperty.Value); - Assert.Equal("SOMEVALUE", someKeyValue.Value); - } + var someKeyProperty = Assert.Single(dataObject.Properties(), x => x.Name == "SOMEKEY"); + var someKeyValue = Assert.IsType(someKeyProperty.Value); + Assert.Equal("SOMEVALUE", someKeyValue.Value); + } - [Fact] - public void ArgumentException_WithCustomRootName_ContainsDataInCustomRootName() - { - const string customRootName = "Ex"; - var applicationException = new ArgumentException(); - applicationException.Data["SOMEKEY"] = "SOMEVALUE"; + [Fact] + public void ArgumentException_WithCustomRootName_ContainsDataInCustomRootName() + { + const string customRootName = "Ex"; + var applicationException = new ArgumentException(); + applicationException.Data["SOMEKEY"] = "SOMEVALUE"; - var rootObject = LogAndDestructureException( - applicationException, - destructuringOptions: new DestructuringOptionsBuilder().WithDefaultDestructurers().WithRootName(customRootName)); - var exceptionDetail = ExtractExceptionDetails(rootObject, customRootName); + var rootObject = LogAndDestructureException( + applicationException, + destructuringOptions: new DestructuringOptionsBuilder().WithDefaultDestructurers().WithRootName(customRootName)); + var exceptionDetail = ExtractExceptionDetails(rootObject, customRootName); - Assert.Single(exceptionDetail.Properties(), x => x.Name == "Data"); - } + Assert.Single(exceptionDetail.Properties(), x => x.Name == "Data"); + } - [Fact] - public void PassedFilter_IsCalledWithCorrectArguments() - { - // Arrange - var exception = new Exception(); - var filterMock = new Mock(); + [Fact] + public void PassedFilter_IsCalledWithCorrectArguments() + { + // Arrange + var exception = new Exception(); + var filterMock = new Mock(); - // Act - LogAndDestructureException(exception, new DestructuringOptionsBuilder().WithFilter(filterMock.Object)); + // Act + LogAndDestructureException(exception, new DestructuringOptionsBuilder().WithFilter(filterMock.Object)); - // Assert - filterMock.Verify(x => x.ShouldPropertyBeFiltered(exception, "StackTrace", null)); - } + // Assert + filterMock.Verify(x => x.ShouldPropertyBeFiltered(exception, "StackTrace", null)); + } - [Fact] - public void WithoutReflectionBasedDestructurer_CustomExceptionIsNotLogged() - { - // Arrange - var exception = new DictNonScalarKeyException(); - var options = new DestructuringOptionsBuilder().WithoutReflectionBasedDestructurer(); + [Fact] + public void WithoutReflectionBasedDestructurer_CustomExceptionIsNotLogged() + { + // Arrange + var exception = new DictNonScalarKeyException(); + var options = new DestructuringOptionsBuilder().WithoutReflectionBasedDestructurer(); - // Act - var rootObject = LogAndDestructureException(exception, options); + // Act + var rootObject = LogAndDestructureException(exception, options); - // Assert - rootObject.Properties().Should().NotContain(x => x.Name == "Properties"); - } + // 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().Which.Value.Should().Be("arg"); - } + [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().Which.Value.Should().Be("arg"); + } - [Fact] - public void ArgumentException_WithStackTrace_ContainsStackTrace() + [Fact] + public void ArgumentException_WithStackTrace_ContainsStackTrace() + { + try { - try - { - throw new ArgumentException(); - } - catch (ArgumentException ex) - { - Test_LoggedExceptionContainsProperty(ex, "StackTrace", ex.StackTrace?.ToString(CultureInfo.InvariantCulture)); - } + throw new ArgumentException(); } - - [Fact] - public void ArgumentException_ContainsType() + catch (ArgumentException ex) { - var applicationException = new ArgumentException(); - Test_LoggedExceptionContainsProperty(applicationException, "Type", "System.ArgumentException"); + Test_LoggedExceptionContainsProperty(ex, "StackTrace", ex.StackTrace?.ToString(CultureInfo.InvariantCulture)); } + } - [Fact] - public void WhenExceptionContainsDictionaryWithNonScalarValue_ShouldNotThrow() - { - // Arrange - var exception = new DictNonScalarKeyException(); - exception.Reference.Add(new List() { 1, 2, 3 }, "VALUE"); - - // Act - var result = LogAndDestructureException(exception, new DestructuringOptionsBuilder()); - - // Assert - var exceptionDetails = ExtractExceptionDetails(result); - var referenceProperty = exceptionDetails.Should().BeOfType().Which - .Properties().Should().ContainSingle(x => x.Name == "Reference").Which; - - var referenceObject = referenceProperty.Value.Should().BeOfType().Which; - var kvp = referenceObject.Properties().Should().ContainSingle() - .Which.Should().BeOfType() - .Which.Name.Should().Be("System.Collections.Generic.List`1[System.Int32]"); - } + [Fact] + public void ArgumentException_ContainsType() + { + var applicationException = new ArgumentException(); + Test_LoggedExceptionContainsProperty(applicationException, "Type", "System.ArgumentException"); + } - [Fact] - public void WhenExceptionContainsDbContext_ShouldSkipIQueryableProperties() - { - // Arrange - using var context = new ExceptionDbContext(); - var exception = new CustomDbContextException("hello world", context); + [Fact] + public void WhenExceptionContainsDictionaryWithNonScalarValue_ShouldNotThrow() + { + // Arrange + var exception = new DictNonScalarKeyException(); + exception.Reference.Add(new List() { 1, 2, 3 }, "VALUE"); + + // Act + var result = LogAndDestructureException(exception, new DestructuringOptionsBuilder()); + + // Assert + var exceptionDetails = ExtractExceptionDetails(result); + var referenceProperty = exceptionDetails.Should().BeOfType().Which + .Properties().Should().ContainSingle(x => x.Name == "Reference").Which; + + var referenceObject = referenceProperty.Value.Should().BeOfType().Which; + var kvp = referenceObject.Properties().Should().ContainSingle() + .Which.Should().BeOfType() + .Which.Name.Should().Be("System.Collections.Generic.List`1[System.Int32]"); + } - // Act - var result = LogAndDestructureException(exception, new DestructuringOptionsBuilder()); + [Fact] + public void WhenExceptionContainsDbContext_ShouldSkipIQueryableProperties() + { + // Arrange + using var context = new ExceptionDbContext(); + var exception = new CustomDbContextException("hello world", context); - // Assert - var exceptionDetails = ExtractExceptionDetails(result).Should().BeOfType().Which; - var nameProperty = exceptionDetails - .Properties().Should().ContainSingle(x => x.Name == nameof(CustomDbContextException.Name)).Which - .Should().BeOfType().Which; + // Act + var result = LogAndDestructureException(exception, new DestructuringOptionsBuilder()); - nameProperty.Value.Should().BeOfType().Which.Value.Should().Be("hello world"); + // Assert + var exceptionDetails = ExtractExceptionDetails(result).Should().BeOfType().Which; + var nameProperty = exceptionDetails + .Properties().Should().ContainSingle(x => x.Name == nameof(CustomDbContextException.Name)).Which + .Should().BeOfType().Which; - var contextProperty = exceptionDetails - .Properties().Should().ContainSingle(x => x.Name == nameof(CustomDbContextException.Context)).Which; + nameProperty.Value.Should().BeOfType().Which.Value.Should().Be("hello world"); - var customerProperty = contextProperty.Value.Should().BeOfType().Which - .Properties().Should().ContainSingle(x => x.Name == nameof(ExceptionDbContext.Customer)).Which; + var contextProperty = exceptionDetails + .Properties().Should().ContainSingle(x => x.Name == nameof(CustomDbContextException.Context)).Which; - customerProperty.Value.Should().BeOfType().Which.Value.Should().BeOfType().Which - .Should().Be("IQueryable skipped"); - } + var customerProperty = contextProperty.Value.Should().BeOfType().Which + .Properties().Should().ContainSingle(x => x.Name == nameof(ExceptionDbContext.Customer)).Which; - public class DictNonScalarKeyException : Exception - { - public DictNonScalarKeyException() => this.Reference = new Dictionary, object>(); + customerProperty.Value.Should().BeOfType().Which.Value.Should().BeOfType().Which + .Should().Be("IQueryable skipped"); + } - public DictNonScalarKeyException(string message) - : base(message) => - this.Reference = new Dictionary, object>(); + public class DictNonScalarKeyException : Exception + { + public DictNonScalarKeyException() => this.Reference = new Dictionary, object>(); - public DictNonScalarKeyException(string message, Exception innerException) - : base(message, innerException) => - this.Reference = new Dictionary, object>(); + public DictNonScalarKeyException(string message) + : base(message) => + this.Reference = new Dictionary, object>(); - public Dictionary, object> Reference { get; } - } + public DictNonScalarKeyException(string message, Exception innerException) + : base(message, innerException) => + this.Reference = new Dictionary, object>(); + + public Dictionary, object> Reference { get; } + } #pragma warning disable CS3001 // Argument type is not CLS-compliant #pragma warning disable CS3003 // Type is not CLS-compliant - public class CustomDbContextException : Exception + public class CustomDbContextException : Exception + { + public CustomDbContextException(string name, DbContext context) { - public CustomDbContextException(string name, DbContext context) - { - this.Name = name; - this.Context = context; - } + this.Name = name; + this.Context = context; + } - public string Name { get; set; } + public string Name { get; set; } - public DbContext Context { get; } - } + public DbContext Context { get; } + } - private class ExceptionDbContext : DbContext - { - public DbSet Customer => this.Set(); + private class ExceptionDbContext : DbContext + { + public DbSet Customer => this.Set(); - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(databaseName: "TestDebUpdateException"); + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(databaseName: "TestDebUpdateException"); - public class CustomerEntity - { - public string? Name { get; set; } + public class CustomerEntity + { + public string? Name { get; set; } - public int Id { get; set; } - } + public int Id { get; set; } } } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionPropertiesBagTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionPropertiesBagTest.cs index d8ffbc95..4f2a0444 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionPropertiesBagTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ExceptionPropertiesBagTest.cs @@ -1,100 +1,99 @@ -namespace Serilog.Exceptions.Test.Destructurers +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Filters; +using Xunit; + +public class ExceptionPropertiesBagTest { - using System; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Filters; - using Xunit; + [Fact] + public void Constructor_GivenNullException_Throws() + { + // Act + var ex = Assert.Throws(() => new ExceptionPropertiesBag(null!)); + + // Assert + Assert.Equal("exception", ex.ParamName); + } + + [Fact] + public void AddedProperty_IsAvailableInReturnedDictionary() + { + // Arrange + var properties = new ExceptionPropertiesBag(new Exception(), null); + + // Act + properties.AddProperty("key", "value"); + + // Assert + var results = properties.GetResultDictionary(); + Assert.Equal(1, results.Count); + Assert.Contains("key", results.Keys); + var value = results["key"]; + Assert.Equal("value", value); + } + + [Fact] + public void CannotAddProperty_WhenResultWasAlreadyAquired() + { + // Arrange + var properties = new ExceptionPropertiesBag(new Exception(), null); + properties.AddProperty("key", "value"); + var results = properties.GetResultDictionary(); + + // Act + var ex = Assert.Throws(() => properties.AddProperty("key2", "value2")); + + // Assert + Assert.Equal("Cannot add exception property 'key2' to bag, after results were already collected", ex.Message); + } + + [Fact] + public void CannotAddProperty_WhenKeyIsNull() + { + // Arrange + var properties = new ExceptionPropertiesBag(new Exception(), null); + + // Act + var ex = Assert.Throws(() => properties.AddProperty(null!, "value")); + + // Assert + Assert.Equal("key", ex.ParamName); + } + + [Fact] + public void AddedProperty_WhenFilterIsSetToIgnoreIt_IsSkipped() + { + // Arrange + var properties = new ExceptionPropertiesBag( + new Exception(), + new IgnorePropertyByNameExceptionFilter(new[] { "key" })); + + // Act + properties.AddProperty("key", "value"); + + // Assert + var results = properties.GetResultDictionary(); + Assert.Equal(0, results.Count); + } - public class ExceptionPropertiesBagTest + [Fact] + public void AddedProperty_WhenFilterIsNotSetToIgnoreIt_IsIncluded() { - [Fact] - public void Constructor_GivenNullException_Throws() - { - // Act - var ex = Assert.Throws(() => new ExceptionPropertiesBag(null!)); - - // Assert - Assert.Equal("exception", ex.ParamName); - } - - [Fact] - public void AddedProperty_IsAvailableInReturnedDictionary() - { - // Arrange - var properties = new ExceptionPropertiesBag(new Exception(), null); - - // Act - properties.AddProperty("key", "value"); - - // Assert - var results = properties.GetResultDictionary(); - Assert.Equal(1, results.Count); - Assert.Contains("key", results.Keys); - var value = results["key"]; - Assert.Equal("value", value); - } - - [Fact] - public void CannotAddProperty_WhenResultWasAlreadyAquired() - { - // Arrange - var properties = new ExceptionPropertiesBag(new Exception(), null); - properties.AddProperty("key", "value"); - var results = properties.GetResultDictionary(); - - // Act - var ex = Assert.Throws(() => properties.AddProperty("key2", "value2")); - - // Assert - Assert.Equal("Cannot add exception property 'key2' to bag, after results were already collected", ex.Message); - } - - [Fact] - public void CannotAddProperty_WhenKeyIsNull() - { - // Arrange - var properties = new ExceptionPropertiesBag(new Exception(), null); - - // Act - var ex = Assert.Throws(() => properties.AddProperty(null!, "value")); - - // Assert - Assert.Equal("key", ex.ParamName); - } - - [Fact] - public void AddedProperty_WhenFilterIsSetToIgnoreIt_IsSkipped() - { - // Arrange - var properties = new ExceptionPropertiesBag( - new Exception(), - new IgnorePropertyByNameExceptionFilter(new[] { "key" })); - - // Act - properties.AddProperty("key", "value"); - - // Assert - var results = properties.GetResultDictionary(); - Assert.Equal(0, results.Count); - } - - [Fact] - public void AddedProperty_WhenFilterIsNotSetToIgnoreIt_IsIncluded() - { - // Arrange - var properties = new ExceptionPropertiesBag( - new Exception(), - new IgnorePropertyByNameExceptionFilter(new[] { "not key" })); - - // Act - properties.AddProperty("key", "value"); - - // Assert - var results = properties.GetResultDictionary(); - Assert.Equal(1, results.Count); - Assert.Contains("key", results.Keys); - var value = results["key"]; - Assert.Equal("value", value); - } + // Arrange + var properties = new ExceptionPropertiesBag( + new Exception(), + new IgnorePropertyByNameExceptionFilter(new[] { "not key" })); + + // Act + properties.AddProperty("key", "value"); + + // Assert + var results = properties.GetResultDictionary(); + Assert.Equal(1, results.Count); + Assert.Contains("key", results.Keys); + var value = results["key"]; + Assert.Equal("value", value); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/LogJsonOutputUtils.cs b/Tests/Serilog.Exceptions.Test/Destructurers/LogJsonOutputUtils.cs index aaeb7e04..79ce32d7 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/LogJsonOutputUtils.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/LogJsonOutputUtils.cs @@ -1,162 +1,161 @@ -namespace Serilog.Exceptions.Test.Destructurers +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.IO; +using FluentAssertions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serilog.Core; +using Serilog.Events; +using Serilog.Exceptions.Core; +using Serilog.Formatting; +using Serilog.Formatting.Json; +using Xunit; + +public static class LogJsonOutputUtils { - using System; - using System.IO; - using FluentAssertions; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - using Serilog.Core; - using Serilog.Events; - using Serilog.Exceptions.Core; - using Serilog.Formatting; - using Serilog.Formatting.Json; - using Xunit; - - public static class LogJsonOutputUtils + public static JObject LogAndDestructureException( + Exception exception, + IDestructuringOptions? destructuringOptions = null) { - public static JObject LogAndDestructureException( - Exception exception, - IDestructuringOptions? destructuringOptions = null) - { - // Arrange - var jsonWriter = new StringWriter(); - destructuringOptions ??= new DestructuringOptionsBuilder().WithDefaultDestructurers(); - ILogger logger = new LoggerConfiguration() - .Enrich.WithExceptionDetails(destructuringOptions) - .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) - .CreateLogger(); - - // Act - logger.Error(exception, "EXCEPTION MESSAGE"); - - // Assert - var writtenJson = jsonWriter.ToString(); - var jsonObj = JsonConvert.DeserializeObject(writtenJson); - var rootObject = Assert.IsType(jsonObj); - return rootObject; - } + // Arrange + var jsonWriter = new StringWriter(); + destructuringOptions ??= new DestructuringOptionsBuilder().WithDefaultDestructurers(); + ILogger logger = new LoggerConfiguration() + .Enrich.WithExceptionDetails(destructuringOptions) + .WriteTo.Sink(new TestTextWriterSink(jsonWriter, new JsonFormatter())) + .CreateLogger(); + + // Act + logger.Error(exception, "EXCEPTION MESSAGE"); + + // Assert + var writtenJson = jsonWriter.ToString(); + var jsonObj = JsonConvert.DeserializeObject(writtenJson); + var rootObject = Assert.IsType(jsonObj); + return rootObject; + } - public static void Test_LoggedExceptionContainsProperty(Exception exception, string propertyKey, string? propertyValue, IDestructuringOptions? destructuringOptions = null) - { - var rootObject = LogAndDestructureException(exception, destructuringOptions); - Assert_JObjectContainsPropertiesExceptionDetailsWithProperty(rootObject, propertyKey, propertyValue); - } + public static void Test_LoggedExceptionContainsProperty(Exception exception, string propertyKey, string? propertyValue, IDestructuringOptions? destructuringOptions = null) + { + var rootObject = LogAndDestructureException(exception, destructuringOptions); + Assert_JObjectContainsPropertiesExceptionDetailsWithProperty(rootObject, propertyKey, propertyValue); + } - public static void Test_LoggedExceptionDoesNotContainProperty(Exception exception, string propertyKey, IDestructuringOptions? destructuringOptions = null) - { - var rootObject = LogAndDestructureException(exception, destructuringOptions); - Assert_JObjectContainsPropertiesExceptionDetailsWithoutProperty(rootObject, propertyKey); - } + public static void Test_LoggedExceptionDoesNotContainProperty(Exception exception, string propertyKey, IDestructuringOptions? destructuringOptions = null) + { + var rootObject = LogAndDestructureException(exception, destructuringOptions); + Assert_JObjectContainsPropertiesExceptionDetailsWithoutProperty(rootObject, propertyKey); + } - public static JArray ExtractInnerExceptionsProperty(JObject jObject) - { - var exceptionDetailValue = ExtractExceptionDetails(jObject); + public static JArray ExtractInnerExceptionsProperty(JObject jObject) + { + var exceptionDetailValue = ExtractExceptionDetails(jObject); - var innerExceptionsProperty = Assert.Single(exceptionDetailValue.Properties(), x => x.Name == "InnerExceptions"); - var innerExceptionsValue = Assert.IsType(innerExceptionsProperty.Value); + var innerExceptionsProperty = Assert.Single(exceptionDetailValue.Properties(), x => x.Name == "InnerExceptions"); + var innerExceptionsValue = Assert.IsType(innerExceptionsProperty.Value); - return innerExceptionsValue; - } + return innerExceptionsValue; + } - public static JObject ExtractExceptionDetails(JObject rootObject, string rootName = "ExceptionDetail") - { - var propertiesObject = ExtractPropertiesObject(rootObject); + public static JObject ExtractExceptionDetails(JObject rootObject, string rootName = "ExceptionDetail") + { + var propertiesObject = ExtractPropertiesObject(rootObject); - var exceptionDetailProperty = Assert.Single(propertiesObject.Properties(), x => x.Name == rootName); - var exceptionDetailValue = Assert.IsType(exceptionDetailProperty.Value); + var exceptionDetailProperty = Assert.Single(propertiesObject.Properties(), x => x.Name == rootName); + var exceptionDetailValue = Assert.IsType(exceptionDetailProperty.Value); - return exceptionDetailValue; - } + return exceptionDetailValue; + } - public static JObject ExtractPropertiesObject(JObject rootObject) - { + public static JObject ExtractPropertiesObject(JObject rootObject) + { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(rootObject); + ArgumentNullException.ThrowIfNull(rootObject); #else - if (rootObject is null) - { - throw new ArgumentNullException(nameof(rootObject)); - } + if (rootObject is null) + { + throw new ArgumentNullException(nameof(rootObject)); + } #endif - var propertiesProperty = Assert.Single(rootObject.Properties(), x => x.Name == "Properties"); - var propertiesObject = Assert.IsType(propertiesProperty.Value); - return propertiesObject; - } + var propertiesProperty = Assert.Single(rootObject.Properties(), x => x.Name == "Properties"); + var propertiesObject = Assert.IsType(propertiesProperty.Value); + return propertiesObject; + } - public static void Assert_ContainsPropertyWithValue( - JObject jObject, - string propertyKey, - string? propertyValue) + public static void Assert_ContainsPropertyWithValue( + JObject jObject, + string propertyKey, + string? propertyValue) + { + var paramNameProperty = ExtractProperty(jObject, propertyKey); + var paramName = Assert.IsType(paramNameProperty.Value); + if (paramName.Value is not null) { - var paramNameProperty = ExtractProperty(jObject, propertyKey); - var paramName = Assert.IsType(paramNameProperty.Value); - if (paramName.Value is not null) - { - var paramNameString = paramName.Value.Should() - .BeOfType($"{propertyKey} value was expected to a string").Which; - propertyValue.Should().Be(paramNameString, $"{propertyKey} value should match expected value"); - } + var paramNameString = paramName.Value.Should() + .BeOfType($"{propertyKey} value was expected to a string").Which; + propertyValue.Should().Be(paramNameString, $"{propertyKey} value should match expected value"); } + } - public static void Assert_DoesNotContainProperty( - JObject jObject, - string propertyKey) + public static void Assert_DoesNotContainProperty( + JObject jObject, + string propertyKey) + { + if (jObject is null) { - if (jObject is null) - { - throw new ArgumentNullException(nameof(jObject)); - } - - jObject.Properties().Should() - .NotContain(x => x.Name == propertyKey, $"property with name {propertyKey} was not expected"); + throw new ArgumentNullException(nameof(jObject)); } - public static JProperty ExtractProperty(JObject jObject, string propertyKey) - { - if (jObject is null) - { - throw new ArgumentNullException(nameof(jObject)); - } - - var paramNameProperty = jObject.Properties().Should() - .ContainSingle(x => x.Name == propertyKey, $"property with name {propertyKey} was expected").Which; - return paramNameProperty; - } + jObject.Properties().Should() + .NotContain(x => x.Name == propertyKey, $"property with name {propertyKey} was not expected"); + } - public static void Assert_JObjectContainsPropertiesExceptionDetailsWithProperty( - JObject jObject, - string propertyKey, - string? propertyValue) + public static JProperty ExtractProperty(JObject jObject, string propertyKey) + { + if (jObject is null) { - var exceptionDetailValue = ExtractExceptionDetails(jObject); - Assert_ContainsPropertyWithValue(exceptionDetailValue, propertyKey, propertyValue); + throw new ArgumentNullException(nameof(jObject)); } - public static void Assert_JObjectContainsPropertiesExceptionDetailsWithoutProperty( - JObject jObject, - string propertyKey) + var paramNameProperty = jObject.Properties().Should() + .ContainSingle(x => x.Name == propertyKey, $"property with name {propertyKey} was expected").Which; + return paramNameProperty; + } + + public static void Assert_JObjectContainsPropertiesExceptionDetailsWithProperty( + JObject jObject, + string propertyKey, + string? propertyValue) + { + var exceptionDetailValue = ExtractExceptionDetails(jObject); + Assert_ContainsPropertyWithValue(exceptionDetailValue, propertyKey, propertyValue); + } + + public static void Assert_JObjectContainsPropertiesExceptionDetailsWithoutProperty( + JObject jObject, + string propertyKey) + { + var exceptionDetailValue = ExtractExceptionDetails(jObject); + Assert_DoesNotContainProperty(exceptionDetailValue, propertyKey); + } + + internal class TestTextWriterSink : ILogEventSink + { + private readonly TextWriter textWriter; + private readonly ITextFormatter textFormatter; + + public TestTextWriterSink(TextWriter textWriter, ITextFormatter textFormatter) { - var exceptionDetailValue = ExtractExceptionDetails(jObject); - Assert_DoesNotContainProperty(exceptionDetailValue, propertyKey); + this.textWriter = textWriter; + this.textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter)); } - internal class TestTextWriterSink : ILogEventSink + public void Emit(LogEvent logEvent) { - private readonly TextWriter textWriter; - private readonly ITextFormatter textFormatter; - - public TestTextWriterSink(TextWriter textWriter, ITextFormatter textFormatter) - { - this.textWriter = textWriter; - this.textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter)); - } - - public void Emit(LogEvent logEvent) - { - this.textFormatter.Format(logEvent, this.textWriter); - this.textWriter.Flush(); - } + this.textFormatter.Format(logEvent, this.textWriter); + this.textWriter.Flush(); } } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/OperationCanceledExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/OperationCanceledExceptionDestructurerTest.cs index c66cf33f..8f7344f0 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/OperationCanceledExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/OperationCanceledExceptionDestructurerTest.cs @@ -1,38 +1,37 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System; - using System.Threading; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; - public sealed class OperationCanceledExceptionDestructurerTest : IDisposable - { - private readonly CancellationTokenSource cancellationTokenSource; +using System; +using System.Threading; +using Xunit; +using static LogJsonOutputUtils; - public OperationCanceledExceptionDestructurerTest() => - this.cancellationTokenSource = new CancellationTokenSource(); +public sealed class OperationCanceledExceptionDestructurerTest : IDisposable +{ + private readonly CancellationTokenSource cancellationTokenSource; - [Fact] - public void OperationCanceledException_CancellationTokenIsAttachedAsProperty() - { - this.cancellationTokenSource.Cancel(); + public OperationCanceledExceptionDestructurerTest() => + this.cancellationTokenSource = new CancellationTokenSource(); - var oce = new OperationCanceledException(this.cancellationTokenSource.Token); + [Fact] + public void OperationCanceledException_CancellationTokenIsAttachedAsProperty() + { + this.cancellationTokenSource.Cancel(); - Test_LoggedExceptionContainsProperty(oce, "CancellationToken", "CancellationRequested: true"); - } + var oce = new OperationCanceledException(this.cancellationTokenSource.Token); - [Fact] - public void OperationCanceledException_DisposedCancellationisAttachedAsProperty() - { - var token = this.cancellationTokenSource.Token; - this.cancellationTokenSource.Dispose(); + Test_LoggedExceptionContainsProperty(oce, "CancellationToken", "CancellationRequested: true"); + } - var oce = new OperationCanceledException(token); + [Fact] + public void OperationCanceledException_DisposedCancellationisAttachedAsProperty() + { + var token = this.cancellationTokenSource.Token; + this.cancellationTokenSource.Dispose(); - Test_LoggedExceptionContainsProperty(oce, "CancellationToken", "CancellationRequested: false"); - } + var oce = new OperationCanceledException(token); - public void Dispose() => this.cancellationTokenSource?.Dispose(); + Test_LoggedExceptionContainsProperty(oce, "CancellationToken", "CancellationRequested: false"); } + + public void Dispose() => this.cancellationTokenSource?.Dispose(); } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/ReflectionBasedDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/ReflectionBasedDestructurerTest.cs index a5b27688..920cdb5b 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/ReflectionBasedDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/ReflectionBasedDestructurerTest.cs @@ -1,643 +1,642 @@ -namespace Serilog.Exceptions.Test.Destructurers +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Serilog.Exceptions.Core; +using Serilog.Exceptions.Destructurers; +using Xunit; +using static LogJsonOutputUtils; + +public class ReflectionBasedDestructurerTest { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using FluentAssertions; - using Serilog.Exceptions.Core; - using Serilog.Exceptions.Destructurers; - using Xunit; - using static LogJsonOutputUtils; - - public class ReflectionBasedDestructurerTest - { - [Fact] - public void DestructureComplexException_EachTypeOfPropertyIsDestructuredAsExpected() - { - // Arrange - var exception = ThrowAndCatchException(() => throw new TestException()); - var propertiesBag = new ExceptionPropertiesBag(exception); - - // Act - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - - // Assert - var properties = propertiesBag.GetResultDictionary(); - Assert.Equal("PublicValue", properties[nameof(TestException.PublicProperty)]); - Assert.Equal("threw System.Exception: Exception of type 'System.Exception' was thrown.", properties[nameof(TestException.ExceptionProperty)]); - Assert.DoesNotContain(properties, x => string.Equals(x.Key, "InternalProperty", StringComparison.Ordinal)); - Assert.DoesNotContain(properties, x => string.Equals(x.Key, "ProtectedProperty", StringComparison.Ordinal)); - Assert.DoesNotContain(properties, x => string.Equals(x.Key, "PrivateProperty", StringComparison.Ordinal)); - Assert.Equal("MessageValue", properties[nameof(TestException.Message)]); + [Fact] + public void DestructureComplexException_EachTypeOfPropertyIsDestructuredAsExpected() + { + // Arrange + var exception = ThrowAndCatchException(() => throw new TestException()); + var propertiesBag = new ExceptionPropertiesBag(exception); + + // Act + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + + // Assert + var properties = propertiesBag.GetResultDictionary(); + Assert.Equal("PublicValue", properties[nameof(TestException.PublicProperty)]); + Assert.Equal("threw System.Exception: Exception of type 'System.Exception' was thrown.", properties[nameof(TestException.ExceptionProperty)]); + Assert.DoesNotContain(properties, x => string.Equals(x.Key, "InternalProperty", StringComparison.Ordinal)); + Assert.DoesNotContain(properties, x => string.Equals(x.Key, "ProtectedProperty", StringComparison.Ordinal)); + Assert.DoesNotContain(properties, x => string.Equals(x.Key, "PrivateProperty", StringComparison.Ordinal)); + Assert.Equal("MessageValue", properties[nameof(TestException.Message)]); #if NET461 || NET472 Assert.StartsWith("Void DestructureComplexException_EachTypeOfPropertyIsDestructuredAsExpected(", properties[nameof(TestException.TargetSite)].ToString()); #endif - Assert.NotEmpty(properties[nameof(TestException.StackTrace)]?.ToString()); - Assert.Equal("Serilog.Exceptions.Test", properties[nameof(TestException.Source)]); - Assert.Equal(-2146233088, properties[nameof(TestException.HResult)]); - Assert.Contains(typeof(TestException).FullName, properties["Type"]?.ToString(), StringComparison.Ordinal); - } + Assert.NotEmpty(properties[nameof(TestException.StackTrace)]?.ToString()); + Assert.Equal("Serilog.Exceptions.Test", properties[nameof(TestException.Source)]); + Assert.Equal(-2146233088, properties[nameof(TestException.HResult)]); + Assert.Contains(typeof(TestException).FullName, properties["Type"]?.ToString(), StringComparison.Ordinal); + } - [Fact] - public void CanDestructureUriProperty() - { - const string uriValue = "http://localhost/property"; - var exception = new UriException("test", new Uri(uriValue)); + [Fact] + public void CanDestructureUriProperty() + { + const string uriValue = "http://localhost/property"; + var exception = new UriException("test", new Uri(uriValue)); - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - var properties = propertiesBag.GetResultDictionary(); - var uriPropertyValue = properties[nameof(UriException.Uri)]; - Assert.IsType(uriPropertyValue); - Assert.Equal(uriValue, uriPropertyValue); - } + var properties = propertiesBag.GetResultDictionary(); + var uriPropertyValue = properties[nameof(UriException.Uri)]; + Assert.IsType(uriPropertyValue); + Assert.Equal(uriValue, uriPropertyValue); + } - [Fact] - public void CanDestructureUriDataItem() + [Fact] + public void CanDestructureUriDataItem() + { + const string uriValue = "http://localhost/data-item"; + var exception = new Exception("test") { - const string uriValue = "http://localhost/data-item"; - var exception = new Exception("test") - { - Data = + Data = { { "UriDataItem", new Uri(uriValue) }, }, - }; - - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + }; - var properties = propertiesBag.GetResultDictionary(); - var data = (IDictionary?)properties[nameof(Exception.Data)]; - var uriDataValue = data?["UriDataItem"]; - Assert.IsType(uriDataValue); - Assert.Equal(uriValue, uriDataValue); - } + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - [Fact] - public async Task CanDestructureTaskAsync() - { - using var cancellationTokenSource = new CancellationTokenSource(0); + var properties = propertiesBag.GetResultDictionary(); + var data = (IDictionary?)properties[nameof(Exception.Data)]; + var uriDataValue = data?["UriDataItem"]; + Assert.IsType(uriDataValue); + Assert.Equal(uriValue, uriDataValue); + } - TaskCanceledException exception; - try - { - await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); - Assert.True(false, "TaskCanceledException was not thrown."); - return; - } - catch (TaskCanceledException taskCancelledException) - { - exception = taskCancelledException; - } - - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - - var properties = propertiesBag.GetResultDictionary(); - var destructuredTaskObject = (IDictionary?)properties[nameof(TaskCanceledException.Task)]; - var destructuredTaskProperties = Assert.IsAssignableFrom>(destructuredTaskObject); - destructuredTaskProperties.Should().ContainKey(nameof(Task.Id)); - destructuredTaskProperties.Should().ContainKey(nameof(Task.Status)) - .WhoseValue.Should().BeOfType() - .Which.Should().Be(nameof(TaskStatus.Canceled)); - destructuredTaskProperties.Should().ContainKey(nameof(Task.CreationOptions)) - .WhoseValue.Should().BeOfType() - .Which.Should().Contain(nameof(TaskCreationOptions.None)); - } + [Fact] + public async Task CanDestructureTaskAsync() + { + using var cancellationTokenSource = new CancellationTokenSource(0); - [Fact] - public void CanDestructureFaultedTask() + TaskCanceledException exception; + try { - var taskException = new Exception("INNER EXCEPTION MESSAGE"); - var task = Task.FromException(taskException); - var exception = new TaskException("TASK EXCEPTION MESSAGE", task); - - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, InnerDestructurer(CreateReflectionBasedDestructurer())); - - var properties = propertiesBag.GetResultDictionary(); - var destructuredTaskObject = (IDictionary?)properties[nameof(TaskCanceledException.Task)]; - var destructuredTaskProperties = Assert.IsAssignableFrom>(destructuredTaskObject); - destructuredTaskProperties.Should().ContainKey(nameof(Task.Id)); - destructuredTaskProperties.Should().ContainKey(nameof(Task.Status)) - .WhoseValue.Should().BeOfType() - .Which.Should().Be(nameof(TaskStatus.Faulted)); - destructuredTaskProperties.Should().ContainKey(nameof(Task.CreationOptions)) - .WhoseValue.Should().BeOfType() - .Which.Should().Be(nameof(TaskCreationOptions.None)); - var taskFirstLevelExceptionDictionary = destructuredTaskProperties.Should().ContainKey(nameof(Task.Exception)) - .WhoseValue.Should().BeAssignableTo>() - .Which; - taskFirstLevelExceptionDictionary.Should().ContainKey("Message") - .WhoseValue.Should().BeOfType() - .Which.Should().Contain("One or more errors occurred.", "task's first level exception is aggregate exception"); - taskFirstLevelExceptionDictionary.Should().ContainKey("InnerExceptions") - .WhoseValue.Should().BeAssignableTo>() - .Which.Should().ContainSingle() - .Which.Should().BeAssignableTo>() - .Which.Should().ContainKey("Message") - .WhoseValue.Should().BeOfType() - .Which.Should().Be("INNER EXCEPTION MESSAGE"); + await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); + Assert.True(false, "TaskCanceledException was not thrown."); + return; } - - [Fact] - public void CanDestructureStructDataItem() + catch (TaskCanceledException taskCancelledException) { - // Arrange - var exception = new Exception("test"); - exception.Data["data"] = new TestStruct() - { - ValueType = 10, - ReferenceType = "ABC", - }; - var propertiesBag = new ExceptionPropertiesBag(exception); - - // Act - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - - // Assert - var properties = propertiesBag.GetResultDictionary(); - var data = (IDictionary?)properties[nameof(Exception.Data)]; - var testStructDataValue = data?["data"]; - Assert.IsAssignableFrom(testStructDataValue); + exception = taskCancelledException; } - [Fact] - public void CanDestructureClassDataItem() - { - // Arrange - var exception = new Exception("test"); - exception.Data["data"] = new TestClass() - { - ValueType = 10, - ReferenceType = "ABC", - }; - var propertiesBag = new ExceptionPropertiesBag(exception); - - // Act - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - - // Assert - var properties = propertiesBag.GetResultDictionary(); - var data = (IDictionary?)properties[nameof(Exception.Data)]; - var testStructDataValue = data?["data"]; - var destructuredStructDictionary = Assert.IsAssignableFrom>(testStructDataValue); - Assert.Equal(10, destructuredStructDictionary[nameof(TestClass.ValueType)]); - Assert.Equal("ABC", destructuredStructDictionary[nameof(TestClass.ReferenceType)]); - } + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + + var properties = propertiesBag.GetResultDictionary(); + var destructuredTaskObject = (IDictionary?)properties[nameof(TaskCanceledException.Task)]; + var destructuredTaskProperties = Assert.IsAssignableFrom>(destructuredTaskObject); + destructuredTaskProperties.Should().ContainKey(nameof(Task.Id)); + destructuredTaskProperties.Should().ContainKey(nameof(Task.Status)) + .WhoseValue.Should().BeOfType() + .Which.Should().Be(nameof(TaskStatus.Canceled)); + destructuredTaskProperties.Should().ContainKey(nameof(Task.CreationOptions)) + .WhoseValue.Should().BeOfType() + .Which.Should().Contain(nameof(TaskCreationOptions.None)); + } + + [Fact] + public void CanDestructureFaultedTask() + { + var taskException = new Exception("INNER EXCEPTION MESSAGE"); + var task = Task.FromException(taskException); + var exception = new TaskException("TASK EXCEPTION MESSAGE", task); + + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, InnerDestructurer(CreateReflectionBasedDestructurer())); + + var properties = propertiesBag.GetResultDictionary(); + var destructuredTaskObject = (IDictionary?)properties[nameof(TaskCanceledException.Task)]; + var destructuredTaskProperties = Assert.IsAssignableFrom>(destructuredTaskObject); + destructuredTaskProperties.Should().ContainKey(nameof(Task.Id)); + destructuredTaskProperties.Should().ContainKey(nameof(Task.Status)) + .WhoseValue.Should().BeOfType() + .Which.Should().Be(nameof(TaskStatus.Faulted)); + destructuredTaskProperties.Should().ContainKey(nameof(Task.CreationOptions)) + .WhoseValue.Should().BeOfType() + .Which.Should().Be(nameof(TaskCreationOptions.None)); + var taskFirstLevelExceptionDictionary = destructuredTaskProperties.Should().ContainKey(nameof(Task.Exception)) + .WhoseValue.Should().BeAssignableTo>() + .Which; + taskFirstLevelExceptionDictionary.Should().ContainKey("Message") + .WhoseValue.Should().BeOfType() + .Which.Should().Contain("One or more errors occurred.", "task's first level exception is aggregate exception"); + taskFirstLevelExceptionDictionary.Should().ContainKey("InnerExceptions") + .WhoseValue.Should().BeAssignableTo>() + .Which.Should().ContainSingle() + .Which.Should().BeAssignableTo>() + .Which.Should().ContainKey("Message") + .WhoseValue.Should().BeOfType() + .Which.Should().Be("INNER EXCEPTION MESSAGE"); + } + + [Fact] + public void CanDestructureStructDataItem() + { + // Arrange + var exception = new Exception("test"); + exception.Data["data"] = new TestStruct() + { + ValueType = 10, + ReferenceType = "ABC", + }; + var propertiesBag = new ExceptionPropertiesBag(exception); + + // Act + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + + // Assert + var properties = propertiesBag.GetResultDictionary(); + var data = (IDictionary?)properties[nameof(Exception.Data)]; + var testStructDataValue = data?["data"]; + Assert.IsAssignableFrom(testStructDataValue); + } + + [Fact] + public void CanDestructureClassDataItem() + { + // Arrange + var exception = new Exception("test"); + exception.Data["data"] = new TestClass() + { + ValueType = 10, + ReferenceType = "ABC", + }; + var propertiesBag = new ExceptionPropertiesBag(exception); + + // Act + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + + // Assert + var properties = propertiesBag.GetResultDictionary(); + var data = (IDictionary?)properties[nameof(Exception.Data)]; + var testStructDataValue = data?["data"]; + var destructuredStructDictionary = Assert.IsAssignableFrom>(testStructDataValue); + Assert.Equal(10, destructuredStructDictionary[nameof(TestClass.ValueType)]); + Assert.Equal("ABC", destructuredStructDictionary[nameof(TestClass.ReferenceType)]); + } - [Fact] - public void DestructuringDepthIsLimitedByConfiguredDepth() + [Fact] + public void DestructuringDepthIsLimitedByConfiguredDepth() + { + // Arrange + var exception = new RecursiveException() { - // Arrange - var exception = new RecursiveException() + Node = new RecursiveNode() { - Node = new RecursiveNode() + Name = "PARENT", + Child = new RecursiveNode() { - Name = "PARENT", + Name = "CHILD 1", Child = new RecursiveNode() { - Name = "CHILD 1", - Child = new RecursiveNode() - { - Name = "CHILD 2", - }, + Name = "CHILD 2", }, }, - }; - var destructurer = new ReflectionBasedDestructurer(1); - - // Act - var propertiesBag = new ExceptionPropertiesBag(exception); - destructurer.Destructure(exception, propertiesBag, EmptyDestructurer()); - - // Assert - // Parent is depth 1 - // First child is depth 2 - var properties = propertiesBag.GetResultDictionary(); - var parent = (IDictionary?)properties[nameof(RecursiveException.Node)]; - Assert.Equal("PARENT", parent?[nameof(RecursiveNode.Name)]); - Assert.IsType(parent?[nameof(RecursiveNode.Child)]); - } + }, + }; + var destructurer = new ReflectionBasedDestructurer(1); + + // Act + var propertiesBag = new ExceptionPropertiesBag(exception); + destructurer.Destructure(exception, propertiesBag, EmptyDestructurer()); + + // Assert + // Parent is depth 1 + // First child is depth 2 + var properties = propertiesBag.GetResultDictionary(); + var parent = (IDictionary?)properties[nameof(RecursiveException.Node)]; + Assert.Equal("PARENT", parent?[nameof(RecursiveNode.Name)]); + Assert.IsType(parent?[nameof(RecursiveNode.Child)]); + } - [Fact] - public void ExceptionWithTypeProperty_StillContainsType_JustWithDollarAsPrefixInLabel() - { - var exceptionWithTypeProperty = new TypePropertyException() { Type = 13 }; - Test_LoggedExceptionContainsProperty(exceptionWithTypeProperty, "$Type", $"Serilog.Exceptions.Test.Destructurers.{nameof(ReflectionBasedDestructurerTest)}+{nameof(TypePropertyException)}"); - } + [Fact] + public void ExceptionWithTypeProperty_StillContainsType_JustWithDollarAsPrefixInLabel() + { + var exceptionWithTypeProperty = new TypePropertyException() { Type = 13 }; + Test_LoggedExceptionContainsProperty(exceptionWithTypeProperty, "$Type", $"Serilog.Exceptions.Test.Destructurers.{nameof(ReflectionBasedDestructurerTest)}+{nameof(TypePropertyException)}"); + } - [Fact] - public void WhenObjectContainsCyclicReferences_ThenNoStackoverflowExceptionIsThrown() - { - // Arrange - var exception = new CyclicException - { - MyObject = new MyObject(), - }; - exception.MyObject.Foo = "bar"; - exception.MyObject.Reference = exception.MyObject; - exception.MyObject.Reference2 = exception.MyObject; - - // Act - var result = new ExceptionPropertiesBag(new Exception()); - var destructurer = new ReflectionBasedDestructurer(10); - destructurer.Destructure(exception, result, EmptyDestructurer()); - - // Assert - var myObject = (Dictionary?)result.GetResultDictionary()["MyObject"]; - - Assert.Equal("bar", myObject?["Foo"]); - var reference1 = (Dictionary?)myObject?["Reference"]; - Assert.Equal(myObject?["$id"], reference1?["$ref"]); - var reference2 = (Dictionary?)myObject?["Reference2"]; - Assert.Equal(myObject?["$id"], reference2?["$ref"]); - Assert.Equal("1", myObject?["$id"]); - } + [Fact] + public void WhenObjectContainsCyclicReferences_ThenNoStackoverflowExceptionIsThrown() + { + // Arrange + var exception = new CyclicException + { + MyObject = new MyObject(), + }; + exception.MyObject.Foo = "bar"; + exception.MyObject.Reference = exception.MyObject; + exception.MyObject.Reference2 = exception.MyObject; + + // Act + var result = new ExceptionPropertiesBag(new Exception()); + var destructurer = new ReflectionBasedDestructurer(10); + destructurer.Destructure(exception, result, EmptyDestructurer()); + + // Assert + var myObject = (Dictionary?)result.GetResultDictionary()["MyObject"]; + + Assert.Equal("bar", myObject?["Foo"]); + var reference1 = (Dictionary?)myObject?["Reference"]; + Assert.Equal(myObject?["$id"], reference1?["$ref"]); + var reference2 = (Dictionary?)myObject?["Reference2"]; + Assert.Equal(myObject?["$id"], reference2?["$ref"]); + Assert.Equal("1", myObject?["$id"]); + } - [Fact] - public void WhenObjectContainsCyclicReferencesInList_ThenRecursiveDestructureIsImmediatelyStopped() + [Fact] + public void WhenObjectContainsCyclicReferencesInList_ThenRecursiveDestructureIsImmediatelyStopped() + { + // Arrange + var cyclic = new MyObjectCollection { - // Arrange - var cyclic = new MyObjectCollection - { - Foo = "Cyclic", - }; - cyclic.Reference = cyclic; - var exception = new Cyclic2Exception - { - MyObjectCollection = new MyObjectCollection(), - }; - exception.MyObjectCollection.Foo = "bar"; - exception.MyObjectCollection.Reference = cyclic; + Foo = "Cyclic", + }; + cyclic.Reference = cyclic; + var exception = new Cyclic2Exception + { + MyObjectCollection = new MyObjectCollection(), + }; + exception.MyObjectCollection.Foo = "bar"; + exception.MyObjectCollection.Reference = cyclic; - // Act - var result = new ExceptionPropertiesBag(new Exception()); - var destructurer = new ReflectionBasedDestructurer(10); - destructurer.Destructure(exception, result, EmptyDestructurer()); + // Act + var result = new ExceptionPropertiesBag(new Exception()); + var destructurer = new ReflectionBasedDestructurer(10); + destructurer.Destructure(exception, result, EmptyDestructurer()); - // Assert - var myObject = (List?)result.GetResultDictionary()[nameof(Cyclic2Exception.MyObjectCollection)]; + // Assert + var myObject = (List?)result.GetResultDictionary()[nameof(Cyclic2Exception.MyObjectCollection)]; - // exception.MyObjectCollection[0] is still list - var firstLevelList = Assert.IsType>(myObject?[0]); + // exception.MyObjectCollection[0] is still list + var firstLevelList = Assert.IsType>(myObject?[0]); - // exception.MyObjectCollection[0][0] we notice that we would again destructure "cyclic" - var secondLevelList = Assert.IsType>(firstLevelList[0]); - Assert.Equal("Cyclic reference", secondLevelList["$ref"]); - } + // exception.MyObjectCollection[0][0] we notice that we would again destructure "cyclic" + var secondLevelList = Assert.IsType>(firstLevelList[0]); + Assert.Equal("Cyclic reference", secondLevelList["$ref"]); + } - [Fact] - public void WhenObjectContainsCyclicReferencesInDict_ThenRecursiveDestructureIsImmediatelyStopped() - { - // Arrange - var cyclic = new MyObjectDict - { - Foo = "Cyclic", - Reference = new Dictionary(), - }; - cyclic.Reference["x"] = cyclic.Reference; - var exception = new CyclicDictException - { - MyObjectDict = cyclic, - }; - - // Act - var result = new ExceptionPropertiesBag(new Exception()); - var destructurer = new ReflectionBasedDestructurer(10); - destructurer.Destructure(exception, result, EmptyDestructurer()); - - // Assert - var myObject = (Dictionary?)result.GetResultDictionary()["MyObjectDict"]; - - // exception.MyObjectDict["Reference"] is still regular dictionary - var firstLevelDict = Assert.IsType>(myObject?["Reference"]); - var id = firstLevelDict["$id"]; - Assert.Equal("1", id); - - // exception.MyObjectDict["Reference"]["x"] we notice that we are destructuring same dictionary - var secondLevelDict = Assert.IsType>(firstLevelDict["x"]); - var refId = Assert.IsType(secondLevelDict["$ref"]); - Assert.Equal(id, refId); - } + [Fact] + public void WhenObjectContainsCyclicReferencesInDict_ThenRecursiveDestructureIsImmediatelyStopped() + { + // Arrange + var cyclic = new MyObjectDict + { + Foo = "Cyclic", + Reference = new Dictionary(), + }; + cyclic.Reference["x"] = cyclic.Reference; + var exception = new CyclicDictException + { + MyObjectDict = cyclic, + }; + + // Act + var result = new ExceptionPropertiesBag(new Exception()); + var destructurer = new ReflectionBasedDestructurer(10); + destructurer.Destructure(exception, result, EmptyDestructurer()); + + // Assert + var myObject = (Dictionary?)result.GetResultDictionary()["MyObjectDict"]; + + // exception.MyObjectDict["Reference"] is still regular dictionary + var firstLevelDict = Assert.IsType>(myObject?["Reference"]); + var id = firstLevelDict["$id"]; + Assert.Equal("1", id); + + // exception.MyObjectDict["Reference"]["x"] we notice that we are destructuring same dictionary + var secondLevelDict = Assert.IsType>(firstLevelDict["x"]); + var refId = Assert.IsType(secondLevelDict["$ref"]); + Assert.Equal(id, refId); + } - [Fact] - public void WhenObjectContainsCyclicReferencesInTask_ThenRecursiveDestructureIsImmediatelyStopped() - { - // Arrange - var exception = new CyclicTaskException(); - var task = Task.FromException(exception); - exception.Task = task; - - // Act - var result = new ExceptionPropertiesBag(exception); - var destructurer = CreateReflectionBasedDestructurer(); - destructurer.Destructure(exception, result, InnerDestructurer(destructurer)); - - // Assert - var resultsDictionary = result.GetResultDictionary(); - var destructuredTask = resultsDictionary[nameof(CyclicTaskException.Task)].Should().BeAssignableTo>().Which; - var destructuredCyclicException = destructuredTask.Should().ContainKey(nameof(Task.Exception)) - .WhoseValue.Should().BeAssignableTo>() - .Which.Should().ContainKey(nameof(AggregateException.InnerExceptions)) - .WhoseValue.Should().BeAssignableTo>() - .Which.Should().ContainSingle() - .Which.Should().BeAssignableTo>().Which; - destructuredCyclicException.Should().ContainKey(nameof(Exception.Message)) - .WhoseValue.Should().BeOfType() - .Which.Should().Contain(nameof(CyclicTaskException)); - destructuredCyclicException.Should().ContainKey(nameof(CyclicTaskException.Task)) - .WhoseValue.Should().BeAssignableTo>() - .Which.Should().ContainKey("$ref", "task was already destructured, so inner task should just contain ref"); - } + [Fact] + public void WhenObjectContainsCyclicReferencesInTask_ThenRecursiveDestructureIsImmediatelyStopped() + { + // Arrange + var exception = new CyclicTaskException(); + var task = Task.FromException(exception); + exception.Task = task; + + // Act + var result = new ExceptionPropertiesBag(exception); + var destructurer = CreateReflectionBasedDestructurer(); + destructurer.Destructure(exception, result, InnerDestructurer(destructurer)); + + // Assert + var resultsDictionary = result.GetResultDictionary(); + var destructuredTask = resultsDictionary[nameof(CyclicTaskException.Task)].Should().BeAssignableTo>().Which; + var destructuredCyclicException = destructuredTask.Should().ContainKey(nameof(Task.Exception)) + .WhoseValue.Should().BeAssignableTo>() + .Which.Should().ContainKey(nameof(AggregateException.InnerExceptions)) + .WhoseValue.Should().BeAssignableTo>() + .Which.Should().ContainSingle() + .Which.Should().BeAssignableTo>().Which; + destructuredCyclicException.Should().ContainKey(nameof(Exception.Message)) + .WhoseValue.Should().BeOfType() + .Which.Should().Contain(nameof(CyclicTaskException)); + destructuredCyclicException.Should().ContainKey(nameof(CyclicTaskException.Task)) + .WhoseValue.Should().BeAssignableTo>() + .Which.Should().ContainKey("$ref", "task was already destructured, so inner task should just contain ref"); + } - [Fact] - public void WhenDestruringArgumentException_ResultShouldBeEquivalentToArgumentExceptionDestructurer() - { + [Fact] + public void WhenDestruringArgumentException_ResultShouldBeEquivalentToArgumentExceptionDestructurer() + { #pragma warning disable CA2208 // Instantiate argument exceptions correctly - var exception = ThrowAndCatchException(() => throw new ArgumentException("MESSAGE", "paramName")); + var exception = ThrowAndCatchException(() => throw new ArgumentException("MESSAGE", "paramName")); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - Test_ResultOfReflectionDestructurerShouldBeEquivalentToCustomOne(exception, new ArgumentExceptionDestructurer()); - } + Test_ResultOfReflectionDestructurerShouldBeEquivalentToCustomOne(exception, new ArgumentExceptionDestructurer()); + } - [Fact] - public void CanDestructureObjectWithHiddenProperty() + [Fact] + public void CanDestructureObjectWithHiddenProperty() + { + var derived = new DerivedClass { - var derived = new DerivedClass - { - HiddenProperty = 123, - }; - var baseClass = (BaseClass)derived; - baseClass.HiddenProperty = 456; - var exception = new HiddenException("test", derived); - - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - - var properties = propertiesBag.GetResultDictionary(); - var info = properties[nameof(HiddenException.Info)] as IDictionary; - Assert.Equal(derived.HiddenProperty, info?[nameof(DerivedClass.HiddenProperty)]); - Assert.Equal(baseClass.HiddenProperty, info?[$"{nameof(BaseClass)}.{nameof(BaseClass.HiddenProperty)}"]); - } + HiddenProperty = 123, + }; + var baseClass = (BaseClass)derived; + baseClass.HiddenProperty = 456; + var exception = new HiddenException("test", derived); - [Fact] - public void CanDestructureObjectWithRedefinedProperty() - { - var exception = new TestExceptionClassWithNewDefinition() { PublicProperty = 20 }; + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + var properties = propertiesBag.GetResultDictionary(); + var info = properties[nameof(HiddenException.Info)] as IDictionary; + Assert.Equal(derived.HiddenProperty, info?[nameof(DerivedClass.HiddenProperty)]); + Assert.Equal(baseClass.HiddenProperty, info?[$"{nameof(BaseClass)}.{nameof(BaseClass.HiddenProperty)}"]); + } - var properties = propertiesBag.GetResultDictionary(); - var info = properties[nameof(TestExceptionClassWithNewDefinition.PublicProperty)]; - } + [Fact] + public void CanDestructureObjectWithRedefinedProperty() + { + var exception = new TestExceptionClassWithNewDefinition() { PublicProperty = 20 }; + + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - [Fact] - public void CanDestructureObjectWithDataWithRedefinedProperty() + var properties = propertiesBag.GetResultDictionary(); + var info = properties[nameof(TestExceptionClassWithNewDefinition.PublicProperty)]; + } + + [Fact] + public void CanDestructureObjectWithDataWithRedefinedProperty() + { + var exception = new RecursiveException { - var exception = new RecursiveException + Node = new RecursiveNodeWithRedefinedProperty { - Node = new RecursiveNodeWithRedefinedProperty - { - Name = 123, - }, - }; + Name = 123, + }, + }; - var propertiesBag = new ExceptionPropertiesBag(exception); - CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); + var propertiesBag = new ExceptionPropertiesBag(exception); + CreateReflectionBasedDestructurer().Destructure(exception, propertiesBag, EmptyDestructurer()); - var properties = propertiesBag.GetResultDictionary(); - var parent = (IDictionary?)properties[nameof(RecursiveException.Node)]; - Assert.Equal(123, parent?[nameof(RecursiveNode.Name)]); - } + var properties = propertiesBag.GetResultDictionary(); + var parent = (IDictionary?)properties[nameof(RecursiveException.Node)]; + Assert.Equal(123, parent?[nameof(RecursiveNode.Name)]); + } - private static void Test_ResultOfReflectionDestructurerShouldBeEquivalentToCustomOne( - Exception exception, - IExceptionDestructurer customDestructurer) - { - // Arrange - var reflectionBasedResult = new ExceptionPropertiesBag(exception); - var customBasedResult = new ExceptionPropertiesBag(exception); - var reflectionBasedDestructurer = CreateReflectionBasedDestructurer(); + private static void Test_ResultOfReflectionDestructurerShouldBeEquivalentToCustomOne( + Exception exception, + IExceptionDestructurer customDestructurer) + { + // Arrange + var reflectionBasedResult = new ExceptionPropertiesBag(exception); + var customBasedResult = new ExceptionPropertiesBag(exception); + var reflectionBasedDestructurer = CreateReflectionBasedDestructurer(); - // Act - reflectionBasedDestructurer.Destructure(exception, reflectionBasedResult, InnerDestructurer(reflectionBasedDestructurer)); - customDestructurer.Destructure(exception, customBasedResult, InnerDestructurer(new ArgumentExceptionDestructurer())); + // Act + reflectionBasedDestructurer.Destructure(exception, reflectionBasedResult, InnerDestructurer(reflectionBasedDestructurer)); + customDestructurer.Destructure(exception, customBasedResult, InnerDestructurer(new ArgumentExceptionDestructurer())); - // Assert - var reflectionBasedDictionary = (Dictionary)reflectionBasedResult.GetResultDictionary(); - var customBasedDictionary = (Dictionary)customBasedResult.GetResultDictionary(); + // Assert + var reflectionBasedDictionary = (Dictionary)reflectionBasedResult.GetResultDictionary(); + var customBasedDictionary = (Dictionary)customBasedResult.GetResultDictionary(); - reflectionBasedDictionary.Should().BeEquivalentTo(customBasedDictionary); - } + reflectionBasedDictionary.Should().BeEquivalentTo(customBasedDictionary); + } - private static Func> EmptyDestructurer() => - (ex) => new ExceptionPropertiesBag(ex).GetResultDictionary(); + private static Func> EmptyDestructurer() => + (ex) => new ExceptionPropertiesBag(ex).GetResultDictionary(); - private static Func?> InnerDestructurer( - IExceptionDestructurer destructurer) => - (ex) => - { - var resultsBag = new ExceptionPropertiesBag(ex); + private static Func?> InnerDestructurer( + IExceptionDestructurer destructurer) => + (ex) => + { + var resultsBag = new ExceptionPropertiesBag(ex); - destructurer.Destructure(ex, resultsBag, InnerDestructurer(destructurer)); + destructurer.Destructure(ex, resultsBag, InnerDestructurer(destructurer)); - return resultsBag.GetResultDictionary(); - }; + return resultsBag.GetResultDictionary(); + }; - private static Exception ThrowAndCatchException(Action throwingAction) + private static Exception ThrowAndCatchException(Action throwingAction) + { + try { - try - { - throwingAction(); - } + throwingAction(); + } #pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) + catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types - { - return ex; - } - - Assert.True(false, $"{nameof(throwingAction)} did not throw"); - return null!; // We should never reach this line. + { + return ex; } - private static ReflectionBasedDestructurer CreateReflectionBasedDestructurer() => - new(10); + Assert.True(false, $"{nameof(throwingAction)} did not throw"); + return null!; // We should never reach this line. + } - public class MyObject - { - public string? Foo { get; set; } + private static ReflectionBasedDestructurer CreateReflectionBasedDestructurer() => + new(10); - public MyObject? Reference { get; set; } + public class MyObject + { + public string? Foo { get; set; } - public MyObject? Reference2 { get; set; } - } + public MyObject? Reference { get; set; } - public class CyclicException : Exception - { - public MyObject? MyObject { get; set; } - } + public MyObject? Reference2 { get; set; } + } - public class MyObjectCollection : IEnumerable - { - public string? Foo { get; set; } + public class CyclicException : Exception + { + public MyObject? MyObject { get; set; } + } - public MyObjectCollection? Reference { get; set; } + public class MyObjectCollection : IEnumerable + { + public string? Foo { get; set; } - public IEnumerator GetEnumerator() => - new List { this.Reference }.GetEnumerator(); + public MyObjectCollection? Reference { get; set; } - IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - } + public IEnumerator GetEnumerator() => + new List { this.Reference }.GetEnumerator(); - public class Cyclic2Exception : Exception - { - public MyObjectCollection? MyObjectCollection { get; set; } - } + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } - public class CyclicDictException : Exception - { - public MyObjectDict? MyObjectDict { get; set; } - } + public class Cyclic2Exception : Exception + { + public MyObjectCollection? MyObjectCollection { get; set; } + } - public class CyclicTaskException : Exception - { - public Task? Task { get; set; } - } + public class CyclicDictException : Exception + { + public MyObjectDict? MyObjectDict { get; set; } + } - public class MyObjectDict - { - public string? Foo { get; set; } + public class CyclicTaskException : Exception + { + public Task? Task { get; set; } + } + + public class MyObjectDict + { + public string? Foo { get; set; } #pragma warning disable CA2227 // Collection properties should be read only - public Dictionary? Reference { get; set; } + public Dictionary? Reference { get; set; } #pragma warning restore CA2227 // Collection properties should be read only - } + } - public class TypePropertyException : Exception - { + public class TypePropertyException : Exception + { #pragma warning disable CA1721 // Property names should not match get methods - public int? Type { get; set; } + public int? Type { get; set; } #pragma warning restore CA1721 // Property names should not match get methods - } + } - public class TestException : Exception + public class TestException : Exception + { + public TestException() + : base("MessageValue") { - public TestException() - : base("MessageValue") - { - StaticProperty = "StaticValue"; - this.PublicProperty = "PublicValue"; - this.InternalProperty = "InternalValue"; - this.ProtectedProperty = "ProtectedValue"; - this.PrivateProperty = "PrivateValue"; - } + StaticProperty = "StaticValue"; + this.PublicProperty = "PublicValue"; + this.InternalProperty = "InternalValue"; + this.ProtectedProperty = "ProtectedValue"; + this.PrivateProperty = "PrivateValue"; + } - public static string? StaticProperty { get; set; } + public static string? StaticProperty { get; set; } - public string PublicProperty { get; set; } + public string PublicProperty { get; set; } #pragma warning disable CA1065 // Do not raise exceptions in unexpected locations #pragma warning disable CA1822 // Member does not access instance data and can be marked as static - public string ExceptionProperty => throw new Exception(); + public string ExceptionProperty => throw new Exception(); #pragma warning restore CA1822 // Member does not access instance data and can be marked as static #pragma warning restore CA1065 // Do not raise exceptions in unexpected locations - internal string InternalProperty { get; set; } + internal string InternalProperty { get; set; } - protected string ProtectedProperty { get; set; } + protected string ProtectedProperty { get; set; } #pragma warning disable IDE0052 // Remove unread private members - private string PrivateProperty { get; set; } + private string PrivateProperty { get; set; } #pragma warning restore IDE0052 // Remove unread private members #pragma warning disable CA1822 // Member does not access instance data and can be marked as static - public string this[int i] => "IndexerValue"; + public string this[int i] => "IndexerValue"; #pragma warning restore CA1822 // Member does not access instance data and can be marked as static - } + } - public class UriException : Exception - { - public UriException(string message, Uri uri) - : base(message) => - this.Uri = uri; + public class UriException : Exception + { + public UriException(string message, Uri uri) + : base(message) => + this.Uri = uri; - public Uri Uri { get; } - } + public Uri Uri { get; } + } - public class TaskException : Exception - { - public TaskException(string message, Task task) - : base(message) => - this.Task = task; + public class TaskException : Exception + { + public TaskException(string message, Task task) + : base(message) => + this.Task = task; - public Task Task { get; } - } + public Task Task { get; } + } - public class RecursiveNode - { - public string? Name { get; set; } + public class RecursiveNode + { + public string? Name { get; set; } - public RecursiveNode? Child { get; set; } - } + public RecursiveNode? Child { get; set; } + } - public class RecursiveNodeWithRedefinedProperty : RecursiveNode - { - public new int Name { get; set; } - } + public class RecursiveNodeWithRedefinedProperty : RecursiveNode + { + public new int Name { get; set; } + } - public class RecursiveException : Exception - { - public RecursiveNode? Node { get; set; } - } + public class RecursiveException : Exception + { + public RecursiveNode? Node { get; set; } + } - [Serializable] + [Serializable] #pragma warning disable SA1201 // Elements should appear in the correct order - internal struct TestStruct + internal struct TestStruct #pragma warning restore SA1201 // Elements should appear in the correct order - { - public int ValueType { get; set; } + { + public int ValueType { get; set; } - public string ReferenceType { get; set; } - } + public string ReferenceType { get; set; } + } - [Serializable] - internal class TestClass - { - public int ValueType { get; set; } + [Serializable] + internal class TestClass + { + public int ValueType { get; set; } - public string? ReferenceType { get; set; } - } + public string? ReferenceType { get; set; } + } - internal class TestExceptionClassWithNewDefinition : TestException - { - public new int PublicProperty { get; set; } - } + internal class TestExceptionClassWithNewDefinition : TestException + { + public new int PublicProperty { get; set; } + } - internal class BaseClass - { - public virtual int HiddenProperty { get; set; } - } + internal class BaseClass + { + public virtual int HiddenProperty { get; set; } + } - internal class DerivedClass : BaseClass - { - public new T? HiddenProperty { get; set; } - } + internal class DerivedClass : BaseClass + { + public new T? HiddenProperty { get; set; } + } #pragma warning disable CA1064 // Exceptions should be public - internal class HiddenException : Exception + internal class HiddenException : Exception #pragma warning restore CA1064 // Exceptions should be public - { - public HiddenException(string message, object info) - : base(message) => - this.Info = info; + { + public HiddenException(string message, object info) + : base(message) => + this.Info = info; - public object Info { get; set; } - } + public object Info { get; set; } } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/SocketExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/SocketExceptionDestructurerTest.cs index 37d9ad4c..fa20990f 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/SocketExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/SocketExceptionDestructurerTest.cs @@ -1,30 +1,29 @@ -namespace Serilog.Exceptions.Test.Destructurers -{ - using System.Net.Sockets; - using Xunit; - using static LogJsonOutputUtils; +namespace Serilog.Exceptions.Test.Destructurers; + +using System.Net.Sockets; +using Xunit; +using static LogJsonOutputUtils; - public class SocketExceptionDestructurerTest +public class SocketExceptionDestructurerTest +{ + [Fact] + public void SocketException_SocketErrorCodeIsAttachedAsProperty() { - [Fact] - public void SocketException_SocketErrorCodeIsAttachedAsProperty() - { - var socketException = new SocketException((int)SocketError.HostUnreachable); - Test_LoggedExceptionContainsProperty(socketException, nameof(SocketException.SocketErrorCode), nameof(SocketError.HostUnreachable)); - } + var socketException = new SocketException((int)SocketError.HostUnreachable); + Test_LoggedExceptionContainsProperty(socketException, nameof(SocketException.SocketErrorCode), nameof(SocketError.HostUnreachable)); + } - [Fact] - public void SocketException_SocketErrorCodeDocumentationIsAttachedAsProperty() - { - var socketException = new SocketException((int)SocketError.ConnectionRefused); - Test_LoggedExceptionContainsProperty(socketException, nameof(SocketException.SocketErrorCode) + "Message", "The remote host is actively refusing a connection."); - } + [Fact] + public void SocketException_SocketErrorCodeDocumentationIsAttachedAsProperty() + { + var socketException = new SocketException((int)SocketError.ConnectionRefused); + Test_LoggedExceptionContainsProperty(socketException, nameof(SocketException.SocketErrorCode) + "Message", "The remote host is actively refusing a connection."); + } - [Fact] - public void SocketException_SocketErrorCodeDocumentationWhenSocketErrorCodeUnknown() - { - var socketException = new SocketException(42); - Test_LoggedExceptionDoesNotContainProperty(socketException, nameof(SocketException.SocketErrorCode) + "Message"); - } + [Fact] + public void SocketException_SocketErrorCodeDocumentationWhenSocketErrorCodeUnknown() + { + var socketException = new SocketException(42); + Test_LoggedExceptionDoesNotContainProperty(socketException, nameof(SocketException.SocketErrorCode) + "Message"); } } diff --git a/Tests/Serilog.Exceptions.Test/Destructurers/TaskCanceledExceptionDestructurerTest.cs b/Tests/Serilog.Exceptions.Test/Destructurers/TaskCanceledExceptionDestructurerTest.cs index b4f04a29..5b613952 100644 --- a/Tests/Serilog.Exceptions.Test/Destructurers/TaskCanceledExceptionDestructurerTest.cs +++ b/Tests/Serilog.Exceptions.Test/Destructurers/TaskCanceledExceptionDestructurerTest.cs @@ -1,99 +1,98 @@ -namespace Serilog.Exceptions.Test.Destructurers +namespace Serilog.Exceptions.Test.Destructurers; + +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; +using static LogJsonOutputUtils; + +public sealed class TaskCanceledExceptionDestructurerTest : IDisposable { - using System; - using System.Threading; - using System.Threading.Tasks; - using FluentAssertions; - using Newtonsoft.Json.Linq; - using Xunit; - using static LogJsonOutputUtils; - - public sealed class TaskCanceledExceptionDestructurerTest : IDisposable + private readonly CancellationTokenSource cancellationTokenSource = new(); + + [Fact] + public void TaskCanceledException_SimplePropertiesAreAttached() + { + // Arrange + this.cancellationTokenSource.Cancel(); + var task = Task.FromCanceled(this.cancellationTokenSource.Token); + + // Act + var ex = new TaskCanceledException(task); + + // Assert + var tce = ex.Should().BeOfType().Which; + var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); + Assert_ContainsPropertyWithValue(exceptionDetails, nameof(TaskCanceledException.CancellationToken), "CancellationRequested: true"); + + var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); + var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; + Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.Status), nameof(TaskStatus.Canceled)); + Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), nameof(TaskCreationOptions.None)); + } + + [Fact] + public void TaskCanceledException_TaskWithSomeCreationOptions_TheyAreDestructured() { - private readonly CancellationTokenSource cancellationTokenSource = new(); - - [Fact] - public void TaskCanceledException_SimplePropertiesAreAttached() - { - // Arrange - this.cancellationTokenSource.Cancel(); - var task = Task.FromCanceled(this.cancellationTokenSource.Token); - - // Act - var ex = new TaskCanceledException(task); - - // Assert - var tce = ex.Should().BeOfType().Which; - var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); - Assert_ContainsPropertyWithValue(exceptionDetails, nameof(TaskCanceledException.CancellationToken), "CancellationRequested: true"); - - var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); - var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; - Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.Status), nameof(TaskStatus.Canceled)); - Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), nameof(TaskCreationOptions.None)); - } - - [Fact] - public void TaskCanceledException_TaskWithSomeCreationOptions_TheyAreDestructured() - { - // Arrange - var task = new Task(() => { }, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); - - // Act - var ex = new TaskCanceledException(task); - - // Assert - var tce = ex.Should().BeOfType().Which; - var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); - var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); - var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; - Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), $"{nameof(TaskCreationOptions.PreferFairness)}, {nameof(TaskCreationOptions.LongRunning)}"); - } - - [Fact] - public void TaskCanceledException_TaskNull() - { - // Arrange - - // Act - var ex = new TaskCanceledException(); - - // Assert - var tce = ex.Should().BeOfType().Which; - var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); - Assert_ContainsPropertyWithValue(exceptionDetails, nameof(TaskCanceledException.Task), "null"); - } - - [Fact] - public void FaultedTaskCanceledException_SimplePropertiesAreAttached() - { - // Arrange - var innerException = new Exception("Inner exception message"); - var task = Task.FromException(innerException); - var ex = new TaskCanceledException(task); - - // Act - var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(ex)); - - // Assert - Assert_ContainsPropertyWithValue(exceptionDetails, "CancellationToken", "CancellationRequested: false"); - - var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); - var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; - Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.Status), nameof(TaskStatus.Faulted)); - Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), nameof(TaskCreationOptions.None)); - var taskException = ExtractProperty(taskPropertyObject, nameof(Task.Exception)); - var taskExceptionObject = taskException.Should().BeOfType() - .Which.Value.Should().BeOfType() - .Which; - - var typeOfTaskException = ExtractProperty(taskExceptionObject, "Type"); - typeOfTaskException.Should().BeOfType() - .Which.Value.Should().BeOfType() - .Which.Value.Should().BeOfType() - .Which.Should().Be("System.AggregateException"); - } - - public void Dispose() => this.cancellationTokenSource?.Dispose(); + // Arrange + var task = new Task(() => { }, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); + + // Act + var ex = new TaskCanceledException(task); + + // Assert + var tce = ex.Should().BeOfType().Which; + var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); + var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); + var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; + Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), $"{nameof(TaskCreationOptions.PreferFairness)}, {nameof(TaskCreationOptions.LongRunning)}"); } + + [Fact] + public void TaskCanceledException_TaskNull() + { + // Arrange + + // Act + var ex = new TaskCanceledException(); + + // Assert + var tce = ex.Should().BeOfType().Which; + var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(tce)); + Assert_ContainsPropertyWithValue(exceptionDetails, nameof(TaskCanceledException.Task), "null"); + } + + [Fact] + public void FaultedTaskCanceledException_SimplePropertiesAreAttached() + { + // Arrange + var innerException = new Exception("Inner exception message"); + var task = Task.FromException(innerException); + var ex = new TaskCanceledException(task); + + // Act + var exceptionDetails = ExtractExceptionDetails(LogAndDestructureException(ex)); + + // Assert + Assert_ContainsPropertyWithValue(exceptionDetails, "CancellationToken", "CancellationRequested: false"); + + var taskProperty = ExtractProperty(exceptionDetails, nameof(TaskCanceledException.Task)); + var taskPropertyObject = taskProperty.Value.Should().BeOfType().Which; + Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.Status), nameof(TaskStatus.Faulted)); + Assert_ContainsPropertyWithValue(taskPropertyObject, nameof(Task.CreationOptions), nameof(TaskCreationOptions.None)); + var taskException = ExtractProperty(taskPropertyObject, nameof(Task.Exception)); + var taskExceptionObject = taskException.Should().BeOfType() + .Which.Value.Should().BeOfType() + .Which; + + var typeOfTaskException = ExtractProperty(taskExceptionObject, "Type"); + typeOfTaskException.Should().BeOfType() + .Which.Value.Should().BeOfType() + .Which.Value.Should().BeOfType() + .Which.Should().Be("System.AggregateException"); + } + + public void Dispose() => this.cancellationTokenSource?.Dispose(); } diff --git a/Tests/Serilog.Exceptions.Test/DictionaryExtensionsTest.cs b/Tests/Serilog.Exceptions.Test/DictionaryExtensionsTest.cs index 0d96f86d..5d547ada 100644 --- a/Tests/Serilog.Exceptions.Test/DictionaryExtensionsTest.cs +++ b/Tests/Serilog.Exceptions.Test/DictionaryExtensionsTest.cs @@ -1,41 +1,40 @@ -namespace Serilog.Exceptions.Test -{ - using System.Collections; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.Linq; - using Xunit; +namespace Serilog.Exceptions.Test; + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using Xunit; - public class DictionaryExtensionsTest +public class DictionaryExtensionsTest +{ + [Fact] + public void ToStringObjectDictionary_StringObjectDictionary_ReturnsStringObjectDictionary() { - [Fact] - public void ToStringObjectDictionary_StringObjectDictionary_ReturnsStringObjectDictionary() - { - var dictionary = (IDictionary)new Dictionary() + var dictionary = (IDictionary)new Dictionary() { { "Key", "Value" }, }; - var actual = dictionary.ToStringObjectDictionary(); + var actual = dictionary.ToStringObjectDictionary(); - Assert.Single(actual); - Assert.Equal("Key", actual.First().Key); - Assert.Equal("Value", actual.First().Value); - } + Assert.Single(actual); + Assert.Equal("Key", actual.First().Key); + Assert.Equal("Value", actual.First().Value); + } - [Fact] - public void ToStringObjectDictionary_ListDictionary_ReturnsStringObjectDictionary() - { - var dictionary = (IDictionary)new ListDictionary() + [Fact] + public void ToStringObjectDictionary_ListDictionary_ReturnsStringObjectDictionary() + { + var dictionary = (IDictionary)new ListDictionary() { { "Key", "Value" }, }; - var actual = dictionary.ToStringObjectDictionary(); + var actual = dictionary.ToStringObjectDictionary(); - Assert.Single(actual); - Assert.Equal("Key", actual.First().Key); - Assert.Equal("Value", actual.First().Value); - } + Assert.Single(actual); + Assert.Equal("Key", actual.First().Key); + Assert.Equal("Value", actual.First().Value); } } diff --git a/Tests/Serilog.Exceptions.Test/Filters/CompositeExceptionPropertyFilterTest.cs b/Tests/Serilog.Exceptions.Test/Filters/CompositeExceptionPropertyFilterTest.cs index 684438ce..f97f5f8b 100644 --- a/Tests/Serilog.Exceptions.Test/Filters/CompositeExceptionPropertyFilterTest.cs +++ b/Tests/Serilog.Exceptions.Test/Filters/CompositeExceptionPropertyFilterTest.cs @@ -1,84 +1,83 @@ -namespace Serilog.Exceptions.Test.Filters -{ - using System; - using Serilog.Exceptions.Filters; - using Xunit; +namespace Serilog.Exceptions.Test.Filters; - public class CompositeExceptionPropertyFilterTest - { - [Fact] - public void CreationOfCompositeFilter_ForNullFilters_Throws() => - Assert.Throws( - () => new CompositeExceptionPropertyFilter(null!)); +using System; +using Serilog.Exceptions.Filters; +using Xunit; - [Fact] - public void CreationOfCompositeFilter_ForEmptyFilters_Throws() => - Assert.Throws( - () => new CompositeExceptionPropertyFilter(Array.Empty())); +public class CompositeExceptionPropertyFilterTest +{ + [Fact] + public void CreationOfCompositeFilter_ForNullFilters_Throws() => + Assert.Throws( + () => new CompositeExceptionPropertyFilter(null!)); - [Fact] - public void CreationOfCompositeFilter_ForOneNullFilter_Throws() - { - // Act - var ex = Assert.Throws( - () => new CompositeExceptionPropertyFilter(new IExceptionPropertyFilter[] { null! })); + [Fact] + public void CreationOfCompositeFilter_ForEmptyFilters_Throws() => + Assert.Throws( + () => new CompositeExceptionPropertyFilter(Array.Empty())); - // Assert - Assert.Contains("index 0", ex.Message, StringComparison.Ordinal); - } + [Fact] + public void CreationOfCompositeFilter_ForOneNullFilter_Throws() + { + // Act + var ex = Assert.Throws( + () => new CompositeExceptionPropertyFilter(new IExceptionPropertyFilter[] { null! })); - [Fact] - public void ShouldFilterTheProperty_WhenFirstFilterFilters_Filters() - { - // Arrange - var filterA = new IgnorePropertyByNameExceptionFilter("A"); - var filterB = new IgnorePropertyByNameExceptionFilter("B"); - var composite = new CompositeExceptionPropertyFilter(filterA, filterB); + // Assert + Assert.Contains("index 0", ex.Message, StringComparison.Ordinal); + } - // Act - var shouldFilter = composite.ShouldPropertyBeFiltered( - new Exception(), - "B", - 1); + [Fact] + public void ShouldFilterTheProperty_WhenFirstFilterFilters_Filters() + { + // Arrange + var filterA = new IgnorePropertyByNameExceptionFilter("A"); + var filterB = new IgnorePropertyByNameExceptionFilter("B"); + var composite = new CompositeExceptionPropertyFilter(filterA, filterB); - // Assert - Assert.True(shouldFilter); - } + // Act + var shouldFilter = composite.ShouldPropertyBeFiltered( + new Exception(), + "B", + 1); - [Fact] - public void ShouldFilterTheProperty_WhenSecondFilterFilters_Filters() - { - // Arrange - var filterA = new IgnorePropertyByNameExceptionFilter("A"); - var filterB = new IgnorePropertyByNameExceptionFilter("B"); - var composite = new CompositeExceptionPropertyFilter(filterA, filterB); + // Assert + Assert.True(shouldFilter); + } - // Act - var shouldFilter = composite.ShouldPropertyBeFiltered( - new Exception(), - "A", - 1); + [Fact] + public void ShouldFilterTheProperty_WhenSecondFilterFilters_Filters() + { + // Arrange + var filterA = new IgnorePropertyByNameExceptionFilter("A"); + var filterB = new IgnorePropertyByNameExceptionFilter("B"); + var composite = new CompositeExceptionPropertyFilter(filterA, filterB); - // Assert - Assert.True(shouldFilter); - } + // Act + var shouldFilter = composite.ShouldPropertyBeFiltered( + new Exception(), + "A", + 1); - [Fact] - public void ShouldFilterTheProperty_WhenNoFilterFilters_NotFilters() - { - // Arrange - var filterA = new IgnorePropertyByNameExceptionFilter("A"); - var filterB = new IgnorePropertyByNameExceptionFilter("B"); - var composite = new CompositeExceptionPropertyFilter(filterA, filterB); + // Assert + Assert.True(shouldFilter); + } + + [Fact] + public void ShouldFilterTheProperty_WhenNoFilterFilters_NotFilters() + { + // Arrange + var filterA = new IgnorePropertyByNameExceptionFilter("A"); + var filterB = new IgnorePropertyByNameExceptionFilter("B"); + var composite = new CompositeExceptionPropertyFilter(filterA, filterB); - // Act - var shouldFilter = composite.ShouldPropertyBeFiltered( - new Exception(), - "C", - 1); + // Act + var shouldFilter = composite.ShouldPropertyBeFiltered( + new Exception(), + "C", + 1); - // Assert - Assert.False(shouldFilter); - } + // Assert + Assert.False(shouldFilter); } } diff --git a/Tests/Serilog.Exceptions.Test/Reflection/ReflectionInfoExtractorTest.cs b/Tests/Serilog.Exceptions.Test/Reflection/ReflectionInfoExtractorTest.cs index 263cd44c..75ada439 100644 --- a/Tests/Serilog.Exceptions.Test/Reflection/ReflectionInfoExtractorTest.cs +++ b/Tests/Serilog.Exceptions.Test/Reflection/ReflectionInfoExtractorTest.cs @@ -1,76 +1,75 @@ -namespace Serilog.Exceptions.Test.Reflection +namespace Serilog.Exceptions.Test.Reflection; + +using System.Linq; +using FluentAssertions; +using Serilog.Exceptions.Reflection; +using Xunit; + +public class ReflectionInfoExtractorTest { - using System.Linq; - using FluentAssertions; - using Serilog.Exceptions.Reflection; - using Xunit; + private readonly ReflectionInfoExtractor reflectionInfoExtractor = new(); - public class ReflectionInfoExtractorTest + [Fact] + public void GivenObjectWithRedefinedProperty_ShouldDiscardBaseClassProperty() { - private readonly ReflectionInfoExtractor reflectionInfoExtractor = new(); - - [Fact] - public void GivenObjectWithRedefinedProperty_ShouldDiscardBaseClassProperty() - { - var testObject = new TestObjectWithRedefinedProperty() { Name = 123 }; + var testObject = new TestObjectWithRedefinedProperty() { Name = 123 }; - var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(typeof(TestObjectWithRedefinedProperty)); + var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(typeof(TestObjectWithRedefinedProperty)); - reflectionInfo.Properties.Should().HaveCount(2); + reflectionInfo.Properties.Should().HaveCount(2); - var namePropertyInfo = reflectionInfo.Properties.Should().ContainSingle(x => x.Name == "Name").Which; - namePropertyInfo.Name.Should().Be(nameof(TestObjectWithRedefinedProperty.Name)); - namePropertyInfo.DeclaringType.Should().Be(typeof(TestObjectWithRedefinedProperty)); - var nameGetter = namePropertyInfo.Getter; - var testObjectName = nameGetter(testObject); - testObjectName.Should().BeOfType().Which.Should().Be(123); + var namePropertyInfo = reflectionInfo.Properties.Should().ContainSingle(x => x.Name == "Name").Which; + namePropertyInfo.Name.Should().Be(nameof(TestObjectWithRedefinedProperty.Name)); + namePropertyInfo.DeclaringType.Should().Be(typeof(TestObjectWithRedefinedProperty)); + var nameGetter = namePropertyInfo.Getter; + var testObjectName = nameGetter(testObject); + testObjectName.Should().BeOfType().Which.Should().Be(123); - var baseClassPropertyInfo = reflectionInfo - .Properties.Should().ContainSingle(x => x.Name == "TestObject.Name").Which; - var baseClassNameGetter = baseClassPropertyInfo.Getter; - var baseClassTestObjectName = baseClassNameGetter(testObject); - baseClassTestObjectName.Should().BeNull(); - } + var baseClassPropertyInfo = reflectionInfo + .Properties.Should().ContainSingle(x => x.Name == "TestObject.Name").Which; + var baseClassNameGetter = baseClassPropertyInfo.Getter; + var baseClassTestObjectName = baseClassNameGetter(testObject); + baseClassTestObjectName.Should().BeNull(); + } - [Fact] - public void GivenObjectWithDoubleRedefinedProperty_ShouldMarkBaseClassPropertiesWithFullName() - { - var testObject = new TestObjectWithDoubleRedefinedProperty() { Name = 456.789 }; + [Fact] + public void GivenObjectWithDoubleRedefinedProperty_ShouldMarkBaseClassPropertiesWithFullName() + { + var testObject = new TestObjectWithDoubleRedefinedProperty() { Name = 456.789 }; - var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(typeof(TestObjectWithDoubleRedefinedProperty)); + var reflectionInfo = this.reflectionInfoExtractor.GetOrCreateReflectionInfo(typeof(TestObjectWithDoubleRedefinedProperty)); - var propertyNames = reflectionInfo.Properties - .Select(x => x.Name) - .ToList(); - propertyNames.Should().BeEquivalentTo( - new[] - { + var propertyNames = reflectionInfo.Properties + .Select(x => x.Name) + .ToList(); + propertyNames.Should().BeEquivalentTo( + new[] + { "Name", "TestObjectWithRedefinedProperty.Name", "TestObject.Name", - }, - x => x.WithoutStrictOrdering()); - var namePropertyInfo = reflectionInfo.Properties.Should().ContainSingle(x => x.Name == "Name").Which; - namePropertyInfo.Name.Should().Be(nameof(TestObjectWithDoubleRedefinedProperty.Name)); - namePropertyInfo.DeclaringType.Should().Be(typeof(TestObjectWithDoubleRedefinedProperty)); - var nameGetter = namePropertyInfo.Getter; - var testObjectName = nameGetter(testObject); - testObjectName.Should().BeOfType().Which.Should().Be(456.789); - } + }, + x => x.WithoutStrictOrdering()); + var namePropertyInfo = reflectionInfo.Properties.Should().ContainSingle(x => x.Name == "Name").Which; + namePropertyInfo.Name.Should().Be(nameof(TestObjectWithDoubleRedefinedProperty.Name)); + namePropertyInfo.DeclaringType.Should().Be(typeof(TestObjectWithDoubleRedefinedProperty)); + var nameGetter = namePropertyInfo.Getter; + var testObjectName = nameGetter(testObject); + testObjectName.Should().BeOfType().Which.Should().Be(456.789); + } - public class TestObject - { - public string? Name { get; set; } - } + public class TestObject + { + public string? Name { get; set; } + } - public class TestObjectWithRedefinedProperty : TestObject - { - public new int Name { get; set; } - } + public class TestObjectWithRedefinedProperty : TestObject + { + public new int Name { get; set; } + } - public class TestObjectWithDoubleRedefinedProperty : TestObjectWithRedefinedProperty - { - public new double Name { get; set; } - } + public class TestObjectWithDoubleRedefinedProperty : TestObjectWithRedefinedProperty + { + public new double Name { get; set; } } } diff --git a/Tools/ExceptionFinderTool/Program.cs b/Tools/ExceptionFinderTool/Program.cs index 05c96199..35c05cc5 100644 --- a/Tools/ExceptionFinderTool/Program.cs +++ b/Tools/ExceptionFinderTool/Program.cs @@ -1,43 +1,42 @@ -namespace ExceptionFinderTool -{ - using System; +namespace ExceptionFinderTool; + +using System; #if NET6_0_OR_GREATER - using System.Globalization; +using System.Globalization; #endif - using System.Linq; - using System.Reflection; - using System.Text; +using System.Linq; +using System.Reflection; +using System.Text; +/// +/// The main program. +/// +public static class Program +{ /// - /// The main program. + /// Defines the entry point of the application. /// - public static class Program + public static void Main() { - /// - /// Defines the entry point of the application. - /// - public static void Main() - { - var stringBuilder = new StringBuilder(); + var stringBuilder = new StringBuilder(); - foreach (var exceptionType in AppDomain - .CurrentDomain - .GetAssemblies() - .SelectMany(x => x.GetTypes()) - .Where(x => x.GetTypeInfo().IsPublic && typeof(Exception).IsAssignableFrom(x)) - .Select(x => x.FullName) - .OrderBy(x => x)) - { + foreach (var exceptionType in AppDomain + .CurrentDomain + .GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(x => x.GetTypeInfo().IsPublic && typeof(Exception).IsAssignableFrom(x)) + .Select(x => x.FullName) + .OrderBy(x => x)) + { #if NET6_0_OR_GREATER - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"typeof({exceptionType}),"); + stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"typeof({exceptionType}),"); #else - stringBuilder.AppendLine($"typeof({exceptionType}),"); + stringBuilder.AppendLine($"typeof({exceptionType}),"); #endif - } - - var types = stringBuilder.ToString(); - Console.WriteLine(types); - Console.Read(); } + + var types = stringBuilder.ToString(); + Console.WriteLine(types); + Console.Read(); } }