RaspberryIO/Unosquare.Swan.Lite/Terminal.Logging.cs
2019-12-04 17:10:06 +01:00

674 lines
30 KiB
C#

using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Unosquare.Swan {
/// <summary>
/// A console terminal helper to create nicer output and receive input from the user
/// This class is thread-safe :).
/// </summary>
public static partial class Terminal {
#region Private Declarations
private static UInt64 _loggingSequence;
#endregion
#region Events
/// <summary>
/// Occurs asynchronously, whenever a logging message is received by the terminal.
/// Only called when Terminal writes data via Info, Error, Trace, Warn, Fatal, Debug methods, regardless of whether or not
/// the console is present. Subscribe to this event to pass data on to your own logger.
/// </summary>
public static event EventHandler<LogMessageReceivedEventArgs> OnLogMessageReceived;
/// <summary>
/// Occurs synchronously (so handle quickly), whenever a logging message is about to be enqueued to the
/// console output. Setting the CancelOutput to true in the event arguments prevents the
/// logging message to be written out to the console.
/// Message filtering only works with logging methods such as Trace, Debug, Info, Warn, Fatal, Error and Dump
/// Standard Write methods do not get filtering capabilities.
/// </summary>
public static event EventHandler<LogMessageDisplayingEventArgs> OnLogMessageDisplaying;
#endregion
#region Main Logging Method
/// <summary>
/// Logs a message.
/// </summary>
/// <param name="messageType">Type of the message.</param>
/// <param name="message">The text.</param>
/// <param name="sourceName">Name of the source.</param>
/// <param name="extendedData">The extended data. Could be an exception, or a dictionary of properties or anything the user specifies.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
private static void LogMessage(
LogMessageType messageType,
String message,
String sourceName,
Object extendedData,
String callerMemberName,
String callerFilePath,
Int32 callerLineNumber) {
lock(SyncLock) {
if(!Settings.GlobalLoggingMessageType.HasFlag(messageType)) {
return;
}
String prefix = GetConsoleColorAndPrefix(messageType, out ConsoleColor color);
#region Create and Format the Output
UInt64 sequence = _loggingSequence;
DateTime date = DateTime.UtcNow;
_loggingSequence++;
String loggerMessage = String.IsNullOrWhiteSpace(message) ?
String.Empty : message.RemoveControlCharsExcept('\n');
String outputMessage = CreateOutputMessage(sourceName, loggerMessage, prefix, date);
// Log the message asynchronously with the appropriate event args
LogMessageReceivedEventArgs eventArgs = new LogMessageReceivedEventArgs(
sequence,
messageType,
date,
sourceName,
loggerMessage,
extendedData,
callerMemberName,
callerFilePath,
callerLineNumber);
#endregion
#region Fire Up External Logging Logic (Asynchronously)
if(OnLogMessageReceived != null) {
_ = Task.Run(() => {
try {
OnLogMessageReceived?.Invoke(sourceName, eventArgs);
} catch {
// Ignore
}
});
}
#endregion
#region Display the Message by Writing to the Output Queue
// Check if we are skipping these messages to be displayed based on settings
if(!Settings.DisplayLoggingMessageType.HasFlag(messageType)) {
return;
}
Write(messageType, sourceName, eventArgs, outputMessage, color);
#endregion
}
}
private static void Write(
LogMessageType messageType,
String sourceName,
LogMessageReceivedEventArgs eventArgs,
String outputMessage,
ConsoleColor color) {
// Select the writer based on the message type
TerminalWriters writer = IsConsolePresent
? messageType.HasFlag(LogMessageType.Error) ? TerminalWriters.StandardError : TerminalWriters.StandardOutput
: TerminalWriters.None;
// Set the writer to Diagnostics if appropriate (Error and Debugging data go to the Diagnostics debugger
// if it is attached at all
if(IsDebuggerAttached
&& (IsConsolePresent == false || messageType.HasFlag(LogMessageType.Debug) ||
messageType.HasFlag(LogMessageType.Error))) {
writer |= TerminalWriters.Diagnostics;
}
// Check if we really need to write this out
if(writer == TerminalWriters.None) {
return;
}
// Further format the output in the case there is an exception being logged
if(writer.HasFlag(TerminalWriters.StandardError) && eventArgs.Exception != null) {
try {
outputMessage =
$"{outputMessage}{Environment.NewLine}{eventArgs.Exception.Stringify().Indent()}";
} catch {
// Ignore
}
}
// Filter output messages via events
LogMessageDisplayingEventArgs displayingEventArgs = new LogMessageDisplayingEventArgs(eventArgs);
OnLogMessageDisplaying?.Invoke(sourceName, displayingEventArgs);
if(displayingEventArgs.CancelOutput == false) {
outputMessage.WriteLine(color, writer);
}
}
private static String GetConsoleColorAndPrefix(LogMessageType messageType, out ConsoleColor color) {
String prefix;
// Select color and prefix based on message type
// and settings
switch(messageType) {
case LogMessageType.Debug:
color = Settings.DebugColor;
prefix = Settings.DebugPrefix;
break;
case LogMessageType.Error:
color = Settings.ErrorColor;
prefix = Settings.ErrorPrefix;
break;
case LogMessageType.Info:
color = Settings.InfoColor;
prefix = Settings.InfoPrefix;
break;
case LogMessageType.Trace:
color = Settings.TraceColor;
prefix = Settings.TracePrefix;
break;
case LogMessageType.Warning:
color = Settings.WarnColor;
prefix = Settings.WarnPrefix;
break;
case LogMessageType.Fatal:
color = Settings.FatalColor;
prefix = Settings.FatalPrefix;
break;
default:
color = Settings.DefaultColor;
prefix = new String(' ', Settings.InfoPrefix.Length);
break;
}
return prefix;
}
private static String CreateOutputMessage(String sourceName, String loggerMessage, String prefix, DateTime date) {
String friendlySourceName = String.IsNullOrWhiteSpace(sourceName)
? String.Empty
: sourceName.SliceLength(sourceName.LastIndexOf('.') + 1, sourceName.Length);
String outputMessage = String.IsNullOrWhiteSpace(sourceName)
? loggerMessage
: $"[{friendlySourceName}] {loggerMessage}";
return String.IsNullOrWhiteSpace(Settings.LoggingTimeFormat)
? $" {prefix} >> {outputMessage}"
: $" {date.ToLocalTime().ToString(Settings.LoggingTimeFormat)} {prefix} >> {outputMessage}";
}
#endregion
#region Standard Public API
#region Debug
/// <summary>
/// Logs a debug message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Debug(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a debug message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Debug(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a debug message to the console.
/// </summary>
/// <param name="extendedData">The exception.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Debug(
this Exception extendedData,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#region Trace
/// <summary>
/// Logs a trace message to the console.
/// </summary>
/// <param name="message">The text.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Trace(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a trace message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Trace(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a trace message to the console.
/// </summary>
/// <param name="extendedData">The extended data.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Trace(
this Exception extendedData,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#region Warn
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="message">The text.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Warn(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Warn(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="extendedData">The extended data.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Warn(
this Exception extendedData,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#region Fatal
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="message">The text.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Fatal(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Fatal(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a warning message to the console.
/// </summary>
/// <param name="extendedData">The extended data.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Fatal(
this Exception extendedData,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#region Info
/// <summary>
/// Logs an info message to the console.
/// </summary>
/// <param name="message">The text.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Info(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an info message to the console.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Info(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an info message to the console.
/// </summary>
/// <param name="extendedData">The extended data.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Info(
this Exception extendedData,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#region Error
/// <summary>
/// Logs an error message to the console's standard error.
/// </summary>
/// <param name="message">The text.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Error(
this String message,
String source = null,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an error message to the console's standard error.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Error(
this String message,
Type source,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an error message to the console's standard error.
/// </summary>
/// <param name="ex">The exception.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Error(
this Exception ex,
String source,
String message,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source, ex, callerMemberName, callerFilePath, callerLineNumber);
#endregion
#endregion
#region Extended Public API
/// <summary>
/// Logs the specified message.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="messageType">Type of the message.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Log(
this String message,
String source,
LogMessageType messageType,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs the specified message.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="source">The source.</param>
/// <param name="messageType">Type of the message.</param>
/// <param name="extendedData">The extended data.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Log(
this String message,
Type source,
LogMessageType messageType,
Object extendedData = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an error message to the console's standard error.
/// </summary>
/// <param name="ex">The ex.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Log(
this Exception ex,
String source = null,
String message = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message ?? ex.Message, source ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs an error message to the console's standard error.
/// </summary>
/// <param name="ex">The ex.</param>
/// <param name="source">The source.</param>
/// <param name="message">The message.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Log(
this Exception ex,
Type source = null,
String message = null,
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message ?? ex.Message, source?.FullName ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
/// <summary>
/// Logs a trace message showing all possible non-null properties of the given object
/// This method is expensive as it uses Stringify internally.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="source">The source.</param>
/// <param name="text">The title.</param>
/// <param name="callerMemberName">Name of the caller member. This is automatically populated.</param>
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
public static void Dump(
this Object obj,
String source,
String text = "Object Dump",
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) {
if(obj == null) {
return;
}
String message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
LogMessage(LogMessageType.Trace, message, source, obj, callerMemberName, callerFilePath, callerLineNumber);
}
/// <summary>
/// Logs a trace message showing all possible non-null properties of the given object
/// This method is expensive as it uses Stringify internally.
/// </summary>
/// <param name="obj">The object.</param>
/// <param name="source">The source.</param>
/// <param name="text">The text.</param>
/// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public static void Dump(
this Object obj,
Type source,
String text = "Object Dump",
[CallerMemberName] String callerMemberName = "",
[CallerFilePath] String callerFilePath = "",
[CallerLineNumber] Int32 callerLineNumber = 0) {
if(obj == null) {
return;
}
String message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
LogMessage(LogMessageType.Trace, message, source?.FullName, obj, callerMemberName, callerFilePath, callerLineNumber);
}
#endregion
}
}