bin/projects/dbatools/dbatools/Message/LogHost.cs

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;

namespace Sqlcollaborative.Dbatools.Message
{
    /// <summary>
    /// Provides static information storage for logging related settings, as well as housing the logging queues.
    /// </summary>
    public static class LogHost
    {
        #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;
        #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 functions using the message or flowcontrol system</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="ModuleName">The name of the module the function writing the error came from</param>
        /// <param name="Tags">The tags that were assigned to the error event</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>
        /// <param name="ComputerName">The computer the error was written on</param>
        public static void WriteErrorEntry(ErrorRecord[] Record, string FunctionName, string ModuleName, List<string> Tags, DateTime Timestamp, string Message, Guid Runspace, string ComputerName)
        {
            DbatoolsExceptionRecord tempRecord = new DbatoolsExceptionRecord(Runspace, ComputerName, Timestamp, FunctionName, ModuleName, Tags, Message);
            foreach (ErrorRecord rec in Record)
            {
                tempRecord.Exceptions.Add(new DbatoolsException(rec, FunctionName, Timestamp, Message, Runspace, ComputerName));
            }

            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="ModuleName">What module did the function writing this message come from?</param>
        /// <param name="Tags">The tags that were applied to 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="ComputerName">The computer the message was generated on</param>
        /// <param name="File">The file from which the message was written</param>
        /// <param name="Line">The line on which the message was written</param>
        /// <param name="TargetObject">The object associated with a given message.</param>
        /// <param name="CallStack">The callstack at the moment the message was written.</param>
        /// <param name="Username">The name of the user under which the code being executed</param>
        /// <returns>The entry that is being written</returns>
        public static LogEntry WriteLogEntry(string Message, LogEntryType Type, DateTime Timestamp, string FunctionName, string ModuleName, List<string> Tags, MessageLevel Level, Guid Runspace, string ComputerName, string File, int Line, IEnumerable<CallStackFrame> CallStack, string Username, object TargetObject = null)
        {
            LogEntry temp = new LogEntry(Message, Type, Timestamp, FunctionName, ModuleName, Tags, Level, Runspace, ComputerName, TargetObject, File, Line, CallStack, Username);
            if (MessageLogFileEnabled) { OutQueueLog.Enqueue(temp); }
            if (MessageLogEnabled) { LogEntries.Enqueue(temp); }

            LogEntry tmp;
            while ((MaxMessageCount > 0) && (LogEntries.Count > MaxMessageCount))
            {
                LogEntries.TryDequeue(out tmp);
            }

            return temp;
        }
        #endregion Access Queues
    }
}