bin/projects/dbatools/dbatools/dbaSystem/DebugHost.cs

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;
 
namespace Sqlcollaborative.Dbatools.dbaSystem
{
    /// <summary>
    /// Hosts static debugging values and methods
    /// </summary>
    public static class DebugHost
    {
        #region Defines
        /// <summary>
        /// The maximum numbers of error records maintained in-memory.
        /// </summary>
        public static int MaxErrorCount = 128;
 
        /// <summary>
        /// The maximum number of messages that can be maintained in the in-memory message queue
        /// </summary>
        public static int MaxMessageCount = 1024;
 
        /// <summary>
        /// The maximum size of a given logfile. When reaching this limit, the file will be abandoned and a new log created. Set to 0 to not limit the size.
        /// </summary>
        public static int MaxMessagefileBytes = 5242880; // 5MB
 
        /// <summary>
        /// The maximum number of logfiles maintained at a time. Exceeding this number will cause the oldest to be culled. Set to 0 to disable the limit.
        /// </summary>
        public static int MaxMessagefileCount = 5;
 
        /// <summary>
        /// The maximum size all error files combined may have. When this number is exceeded, the oldest entry is culled.
        /// </summary>
        public static int MaxErrorFileBytes = 20971520; // 20MB
 
        /// <summary>
        /// This is the upper limit of length all items in the log folder may have combined across all processes.
        /// </summary>
        public static int MaxTotalFolderSize = 104857600; // 100MB
 
        /// <summary>
        /// Path to where the logfiles live.
        /// </summary>
        public static string LoggingPath;
 
        /// <summary>
        /// Any logfile older than this will automatically be cleansed
        /// </summary>
        public static TimeSpan MaxLogFileAge = new TimeSpan(7, 0, 0, 0);
 
        /// <summary>
        /// Governs, whether a log file for the system messages is written
        /// </summary>
        public static bool MessageLogFileEnabled = true;
 
        /// <summary>
        /// Governs, whether a log of recent messages is kept in memory
        /// </summary>
        public static bool MessageLogEnabled = true;
 
        /// <summary>
        /// Governs, whether log files for errors are written
        /// </summary>
        public static bool ErrorLogFileEnabled = true;
 
        /// <summary>
        /// Governs, whether a log of recent errors is kept in memory
        /// </summary>
        public static bool ErrorLogEnabled = true;
 
        /// <summary>
        /// Enables the developer mode. In this additional information and logs are written, in order to make it easier to troubleshoot issues.
        /// </summary>
        public static bool DeveloperMode = false;
 
        /// <summary>
        /// Returns whether the module is currently in a development branch. Some warnings will only be shown in development branch, but hidden in master / public releases
        /// </summary>
        public static bool DevelopmentBranch = true;
        #endregion Defines
 
        #region Queues
        private static ConcurrentQueue<DbatoolsExceptionRecord> ErrorRecords = new ConcurrentQueue<DbatoolsExceptionRecord>();
 
        private static ConcurrentQueue<LogEntry> LogEntries = new ConcurrentQueue<LogEntry>();
 
        /// <summary>
        /// The outbound queue for errors. These will be processed and written to xml
        /// </summary>
        public static ConcurrentQueue<DbatoolsExceptionRecord> OutQueueError = new ConcurrentQueue<DbatoolsExceptionRecord>();
 
        /// <summary>
        /// The outbound queue for logs. These will be processed and written to logfile
        /// </summary>
        public static ConcurrentQueue<LogEntry> OutQueueLog = new ConcurrentQueue<LogEntry>();
        #endregion Queues
 
        #region Access Queues
        /// <summary>
        /// Retrieves a copy of the Error stack
        /// </summary>
        /// <returns>All errors thrown by dbatools functions</returns>
        public static DbatoolsExceptionRecord[] GetErrors()
        {
            DbatoolsExceptionRecord[] temp = new DbatoolsExceptionRecord[ErrorRecords.Count];
            ErrorRecords.CopyTo(temp, 0);
            return temp;
        }
 
        /// <summary>
        /// Retrieves a copy of the message log
        /// </summary>
        /// <returns>All messages logged this session.</returns>
        public static LogEntry[] GetLog()
        {
            LogEntry[] temp = new LogEntry[LogEntries.Count];
            LogEntries.CopyTo(temp, 0);
            return temp;
        }
 
        /// <summary>
        /// Write an error record to the log
        /// </summary>
        /// <param name="Record">The actual error record as powershell wrote it</param>
        /// <param name="FunctionName">The name of the function writing the error</param>
        /// <param name="Timestamp">When was the error written</param>
        /// <param name="Message">What message was passed to the user</param>
        /// <param name="Runspace">The runspace the message was written from</param>
        public static void WriteErrorEntry(ErrorRecord[] Record, string FunctionName, DateTime Timestamp, string Message, Guid Runspace)
        {
            DbatoolsExceptionRecord tempRecord = new DbatoolsExceptionRecord(Runspace, Timestamp, FunctionName, Message);
            foreach (ErrorRecord rec in Record)
            {
                tempRecord.Exceptions.Add(new DbatoolsException(rec, FunctionName, Timestamp, Message, Runspace));
            }
 
            if (ErrorLogFileEnabled) { OutQueueError.Enqueue(tempRecord); }
            if (ErrorLogEnabled) { ErrorRecords.Enqueue(tempRecord); }
 
            DbatoolsExceptionRecord tmp;
            while ((MaxErrorCount > 0) && (ErrorRecords.Count > MaxErrorCount))
            {
                ErrorRecords.TryDequeue(out tmp);
            }
        }
 
        /// <summary>
        /// Write a new entry to the log
        /// </summary>
        /// <param name="Message">The message to log</param>
        /// <param name="Type">The type of the message logged</param>
        /// <param name="Timestamp">When was the message generated</param>
        /// <param name="FunctionName">What function wrote the message</param>
        /// <param name="Level">At what level was the function written</param>
        /// <param name="Runspace">The runspace the message is coming from</param>
        /// <param name="TargetObject">The object associated with a given message.</param>
        public static void WriteLogEntry(string Message, LogEntryType Type, DateTime Timestamp, string FunctionName, MessageLevel Level, Guid Runspace, object TargetObject = null)
        {
            LogEntry temp = new LogEntry(Message, Type, Timestamp, FunctionName, Level, Runspace, TargetObject);
            if (MessageLogFileEnabled) { OutQueueLog.Enqueue(temp); }
            if (MessageLogEnabled) { LogEntries.Enqueue(temp); }
 
            LogEntry tmp;
            while ((MaxMessageCount > 0) && (LogEntries.Count > MaxMessageCount))
            {
                LogEntries.TryDequeue(out tmp);
            }
        }
        #endregion Access Queues
 
        #region Start Times
        /// <summary>
        /// Lists the duration for the last import of dbatools.
        /// </summary>
        public static List<StartTimeEntry> ImportTimeEntries = new List<StartTimeEntry>();
 
        /// <summary>
        /// Returns the calculated time each phase took during the last import of dbatool.
        /// </summary>
        public static List<StartTimeResult> ImportTime
        {
            get
            {
                List<StartTimeResult> result = new List<StartTimeResult>();
                int n = 0;
                foreach (StartTimeEntry entry in ImportTimeEntries)
                    if (n++ > 0)
                        result.Add(new StartTimeResult(entry.Action, ImportTimeEntries[n - 2].Timestamp, entry.Timestamp));
 
                return result;
            }
        }
        #endregion Start Times
    }
}