C#开发高性能Log Help 类设计开发

概述

项目中要在操作数据库的异常处理中加入写Log日志,对于商业上有要求,写log时对其它操作尽可能影响小,不能因为加入log导致耗时太多.

设计思想

在写入日志时利用Queue来管理,写日志有一个专门的backgroud线程来处理,如果没有日志要写,这个线程处于wait状态,这就有了线程的异步处理.

简单的实现方式

        //<summary>
        //Write Log
        //<summary>
        public static void WriteLog(string logFile, string msg)
        {
            try
            {
                System.IO.StreamWriter sw = System.IO.File.AppendText(
                        logPath + LogFilePrefix +" "+ logFile + " " +
                        DateTime.Now.ToString("yyyyMMdd") + ".Log"
                    );
                sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:  ") + msg);
                sw.Close();
            }
            catch (Exception)
            {
                
                throw;
            }
        }

我们的设计图

image

而后我们在AddLogMessage时semaphore.Release()就能唤醒wait中的log 线程.

代码设计

    /// <summary>
    /// Author: spring yang
    /// Create time:2012/3/30 
    /// Log Help class
    /// </summary>
    /// <remarks>High performance log class</remarks>
    public class Log : IDisposable
    {
        //Log Message queue
        private static Queue<LogMessage> _logMessages;

        //log save directory
        private static string _logDirectory;

        //log write file state
        private static bool _state;

        //log type
        private static LogType _logType;

        //log life time sign
        private static DateTime _timeSign;

        //log file stream writer
        private static StreamWriter _writer;

        /// <summary>
        /// Wait enqueue wirte log message semaphore will release
        /// </summary>
        private Semaphore _semaphore;

        /// <summary>
        /// Single instance
        /// </summary>
        private static Log _log;

        /// <summary>
        /// Gets a single instance
        /// </summary>
        public static Log LogInstance
        {
            get { return _log ?? (_log = new Log()); }
        }

        private object _lockObjeck;

        /// <summary>
        /// Initialize Log instance
        /// </summary>
        private void Initialize()
        {
            if (_logMessages == null)
            {   _state = true;
                string logPath = System.Configuration.ConfigurationManager.AppSettings["LogDirectory"];
                _logDirectory = string.IsNullOrEmpty(logPath) ? ".\\log\\" : logPath;
                if (!Directory.Exists(_logDirectory)) Directory.CreateDirectory(_logDirectory);
                _logType = LogType.Daily;
                _lockObjeck=new object();
                _semaphore = new Semaphore(0, int.MaxValue, Constants.LogSemaphoreName);
                _logMessages = new Queue<LogMessage>();
                var thread = new Thread(Work) {IsBackground = true};
                thread.Start();
            }
        }
 

        /// <summary>
        /// Create a log instance
        /// </summary>
        private Log()
        {
            Initialize();
        }

        /// <summary>
        /// Log save name type,default is daily
        /// </summary>
        public LogType LogType
        {
            get { return _logType; }
            set { _logType = value; }
        }

        /// <summary>
        /// Write Log file  work method
        /// </summary>
        private void Work()
        {
            while (true)
            {
                //Determine log queue have record need wirte
                if (_logMessages.Count > 0)
                {
                    FileWriteMessage();
                }
                else
                    if (WaitLogMessage()) break;
            }
        }

        /// <summary>
        /// Write message to log file
        /// </summary>
        private void FileWriteMessage()
        {
            LogMessage logMessage=null;
            lock (_lockObjeck)
            {
                if(_logMessages.Count>0)
                logMessage = _logMessages.Dequeue();
            }
            if (logMessage != null)
            {
                FileWrite(logMessage);
            }
        }


        /// <summary>
        /// The thread wait a log message
        /// </summary>
        /// <returns>is close or not</returns>
        private bool WaitLogMessage()
        {
            //determine log life time is true or false
            if (_state)
            {
                WaitHandle.WaitAny(new WaitHandle[] { _semaphore }, -1, false);
                return false;
            }
            FileClose();
            return true;
        }

        /// <summary>
        /// Gets file name by log type
        /// </summary>
        /// <returns>log file name</returns>
        private string GetFilename()
        {
            DateTime now = DateTime.Now;
            string format = "";
            switch (_logType)
            {
                case LogType.Daily:
                    _timeSign = new DateTime(now.Year, now.Month, now.Day);
                    _timeSign = _timeSign.AddDays(1);
                    format = "yyyyMMdd'.log'";
                    break;
                case LogType.Weekly:
                    _timeSign = new DateTime(now.Year, now.Month, now.Day);
                    _timeSign = _timeSign.AddDays(7);
                    format = "yyyyMMdd'.log'";
                    break;
                case LogType.Monthly:
                    _timeSign = new DateTime(now.Year, now.Month, 1);
                    _timeSign = _timeSign.AddMonths(1);
                    format = "yyyyMM'.log'";
                    break;
                case LogType.Annually:
                    _timeSign = new DateTime(now.Year, 1, 1);
                    _timeSign = _timeSign.AddYears(1);
                    format = "yyyy'.log'";
                    break;
            }
            return now.ToString(format);
        }

        /// <summary>
        /// Write log file message
        /// </summary>
        /// <param name="msg"></param>
        private void FileWrite(LogMessage msg)
        {
            try
            {
                if (_writer == null)
                {
                    FileOpen();
                }
                else
                {
                    //determine the log file is time sign
                    if (DateTime.Now >= _timeSign)
                    {
                        FileClose();
                        FileOpen();
                    }
                    _writer.WriteLine(Constants.LogMessageTime+msg.Datetime);
                    _writer.WriteLine(Constants.LogMessageType+msg.Type);
                    _writer.WriteLine(Constants.LogMessageContent+msg.Text);
                    _writer.Flush();
                }
            }
            catch (Exception e)
            {
                Console.Out.Write(e);
            }
        }

        /// <summary>
        /// Open log file write log message
        /// </summary>
        private void FileOpen()
        {
            _writer = new StreamWriter(Path.Combine(_logDirectory, GetFilename()), true, Encoding.UTF8);
        }

        /// <summary>
        /// Close log file 
        /// </summary>
        private void FileClose()
        {
            if (_writer != null)
            {
                _writer.Flush();
                _writer.Close();
                _writer.Dispose();
                _writer = null;
            }
        }

        /// <summary>
        /// Enqueue a new log message and release a semaphore
        /// </summary>
        /// <param name="msg">Log message</param>
        public void Write(LogMessage msg)
        {
            if (msg != null)
            {
                lock (_lockObjeck)
                {
                    _logMessages.Enqueue(msg);
                    _semaphore.Release();
                }
            }
        }

        /// <summary>
        /// Write message by message content and type
        /// </summary>
        /// <param name="text">log message</param>
        /// <param name="type">message type</param>
        public void Write(string text, MessageType type)
        {
            Write(new LogMessage(text, type));
        }

        /// <summary>
        /// Write Message by datetime and message content and type
        /// </summary>
        /// <param name="dateTime">datetime</param>
        /// <param name="text">message content</param>
        /// <param name="type">message type</param>
        public void Write(DateTime dateTime, string text, MessageType type)
        {
            Write(new LogMessage(dateTime, text, type));
        }

        /// <summary>
        /// Write message ty exception and message type 
        /// </summary>
        /// <param name="e">exception</param>
        /// <param name="type">message type</param>
        public void Write(Exception e, MessageType type)
        {
            Write(new LogMessage(e.Message, type));
        }

        #region IDisposable member

        /// <summary>
        /// Dispose log
        /// </summary>
        public void Dispose()
        {
            _state = false;
        }

        #endregion
    }




    /// <summary>
    /// Log Type
    /// </summary>
    /// <remarks>Create log by daily or weekly or monthly or annually</remarks>
    public enum LogType
    {
        /// <summary>
        /// Create log by daily
        /// </summary>
        Daily,

        /// <summary>
        /// Create log by weekly
        /// </summary>
        Weekly,

        /// <summary>
        /// Create log by monthly
        /// </summary>
        Monthly,

        /// <summary>
        /// Create log by annually
        /// </summary>
        Annually
    }





    /// <summary>
    /// Log Message Class
    /// </summary>
    public class LogMessage
    {

        /// <summary>
        /// Create Log message instance
        /// </summary>
        public LogMessage()
            : this("", MessageType.Unknown)
        {
        }

        /// <summary>
        /// Crete log message by message content and message type
        /// </summary>
        /// <param name="text">message content</param>
        /// <param name="messageType">message type</param>
        public LogMessage(string text, MessageType messageType)
            : this(DateTime.Now, text, messageType)
        {
        }

        /// <summary>
        /// Create log message by datetime and message content and message type
        /// </summary>
        /// <param name="dateTime">date time </param>
        /// <param name="text">message content</param>
        /// <param name="messageType">message type</param>
        public LogMessage(DateTime dateTime, string text, MessageType messageType)
        {
            Datetime = dateTime;
            Type = messageType;
            Text = text;
        }

        /// <summary>
        /// Gets or sets datetime
        /// </summary>
        public DateTime Datetime { get; set; }

        /// <summary>
        /// Gets or sets message content
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// Gets or sets message type
        /// </summary>
        public MessageType Type { get; set; }

        /// <summary>
        /// Get Message to string
        /// </summary>
        /// <returns></returns>
        public new string ToString()
        {
            return Datetime.ToString(CultureInfo.InvariantCulture) + "\t" + Text + "\n";
        }
    }


    /// <summary>
    /// Log Message Type enum
    /// </summary>
    public enum MessageType
    {
        /// <summary>
        /// unknown type 
        /// </summary>
        Unknown,

        /// <summary>
        /// information type
        /// </summary>
        Information,

        /// <summary>
        /// warning type
        /// </summary>
        Warning,

        /// <summary>
        /// error type
        /// </summary>
        Error,

        /// <summary>
        /// success type
        /// </summary>
        Success
    }

Test Case:

        public static void TestLog()
        {
            Log.LogInstance.Write(  "Test Message",MessageType.Information);
            Log.LogInstance.Write("one",MessageType.Error);
            Log.LogInstance.Write("two", MessageType.Success);
            Log.LogInstance.Write("three", MessageType.Warning);
        }

运行结果:

image

接受Mainz的建议,改了部分代码,

Mainz:http://www.cnblogs.com/Mainz/

欢迎各位参与讨论,如果觉得对你有帮助,请点击image    推荐下,万分谢谢.

作者:spring yang

出处:http://www.cnblogs.com/springyangwc/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/springyangwc/p/2425822.html