using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace Swan.Logging { /// /// Entry-point for logging. Use this static class to register/unregister /// loggers instances. By default, the ConsoleLogger is registered. /// public static class Logger { /*private static readonly Object SyncLock = new Object();*/ // [Obsolete("NEED", false)] private static readonly List Loggers = new List(); // [Obsolete("NEED", false)] private static UInt64 _loggingSequence; // [Obsolete("NEED", false)] static Logger() { if(Terminal.IsConsolePresent) { Loggers.Add(ConsoleLogger.Instance); } if(DebugLogger.IsDebuggerAttached) { Loggers.Add(DebugLogger.Instance); } } /*#region Standard Public API /// /// Registers the logger. /// /// The type of logger to register. /// There is already a logger with that class registered. public static void RegisterLogger() where T : ILogger { lock(SyncLock) { ILogger loggerInstance = Loggers.FirstOrDefault(x => x.GetType() == typeof(T)); if(loggerInstance != null) { throw new InvalidOperationException("There is already a logger with that class registered."); } Loggers.Add(Activator.CreateInstance()); } } /// /// Registers the logger. /// /// The logger. public static void RegisterLogger(ILogger logger) { lock(SyncLock) { Loggers.Add(logger); } } /// /// Unregisters the logger. /// /// The logger. /// logger. public static void UnregisterLogger(ILogger logger) => RemoveLogger(x => x == logger); /// /// Unregisters the logger. /// /// The type of logger to unregister. public static void UnregisterLogger() => RemoveLogger(x => x.GetType() == typeof(T)); /// /// Remove all the loggers. /// public static void NoLogging() { lock(SyncLock) { Loggers.Clear(); } } #region Debug /// /// Logs a debug message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Debug(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a debug message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Debug(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Debug, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a debug message to the console. /// /// The exception. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Debug(this Exception extendedData, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); #endregion #region Trace /// /// Logs a trace message to the console. /// /// The text. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Trace(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a trace message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Trace(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Trace, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a trace message to the console. /// /// The extended data. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Trace(this Exception extendedData, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); #endregion #region Warn /// /// Logs a warning message to the console. /// /// The text. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Warn(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a warning message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Warn(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Warning, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a warning message to the console. /// /// The extended data. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Warn(this Exception extendedData, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); #endregion #region Fatal /// /// Logs a warning message to the console. /// /// The text. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Fatal(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a warning message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Fatal(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Fatal, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a warning message to the console. /// /// The extended data. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Fatal(this Exception extendedData, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); #endregion #region Info /// /// Logs an info message to the console. /// /// The text. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Info(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs an info message to the console. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Info(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Info, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs an info message to the console. /// /// The extended data. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Info(this Exception extendedData, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); #endregion #region Error /// /// Logs an error message to the console's standard error. /// /// The text. /// The source. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Error(this String message, String source = null, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Error, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs an error message to the console's standard error. /// /// The message. /// The source. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Error(this String message, Type source, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Error, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs an error message to the console's standard error. /// /// The exception. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Error(this Exception ex, String source, String message, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Error, message, source, ex, callerMemberName, callerFilePath, callerLineNumber); #endregion #endregion*/ #region Extended Public API /*/// /// Logs the specified message. /// /// The message. /// The source. /// Type of the message. /// The extended data. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Log(this String message, String source, LogLevel messageType, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs the specified message. /// /// The message. /// The source. /// Type of the message. /// The extended data. /// Name of the caller member. /// The caller file path. /// The caller line number. public static void Log(this String message, Type source, LogLevel messageType, Object extendedData = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);*/ /// /// Logs an error message to the console's standard error. /// /// The ex. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. // [Obsolete("NEED", false)] public static void Log(this Exception ex, String source = null, String message = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Error, message ?? ex.Message, source ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber); /*/// /// Logs an error message to the console's standard error. /// /// The ex. /// The source. /// The message. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. public static void Log(this Exception ex, Type source = null, String message = null, [CallerMemberName] String callerMemberName = "", [CallerFilePath] String callerFilePath = "", [CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogLevel.Error, message ?? ex.Message, source?.FullName ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber); /// /// Logs a trace message showing all possible non-null properties of the given object /// This method is expensive as it uses Stringify internally. /// /// The object. /// The source. /// The title. /// Name of the caller member. This is automatically populated. /// The caller file path. This is automatically populated. /// The caller line number. This is automatically populated. 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(LogLevel.Trace, message, source, obj, callerMemberName, callerFilePath, callerLineNumber); } /// /// Logs a trace message showing all possible non-null properties of the given object /// This method is expensive as it uses Stringify internally. /// /// The object. /// The source. /// The text. /// Name of the caller member. /// The caller file path. /// The caller line number. 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(LogLevel.Trace, message, source?.FullName, obj, callerMemberName, callerFilePath, callerLineNumber); }*/ #endregion /*private static void RemoveLogger(Func criteria) { lock(SyncLock) { ILogger loggerInstance = Loggers.FirstOrDefault(criteria); if(loggerInstance == null) { throw new InvalidOperationException("The logger is not registered."); } loggerInstance.Dispose(); _ = Loggers.Remove(loggerInstance); } }*/ // [Obsolete("NEED", false)] private static void LogMessage(LogLevel logLevel, String message, String sourceName, Object extendedData, String callerMemberName, String callerFilePath, Int32 callerLineNumber) { UInt64 sequence = _loggingSequence; DateTime date = DateTime.UtcNow; _loggingSequence++; String loggerMessage = String.IsNullOrWhiteSpace(message) ? String.Empty : message.RemoveControlCharsExcept('\n'); LogMessageReceivedEventArgs eventArgs = new LogMessageReceivedEventArgs(sequence, logLevel, date, sourceName, loggerMessage, extendedData, callerMemberName, callerFilePath, callerLineNumber); foreach(ILogger logger in Loggers) { _ = Task.Run(() => { if(logger.LogLevel <= logLevel) { logger.Log(eventArgs); } }); } } } }