Skip to content

Instantly share code, notes, and snippets.

@IanYates
Created December 7, 2015 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IanYates/876eea690e4ca3e72d89 to your computer and use it in GitHub Desktop.
Save IanYates/876eea690e4ca3e72d89 to your computer and use it in GitHub Desktop.
Serilog.Exceptions
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
using System.Linq;
public class AggregateExceptionDestructurer : ExceptionDestructurer
{
public override Type[] TargetTypes
{
get { return new Type[] { typeof(AggregateException) }; }
}
public override void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException)
{
base.Destructure(exception, data, destructureException);
var aggregateException = (AggregateException)exception;
data.Add(
nameof(AggregateException.InnerExceptions),
aggregateException.InnerExceptions.Select(destructureException).ToList());
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
public class ArgumentExceptionDestructurer : ExceptionDestructurer
{
public override Type[] TargetTypes
{
get { return new Type[] { typeof(ArgumentException) }; }
}
public override void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException)
{
base.Destructure(exception, data, destructureException);
var argumentException = (ArgumentException)exception;
data.Add(nameof(ArgumentException.ParamName), argumentException.ParamName);
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class ExceptionDestructurer : IExceptionDestructurer
{
public virtual Type[] TargetTypes
{
get
{
return new Type[]
{
typeof(AccessViolationException),
typeof(AppDomainUnloadedException),
typeof(ApplicationException),
typeof(ArithmeticException),
typeof(ArrayTypeMismatchException),
// typeof(BadImageFormatException), FileName, FusionLog
typeof(CannotUnloadAppDomainException),
typeof(ContextMarshalException),
typeof(DataMisalignedException),
typeof(DivideByZeroException),
typeof(DllNotFoundException),
typeof(DuplicateWaitObjectException),
typeof(EntryPointNotFoundException),
typeof(Exception),
typeof(FieldAccessException),
typeof(FormatException),
typeof(IndexOutOfRangeException),
typeof(InsufficientExecutionStackException),
typeof(InsufficientMemoryException),
typeof(InvalidCastException),
typeof(InvalidOperationException),
typeof(InvalidProgramException),
typeof(InvalidTimeZoneException),
typeof(KeyNotFoundException),
typeof(MemberAccessException),
typeof(MissingFieldException),
typeof(MissingMemberException),
typeof(MissingMethodException),
typeof(MulticastNotSupportedException),
typeof(NotFiniteNumberException),
typeof(NotImplementedException),
typeof(NotSupportedException),
typeof(NullReferenceException),
// typeof(ObjectDisposedException), ObjectName
typeof(OperationCanceledException),
typeof(OutOfMemoryException),
typeof(OverflowException),
typeof(PlatformNotSupportedException),
typeof(RankException),
typeof(StackOverflowException),
typeof(SystemException),
typeof(TimeoutException),
typeof(TimeZoneNotFoundException),
typeof(TypeAccessException),
// typeof(TypeInitializationException), TypeName
// typeof(TypeLoadException), TypeName
typeof(TypeUnloadedException),
typeof(UnauthorizedAccessException),
typeof(UriFormatException)
};
}
}
public virtual void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> innerDestructure)
{
data.Add("Type", exception.GetType().FullName);
if (exception.Data.Count != 0)
{
data.Add(
nameof(Exception.Data),
exception.Data
.Cast<DictionaryEntry>()
.Where(k => k.Key is string)
.ToDictionary(e => (string)e.Key, e => e.Value));
}
if (!string.IsNullOrEmpty(exception.HelpLink))
{
data.Add(nameof(Exception.HelpLink), exception.HelpLink);
}
if (exception.HResult != 0)
{
data.Add(nameof(Exception.HResult), exception.HResult);
}
data.Add(nameof(Exception.Message), exception.Message);
data.Add(nameof(Exception.Source), exception.Source);
data.Add(nameof(Exception.StackTrace), exception.StackTrace);
if (exception.TargetSite != null)
{
data.Add(nameof(Exception.TargetSite), exception.TargetSite.ToString());
}
if (exception.InnerException != null)
{
data.Add(nameof(Exception.InnerException), innerDestructure(exception.InnerException));
}
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
using Serilog.Core;
using Serilog.Events;
/// <summary>
/// Enrich a <see cref="LogEvent"/> with details about an <see cref="LogEvent.Exception"/> if present.
/// https://groups.google.com/forum/#!searchin/getseq/enhance$20exception/getseq/rsAL4u3JpLM/PrszbPbtEb0J
/// </summary>
public sealed class ExceptionEnricher : ILogEventEnricher
{
public static readonly IExceptionDestructurer[] DefaultDestructurers =
{
new ExceptionDestructurer(),
new ArgumentExceptionDestructurer(),
new AggregateExceptionDestructurer(),
new ReflectionTypeLoadExceptionDestructurer(),
new SqlExceptionDestructurer()
};
public static readonly IExceptionDestructurer ReflectionBasedDestructurer = new ReflectionBasedDestructurer();
private readonly Dictionary<Type, IExceptionDestructurer> destructurers;
public ExceptionEnricher()
: this(DefaultDestructurers)
{
}
public ExceptionEnricher(params IExceptionDestructurer[] destructurers)
: this((IEnumerable<IExceptionDestructurer>)destructurers)
{
}
public ExceptionEnricher(IEnumerable<IExceptionDestructurer> destructurers)
{
this.destructurers = new Dictionary<Type, IExceptionDestructurer>();
foreach (var destructurer in destructurers)
{
foreach (var targetType in destructurer.TargetTypes)
{
this.destructurers.Add(targetType, destructurer);
}
}
}
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Exception != null)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
"ExceptionDetail",
this.DestructureException(logEvent.Exception),
true));
}
}
private Dictionary<string, object> DestructureException(Exception exception)
{
var data = new Dictionary<string, object>();
var exceptionType = exception.GetType();
if (this.destructurers.ContainsKey(exceptionType))
{
var destructurer = this.destructurers[exceptionType];
destructurer.Destructure(exception, data, this.DestructureException);
}
else
{
ReflectionBasedDestructurer.Destructure(exception, data, this.DestructureException);
}
return data;
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
public interface IExceptionDestructurer
{
Type[] TargetTypes { get; }
void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException);
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class ReflectionBasedDestructurer : IExceptionDestructurer
{
private const int MaxRecursiveLevel = 10;
public Type[] TargetTypes
{
get { return new Type[] { typeof(Exception) }; }
}
public void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException)
{
foreach (var p in this.DestructureObject(exception, exception.GetType(), 0))
{
data.Add(p.Key, p.Value);
}
}
private object DestructureValue(object value, int level)
{
if (value == null)
{
return null;
}
var valueType = value.GetType();
if (valueType.IsSubclassOf(typeof(MemberInfo)))
{
return value;
}
if (Type.GetTypeCode(valueType) != TypeCode.Object || valueType.IsValueType)
{
return value;
}
if (level >= MaxRecursiveLevel)
{
return value;
}
if (typeof(IDictionary).IsAssignableFrom(valueType))
{
return ((IDictionary)value)
.Cast<DictionaryEntry>()
.Where(e => e.Key is string)
.ToDictionary(e => (string)e.Key, e => this.DestructureValue(e.Value, level + 1));
}
if (typeof(IEnumerable).IsAssignableFrom(valueType))
{
return ((IEnumerable)value)
.Cast<object>()
.Select(o => this.DestructureValue(o, level + 1))
.ToList();
}
return this.DestructureObject(value, valueType, level);
}
private IDictionary<string, object> DestructureObject(object value, Type valueType, int level)
{
var values = valueType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead)
.ToDictionary(p => p.Name, p => this.DestructureValue(p.GetValue(value), level + 1));
values.Add("Type", valueType);
return values;
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class ReflectionTypeLoadExceptionDestructurer : ExceptionDestructurer
{
public override Type[] TargetTypes
{
get { return new Type[] { typeof(ReflectionTypeLoadException) }; }
}
public override void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException)
{
base.Destructure(exception, data, destructureException);
var reflectionTypeLoadException = (ReflectionTypeLoadException)exception;
if (reflectionTypeLoadException.LoaderExceptions != null)
{
data.Add(
nameof(ReflectionTypeLoadException.LoaderExceptions),
reflectionTypeLoadException.LoaderExceptions.Select(destructureException).ToList());
}
}
}
}
namespace Serilog.Exceptions
{
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
public class SqlExceptionDestructurer : ExceptionDestructurer
{
public override Type[] TargetTypes
{
get { return new Type[] { typeof(SqlException) }; }
}
public override void Destructure(
Exception exception,
IDictionary<string, object> data,
Func<Exception, IDictionary<string, object>> destructureException)
{
base.Destructure(exception, data, destructureException);
var sqlException = (SqlException)exception;
data.Add(nameof(SqlException.ClientConnectionId), sqlException.ClientConnectionId);
data.Add(nameof(SqlException.Class), sqlException.Class);
data.Add(nameof(SqlException.LineNumber), sqlException.LineNumber);
data.Add(nameof(SqlException.Number), sqlException.Number);
data.Add(nameof(SqlException.Server), sqlException.Server);
data.Add(nameof(SqlException.State), sqlException.State);
data.Add(nameof(SqlException.Errors), sqlException.Errors.Cast<SqlError>().ToList());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment