// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tools.DotNETCommon;
namespace Tools.DotNETCommon
{
///
/// Methods for adding context information to exceptions
///
public static class ExceptionUtils
{
///
/// Unique key name for adding context to exceptions thrown inside Epic apps
///
const string ContextEntryName = "EpicGames.Context";
///
/// Adds a context message to a list stored on the given exception. Intended to be used in situations where supplying additional context
/// for an exception is valuable, but wrapping it would remove information.
///
/// The exception that was thrown
/// Message to append to the context list
public static void AddContext(Exception Ex, string Message)
{
List Messages = Ex.Data[ContextEntryName] as List;
if (Messages == null)
{
Messages = new List();
Ex.Data[ContextEntryName] = Messages;
}
Messages.Add(Message);
}
///
/// Adds a context message to a list stored on the given exception. Intended to be used in situations where supplying additional context
/// for an exception is valuable, but wrapping it would remove information.
///
/// The exception that was thrown
/// Formatting string for
/// Message to append to the context list
public static void AddContext(Exception Ex, string Format, params object[] Args)
{
AddContext(Ex, String.Format(Format, Args));
}
///
/// Enumerates the context lines from the given exception
///
/// The exception to retrieve context from
/// Sequence of context lines
public static IEnumerable GetContext(Exception Ex)
{
List Messages = Ex.Data[ContextEntryName] as List;
if (Messages != null)
{
foreach (string Message in Messages)
{
yield return Message;
}
}
}
///
/// Formats an exception for display in the log, including additional lines of context that were attached to it.
///
/// The exception to format
/// String containing the exception information. May be multiple lines.
public static string FormatException(Exception Ex)
{
StringBuilder ErrorMessage = new StringBuilder();
if (Ex is AggregateException)
{
Exception InnerException = ((AggregateException)Ex).InnerException;
ErrorMessage.Append(InnerException.ToString());
foreach (string Line in GetContext(InnerException))
{
ErrorMessage.AppendFormat("\n {0}", Line);
}
}
else
{
ErrorMessage.Append(Ex.ToString());
}
foreach (string Line in GetContext(Ex))
{
ErrorMessage.AppendFormat("\n{0}", Line);
}
return ErrorMessage.ToString();
}
///
/// Formats a detailed information about where an exception occurs, including any inner exceptions
///
/// The exception to format
/// String containing the exception information. May be multiple lines.
public static string FormatExceptionDetails(Exception Ex)
{
List ExceptionStack = new List();
for (Exception CurrentEx = Ex; CurrentEx != null; CurrentEx = CurrentEx.InnerException)
{
ExceptionStack.Add(CurrentEx);
}
StringBuilder Message = new StringBuilder();
for (int Idx = ExceptionStack.Count - 1; Idx >= 0; Idx--)
{
Exception CurrentEx = ExceptionStack[Idx];
Message.AppendFormat("{0}{1}: {2}\n{3}", (Idx == ExceptionStack.Count - 1) ? "" : "Wrapped by ", CurrentEx.GetType().Name, CurrentEx.Message, CurrentEx.StackTrace);
if (CurrentEx.Data.Count > 0)
{
foreach (object Key in CurrentEx.Data.Keys)
{
object Value = CurrentEx.Data[Key];
string ValueString;
if(Value is List)
{
ValueString = String.Format("({0})", String.Join(", ", ((List)Value).Select(x => String.Format("\"{0}\"", x))));
}
else
{
ValueString = Value.ToString();
}
Message.AppendFormat(" data: {0} = {1}", Key, ValueString);
}
}
}
return Message.ToString();
}
}
}