代码之家  ›  专栏  ›  技术社区  ›  Pissu Pusa

实现非阻塞线程安全的单例日志记录类[关闭]

  •  -1
  • Pissu Pusa  · 技术社区  · 7 年前

    我想编写一个logger类,它根据用户操作将输出写入日志文件。为此,我编写了一个logmessage类。以下是我所拥有的。

    public class LogMessage
    {
        private  static Object padlock = new Object();
        private static string serviceDirectory ="C:\\MySite\\";
        private static string filePath = "\\Logs\\Combined\\ActivityMonitorLog-" + DateTime.Now.ToString("dd'-'MM'-'yyyy") + ".log";
    
        private static volatile LogMessage instance = new LogMessage();
        public static LogMessage Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (padlock)
                    {
                        if (instance == null)
                            instance = new LogMessage();
                    }
                }
    
                return instance;
            }
        }
    
    
        public void SaveLogMessage(string userName, string message, string stackTrace)
        {
            lock (padlock)
            {
                using (StreamWriter logWriter =
                    new StreamWriter(serviceDirectory + filePath, true))
                {
                    string logMessage = "";
                    if (!string.IsNullOrEmpty(stackTrace))
                        logMessage = string.Format("{0} >> user : {1} ::: {2} ::: stack trace ::: {3}", DateTime.Now.ToString(), userName, message,stackTrace);
                    else
                        logMessage = string.Format("{0} >> user : {1} ::: {2}", DateTime.Now.ToString(), userName, message);
    
                    logWriter.WriteLine(logMessage);
    
                }
            }
        }
    }
    

    当我想做一个日志条目时,我会调用下面的about方法

    LogMessage.Instance.SaveLogMessage("My User", "Enter Logout PageLoad : Current Method :" + "Page_Load @ Logout.aspx.cs", "");
    

    我相信我已经实现了单例部分和线程安全。但我不认为这是无阻塞的,因为多个用户同时登录,每个人都必须等到文件可以写入。我正在考虑使用 backgroundworker 从LogMessage singleton对象调用此SaveLogMessage()。这是正确的做法吗?

    更新

    3 回复  |  直到 7 年前
        1
  •  1
  •   Bret Mulvey    7 年前

    对于“实例”字段,请使用“只读”而不是“volatile”,因为该字段的值永远不会更改。

        2
  •  1
  •   Huan Jiang    7 年前
    1. 我认为使用ReaderWriterLockslim的静态方法就足够了。 here
    2. 如果您确实想在这里使用singleton,可以使用Lazy-refer Implementing the Singleton Pattern in C#

    log4net 或任何其他现有图书馆。

        3
  •  1
  •   Pissu Pusa    7 年前

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Web;
    using System.Threading;
    using System.Threading.Tasks;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Web.Configuration;
    /// <summary>
    /// Summary description for LogMessage
    /// </summary>
    public class LogMessage
    {
        static ReaderWriterLock locker = new ReaderWriterLock();
    
        private static string serviceDirectory = HttpContext.Current != null ?
            AppDomain.CurrentDomain.BaseDirectory :
            Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        private static string fullpath = serviceDirectory + "\\ActivityLog.log";
        private static readonly LogMessage instance = new LogMessage();
    
        public static LogMessage Instance
        {
            get { return instance; }
        }
    
        public void SaveLogMessage(string userName, string message, string stackTrace, bool inOut)
        {
            bool EnableActivityLogging = false;
    
            if (string.IsNullOrEmpty(WebConfigurationManager.AppSettings["EnableActivityLogging"]))
                return;
    
            EnableActivityLogging = Convert.ToBoolean(WebConfigurationManager.AppSettings["EnableActivityLogging"]);
    
            if (!EnableActivityLogging)
                return;
    
            BackgroundWorker logbw = new BackgroundWorker();
            logbw.DoWork += logbw_DoWork;
            logbw.RunWorkerCompleted += logbw_RunWorkerCompleted;
    
            List<string> paramList = new List<string>();
            paramList.Add(userName);
            paramList.Add(message);
            paramList.Add(stackTrace);
            paramList.Add(inOut.ToString());
    
            if (!logbw.IsBusy)
            {
                logbw.RunWorkerAsync(paramList);
            }
        }
    
        void logbw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Debug.Write("Log Message Background Worker is now free...");
        }
    
        void logbw_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> paramList = (List<string>)e.Argument;
            string userName = paramList[0].ToString();
            string message = paramList[1].ToString();
            string stackTrace = paramList[2].ToString();
            bool inOut = bool.Parse(paramList[3].ToString());
    
            try
            {
                locker.AcquireWriterLock(int.MaxValue);
                using (StreamWriter logWriter =
                    new StreamWriter(fullpath, true))
                {
                    string logMessage = "";
                    if (!string.IsNullOrEmpty(stackTrace))
                    {
                        if (inOut)//IN
                        {
                            logMessage = string.Format("{0} U:{1} IN:{2} E:{3}", DateTime.Now.ToString(), userName, message, stackTrace);
                        }
                        else//OUT
                        {
                            logMessage = string.Format("{0} U:{1} OUT:{2} E:{3}", DateTime.Now.ToString(), userName, message, stackTrace);
                        }
                    }
                    else
                    {
                        if (inOut)//IN
                        {
                            logMessage = string.Format("{0} U:{1} IN:{2}", DateTime.Now.ToString(), userName, message);
                        }
                        else//OUT
                        {
                            logMessage = string.Format("{0} U:{1} OUT:{2}", DateTime.Now.ToString(), userName, message);
                        }
                    }
    
                    logWriter.WriteLine(logMessage);
    
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }
    
    }
    

    现在,我可以使用下面这样的单例对象来保存日志条目

        LogMessage.Instance.SaveLogMessage(Context.User.Identity.Name, "Custom Message" + " M:" + MethodInfo.GetCurrentMethod().Name + "@" + Path.GetFileName(Page.AppRelativeVirtualPath), "", true);