using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Tango.Logging { /// /// Represents a helper class for logging information and errors. /// public class LogManager { private List _loggers; private ProducerConsumerQueue _logs; private Thread _loggingThread; private bool _isStarted; /// /// Occurs when a new log as been received. /// public event EventHandler NewLog; /// /// Gets the registered loggers. /// public List RegisteredLoggers { get { return _loggers.ToList(); } } private static LogManager _default; /// /// Gets the default log manager instance. /// public static LogManager Default { get { if (_default == null) { _default = new LogManager(); } return _default; } } /// /// Initializes the class. /// public LogManager() { _loggers = new List(); _logs = new ProducerConsumerQueue(); Categories = new List(); Categories.Add(LogCategory.Critical); Categories.Add(LogCategory.Error); Categories.Add(LogCategory.Info); Categories.Add(LogCategory.Warning); } /// /// Gets or sets the logging categories filter. /// public List Categories { get; private set; } /// /// Registers a logger. /// /// The logger. public void RegisterLogger(ILogger logger) { if (logger != null && !_loggers.Contains(logger)) { _loggers.Add(logger); } } /// /// Unregisters a logger. /// /// The logger. public void UnregisterLogger(ILogger logger) { if (logger != null && _loggers.Contains(logger)) { _loggers.Remove(logger); } } /// /// Add new exception log item. /// /// Exception. /// Error description. public Exception Log(Exception e, String description = null, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0) { return Log(e, LogCategory.Error, description, caller, file, lineNumber); } /// /// Add new exception log item. /// /// Exception. /// Error description. public Exception Log(Exception e, LogCategory category, String description = null, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0) { if (!Categories.Contains(category)) return e; ExceptionLogItem log = new ExceptionLogItem(); log.CallerMethodName = caller; log.CallerFile = file; log.CallerLineNumber = lineNumber; log.TimeStamp = DateTime.Now; log.Exception = e; log.Category = category; log.Description = description; log.Message = log.Description + Environment.NewLine + log.Exception.FlattenException(); AppendLog(log); return e; } /// /// Add new message log item. /// /// Message. public String Log(String message, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0) { return Log(message, LogCategory.Info, null, caller, file, lineNumber); } /// /// Add new message log item. /// /// Message. public String Log(String message, LogCategory category, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0) { return Log(message, category, null, caller, file, lineNumber); } /// /// Add new message log item. /// /// Message. public String Log(String message, LogCategory category, Object logObject, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0) { if (!Categories.Contains(category)) return message; MessageLogItem log = new MessageLogItem(); log.CallerMethodName = caller; log.CallerFile = file; log.CallerLineNumber = lineNumber; log.TimeStamp = DateTime.Now; log.Category = category; log.Message = message; log.LogObject = logObject; AppendLog(log); return message; } /// /// Appends the specified log item. /// /// The log item. public void Log(LogItemBase logItem) { AppendLog(logItem); } /// /// Logs the current referenced assemblies in the app domain. /// public void LogReferencedAssemblies() { String log = "--------------------- Referenced Assemblies --------------------------" + Environment.NewLine + Environment.NewLine; try { string codeBase = typeof(LogManager).Assembly.CodeBase; UriBuilder uri = new UriBuilder(codeBase); string path = Uri.UnescapeDataString(uri.Path); String folder = Path.GetDirectoryName(path); foreach (var file in Directory.GetFiles(folder, "*.dll")) { FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(file); string version = fvi.ProductVersion; log += Path.GetFileNameWithoutExtension(file) + ", v" + version + Environment.NewLine; } log += Environment.NewLine + "--------------------- --------------------- --------------------------"; } catch { } Log(log); } /// /// Add new message log item. /// /// Message. public void Log(MessageLogItem log) { log.TimeStamp = DateTime.Now; AppendLog(log); } /// /// Appends the log. /// /// The log. private void AppendLog(LogItemBase log) { _logs.BlockEnqueue(log); StartLoggingThread(); } /// /// Starts the logging thread. /// private void StartLoggingThread() { if (!_isStarted) { _isStarted = true; _loggingThread = new Thread(LoggingThreadMethod); _loggingThread.IsBackground = true; _loggingThread.Start(); } } /// /// Loggings thread method. /// //[DebuggerStepThrough] //[DebuggerHidden] private void LoggingThreadMethod() { while (_isStarted) { LogItemBase log = _logs.BlockDequeue(); _loggers.Where(x => x.Enabled).ToList().ForEach(x => x.OnLog(log)); NewLog?.Invoke(this, log); } } /// /// Creates a new log safe which can be used to keep logs and then be disposed. /// /// public LogSafe CreateLogSafe() { return new LogSafe(this); } } }