File: OISLogger.cs

Details

File: OISLogger.cs
Date: Thu, May 26, 2011
Created by: Cynic

Source code for the CoDocker CSharp to HTML converter from OfItselfSo.com

using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices; 
using System.Windows.Forms;
using System.Threading;
using System.Drawing;

/// +------------------------------------------------------------------------------------------------------------------------------+
/// �                                                   TERMS OF USE: MIT License                                                  �
/// +------------------------------------------------------------------------------------------------------------------------------�
/// �Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    �
/// �files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    �
/// �modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software�
/// �is furnished to do so, subject to the following conditions:                                                                   �
/// �                                                                                                                              �
/// �The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.�
/// �                                                                                                                              �
/// �THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          �
/// �WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         �
/// �COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   �
/// �ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         �
/// +------------------------------------------------------------------------------------------------------------------------------+

/// #########
/// Note: the three letter "OIS" prefix used here is an acronym for "OfItselfSo.com" this softwares home website.
/// #########

namespace OISCommon
    {

    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// <summary>
    /// This is a singleton class which contains logging and other misc items
    /// useful to an application. If you don't know what a Singleton class is 
    /// you should look it up. Otherwise the way this class works will not really
    /// make sense.
    /// 
    /// Note that calls to the RecordMessage() logwriters are thread safe
    /// due to the Writer lock acquisition.
    /// 
    /// The logfile stream is opened, flushed and closed with each call. This
    /// means RecordMessage() calls are not suitable for the fast recording of data.  
    /// They are too slow for that. However, if things crash the log will have
    /// everything that was written to it. Basically it is useful for recording
    /// way points in the code path and a back trail of user actions and errors. 
    /// 
    /// </summary>
    /// <history>
    ///    03 Nov 09  Cynic - Started
    /// </history>
    public sealed class OISLogger 
    {
        // constants
        private const string DEFAULTDIALOGBOXTITLE = "OIS Application Notice";
        private const string STANDARDLOGFILEEXT = ".log";
        private const string DEFAULTLOGFILENAME = "OISBaseApp";
        private const string DEFAULTLOGFILE_SUBDIR = "Log Files";
        const int timeOut = 100;   // logfile lock time out in milliseconds 100=.1sec

        private bool ignoreLogging = false;   
        private string defaultDialogBoxTitle = DEFAULTDIALOGBOXTITLE;
        private string logFileDirectory = null;
        private string logFileName = DEFAULTLOGFILENAME + STANDARDLOGFILEEXT;

        // this is the applications main form - only set it when the logger is created
        private frmOISBase applicationMainForm=null;

        private object randomNumberSeedLock=new object();
        private int randomNumberSeed=(int)DateTime.Now.Ticks; 

        // the main registry key of the application. We keep this in here because
        // there are some generic classes which need to record settings etc.
        private string applicationPrimaryRegistryKey=null;

        int uniqueObjectCounter=0;
        private Icon appIcon=null;

        // Declaring the ReaderWriterLock at the class level
        // makes it visible to all threads.
        static ReaderWriterLock rwl = new ReaderWriterLock();

        // instantiate the singleton OISLogger here
        public static readonly OISLogger OISLoggerInstance = new OISLogger();

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Constructor - note this is private. Required for a singleton class
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        private OISLogger()
        {
        }

        // ########################################################################
        // ##### Log Message Writers
        // ########################################################################

        #region Log Message Writers

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Log an exception.
        /// </summary>
        /// <param name="Message">Exception to LogBase. </param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void RecordMessage(Exception Message)
        {
            this.RecordMessage(Message.Message);
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Log a message. This is thread safe due to the Writer lock acquisition.
        /// We are opening, flushing and closing the stream with each call. This
        /// means this code is not suitable for the fast recording of data. It 
        /// is too slow for that. However, if things crash the log will have
        /// everything that was written to it. Basically it is useful for recording
        /// way points and a back trail of code paths and errors.
        /// </summary>
        /// <param name="msgIn">Message text. </param>
        /// <param name="wantTimeStamp">if true we output a timestamp</param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void RecordMessage(string msgIn, bool wantTimeStamp)
        {
            // if we are ignoring logging then leave now
            if (ignoreLogging == true) return;
            if (msgIn == null) return;

            FileStream fileStream = null;
            StreamWriter writer = null;
            StringBuilder message = new StringBuilder();
            try
            {
                rwl.AcquireWriterLock(timeOut);
                try
                {
                    fileStream = new FileStream(this.LogFileDirectory +
                                                this.LogFileName,
                                                FileMode.OpenOrCreate,
                                                FileAccess.Write);
                    writer = new StreamWriter(fileStream);

                    // Set the file pointer to the end of the file
                    writer.BaseStream.Seek(0, SeekOrigin.End);

                    // Create the message
                    if(wantTimeStamp==true)
                    {
                        message.Append(System.DateTime.Now.ToString());
                        message.Append(" ");
                    }
                    message.Append(msgIn);

                    // Force the write to the underlying file
                    writer.WriteLine(message.ToString());
                    writer.Flush();
                }
                catch (Exception e)
                {
                    // will launch a dialog box if things are not right
                    HandleLoggerException(e);
                }
                finally
                {
                    // ensure the writer is closed
                    if (writer != null) writer.Close();
                    // Ensure that the lock is released.
                    if (rwl != null) rwl.ReleaseWriterLock();
                }
            }
            catch (ApplicationException)
            {
                // The writer lock request timed out. Not much we can do - ignore it
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Log a message.
        /// </summary>
        /// <param name="msgIne">Message text. </param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void RecordMessage(string msgIn)
        {
            RecordMessage(msgIn,true);
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Post details of an exception to the log. Also places the trace information
        /// out to the log
        /// </summary>
        /// <param name="e">The exception to log the details of</param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void RecordException(Exception e)
        {
            RecordMessage("Exception Message: "+e.Message+"\nException Source: "+e.Source);
            RecordMessage(e.StackTrace);
        }
        #endregion

        // ########################################################################
        // ##### Logging Code
        // ########################################################################

        #region Logging Code

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Gets/Sets the ignore logging property
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public bool IgnoreLogging
        {
            get { return this.ignoreLogging; }
            set { this.ignoreLogging = value; }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Gets/Sets the log file name
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public string LogFileName
        {
            get
            {
                if (this.logFileName == null) logFileName = DEFAULTLOGFILENAME + STANDARDLOGFILEEXT;
                else if (this.logFileName.Length == 0) logFileName = DEFAULTLOGFILENAME + STANDARDLOGFILEEXT;
                return logFileName;
            }
            set
            {
                // we require it to have the standard extension - if it does not then
                // tack it on
                if (value == null) logFileName = logFileName = DEFAULTLOGFILENAME + STANDARDLOGFILEEXT;
                else if (value.Trim().Length == 0) logFileName = DEFAULTLOGFILENAME + STANDARDLOGFILEEXT;
                else if (value.Trim().Length <= STANDARDLOGFILEEXT.Length) logFileName = value.Trim() + STANDARDLOGFILEEXT;
                else
                {
                    if (value.Trim().EndsWith(STANDARDLOGFILEEXT) == false)
                    {
                        this.logFileName = value.Trim() + STANDARDLOGFILEEXT;
                    }
                    else this.logFileName = value.Trim();
                }
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Get or set the log file directory location
        /// </summary>
        /// <history>
        ///    04 Nov 09  Cynic - Started
        /// </history>
        public string LogFileDirectory
        {
            get
            {
                // return "" if it is null. This should put the log in the default directory
                if (this.logFileDirectory == null) logFileDirectory = "";
                return this.logFileDirectory;
            }
            set
            {
                if (value == null) logFileDirectory = "";
                else if (value.Trim().Length == 0) logFileDirectory = "";
                else
                {
                    // Verify a '\' exists on the end of the location
                    if (value.Trim().EndsWith("\\") == false)
                    {
                        logFileDirectory = value.Trim() + "\\";
                    }
                    else
                    {
                        logFileDirectory = value.Trim();
                    }
                }
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Initializes the logging 
        /// </summary>
        /// <returns>true logging inited, false it did not</returns>
        /// <param name="logDirName">The directory name for the log file. If null or "" we 
        /// use the current exe directory</param>
        /// <param name="logfileBaseName">The base name of the log file</param>
        /// <param name="wantDistinctName">if true we give it a distinct name</param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public bool InitLogging(string logDirName, string logfileBaseName, bool wantDistinctName)
        {
            string workingFileName = null;
            string workingDirName = null;

            // now build a filename for it
            if (wantDistinctName == false)
            {
                workingFileName = logfileBaseName;
            }
            else
            {
                workingFileName = logfileBaseName + "_" + System.DateTime.Now.ToString("yyyyMMddHHmmssfffffff");
            }
            // get the best directory we can think of
            workingDirName = GetBestLogfileDir(logDirName);
            if (workingDirName == null) return false;

            // set these now, they have checks built in
            LogFileDirectory = workingDirName;
            LogFileName = workingFileName;

            // this sets everything up
            this.Reset(LogFileDirectory, LogFileName);
            return true;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Figures out the best log directory name. Will create the directory if 
        /// it does not exist.
        /// </summary>
        /// <param name="logDir">the log directory to use, if null or "" we use the 
        /// application exe directory</param>
        /// <returns>log directory to use</returns>
        /// <history>
        ///    06 Nov 09  Cynic - Started
        /// </history>
        private string GetBestLogfileDir(string logDir)
        {
            string workingLogDir = logDir;

            // is this sensible?
            if (workingLogDir == null)
            {
                // could not be found so generate one
                workingLogDir = Path.Combine(Environment.CurrentDirectory, DEFAULTLOGFILE_SUBDIR);
            }

            // is it dodgy?
            if (Path.IsPathRooted(workingLogDir) == false)
            {
                // could not be found so generate one
                workingLogDir = Path.Combine(Environment.CurrentDirectory, DEFAULTLOGFILE_SUBDIR);
            }

            // does it exist?
            if (Directory.Exists(workingLogDir) == false)
            {
                Directory.CreateDirectory(workingLogDir);
            }
            // test again
            if (Directory.Exists(workingLogDir) == false)
            {
                return null;
            }
            // return it
            return workingLogDir;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Dumps a standard logfile header out to the log
        /// </summary>
        /// <history>
        ///    04 Nov 09  Cynic - Started
        /// </history>
        public void EmitStandardLogfileheader(string softwareName)
        {
            RecordMessage("###########################################################");
            if (softwareName != null)
            {
                RecordMessage("##### " + softwareName + " Software");
            }
            else
            {
                RecordMessage("###########################################################");
            }
            RecordMessage("###########################################################");
            RecordMessage("");
            RecordMessage("Operation System: " + Environment.OSVersion.ToString());
            RecordMessage(".NET Version: " + Environment.Version.ToString());
            RecordMessage("Current Directory: " + Environment.CurrentDirectory.ToString());
            RecordMessage("Machine Name: " + Environment.MachineName.ToString());
            RecordMessage("User Name: " + Environment.UserName.ToString());
            RecordMessage("");
            RecordMessage("Logging Begins");

        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Reset the log file - create it as an empty file. Will overwrite if it
        ///     already exists. Call this before any writes to the log
        /// </summary>
        /// <param name="logFileDirectoryIn">Path to log file.</param>
        /// <param name="logFileNameIn">Name of log file. </param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void Reset(string logFileDirectoryIn, string logFileNameIn)
        {
            FileStream fileStream = null;

            // use the properties to set these - they perform checks
            LogFileDirectory = logFileDirectoryIn;
            LogFileName = logFileNameIn;
            try
            {
                rwl.AcquireWriterLock(timeOut);
                try
                {
                    fileStream = new FileStream(this.LogFileDirectory +
                                                this.LogFileName,
                                                FileMode.Create,
                                                FileAccess.Write);
                    fileStream.Close();
                }
                catch (Exception e)
                {
                    // will launch a dialog box if things are not right
                    HandleLoggerException(e);
                }
                finally
                {
                    // ensure the filestream is closed
                    if (fileStream != null) fileStream.Close();
                    // Ensure that the lock is released.
                    if (rwl != null) rwl.ReleaseWriterLock();
                }
            }
            catch (ApplicationException)
            {
                // The writer lock request timed out. Not much we can do - ignore it
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// This is called by the logger when an error occurs. It tells the user
        /// what error happened and offers to fix it or to run without logging.
        /// </summary>
        /// <param name="errText">The error text to post</param>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public void HandleLoggerException(Exception e)
        {
            // you CANNOT call the standard logging functions here. The 
            // Standard logging functions are what caused the problem in the
            // first place. Stick soley with the Windows API !!!!
            frmOISLoggerException dlgObj = new frmOISLoggerException(e);
            if (dlgObj == null)
            {
                IgnoreLogging = true;
                return;
            }
            dlgObj.Text = DefaultDialogBoxTitle + " Log File Error";
            dlgObj.LogFileDirectory = LogFileDirectory;
            dlgObj.LogFileName = LogFileName;
            DialogResult result = dlgObj.ShowDialog();
            // the box is set to return DialogResult.OK for "use new logfile path and name"
            // and DialogResult.Ignore for "Turn Logging Off"
            if (result == DialogResult.OK)
            {
                // reset these
                LogFileDirectory = dlgObj.LogFileDirectory;
                LogFileName = dlgObj.LogFileName;
            }
            else
            {
                // turn logging off
                IgnoreLogging = true;
            }
        }

        #endregion

        // ########################################################################
        // ##### Misc. Properties and Functions
        // ########################################################################

        #region Misc. Properties and Functions

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the "this" value of the applications main form. This should 
        /// only be set once - immediately after the first instance of the singleton
        /// logger object is created.
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public frmOISBase ApplicationMainForm
        {
            get
            {
                return applicationMainForm;
            }
            set
            {
                applicationMainForm = value;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the default culture we use for most things
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public System.Globalization.CultureInfo GetDefaultCulture()
        {
            System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("");
            culture.NumberFormat.NumberDecimalSeparator = ".";
            culture.NumberFormat.NumberGroupSeparator = "";
            return culture;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the random number seed for the application
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public int RandomNumberSeed
        {
            get
            {
                // we do this so that nobody ever gets the same seed
                lock (randomNumberSeedLock)
                {
                    return ++randomNumberSeed;
                }
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Set the applications primary registry key.
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public string ApplicationPrimaryRegistryKey
        {
            get
            {
                return applicationPrimaryRegistryKey;
            }
            set
            {
                applicationPrimaryRegistryKey = value;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the default icon for this application
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public Icon AppIcon
        {
            get
            {
                return appIcon;
            }
            set
            {
                appIcon = value;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Gets/Sets the default dialog box title
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public string DefaultDialogBoxTitle
        {
            get
            {
                if (defaultDialogBoxTitle == null)
                {
                    defaultDialogBoxTitle = DEFAULTDIALOGBOXTITLE;
                }
                return this.defaultDialogBoxTitle;
            }
            set
            {
                if (value == null) defaultDialogBoxTitle = DEFAULTDIALOGBOXTITLE;
                else defaultDialogBoxTitle = value;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Make a Beep sound - just essentially the Win32 MessageBeep()
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        [DllImport("user32")]
        public static extern int MessageBeep(int wType);

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=  
        /// <summary>
        /// Play a system .wav sound file.
        /// Usage: OISLogger.PlaySound("beep.wav", 0, OISLogger.SND_logFileName | OISLogger.SND_ASYNC); 
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public const int SND_logFileName = 0x00020000;
        public const int SND_ASYNC = 0x0001;
        [DllImport("winmm.dll")]
        public static extern bool PlaySound(string pszSound, int hmod, int fdwSound);

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Returns a temporary object name. We put it here in a singleton class
        /// so that we can crank up a counter for every one - this ensuring it
        /// is unique
        /// </summary>
        /// <history>
        ///    03 Nov 09  Cynic - Started
        /// </history>
        public string GetTempObjectName(string leadingStr)
        {
            lock (this)
            {
                uniqueObjectCounter++;
                return leadingStr + System.DateTime.Now.ToString("yyyyMMddHHmmssfffffff") + uniqueObjectCounter.ToString();
            }
        }

        #endregion

    }
}
HTML Code Generated by CoDocker v00.90 Beta