实现非阻塞线程安全单例日志记录 class
Implementing Non blocking thread safe singleton logging class
我想编写一个记录器 class,它根据用户操作将输出写入日志文件。为此,我写了一条日志消息class。以下是我的。
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 单例对象调用此 SaveLogMessage()。这是正确的做法吗?
更新
我已经使用 backgroundworker 实现了这个,完整的 class 答案中提到了如何调用。感谢输入 @bret 和 @huan
对于您的 "instance" 字段,请使用 "readonly" 而不是 "volatile",因为该字段的值永远不会改变。
您甚至不需要 public 实例 属性--您可以将 SaveLogMessage 设为静态方法并直接使用它。
在 SaveLogMessage 正文中,您可以使用 Task.Run(() => { ... }) 在后台线程上执行日志记录操作以使其成为非阻塞。如果您非常频繁地记录,这可能不是非常有效。
考虑改用 System.Diagnostics.Trace,这会给您带来更大的灵活性。
- 写时最好使用ReaderWriterLockslim 只加写锁
- 我认为使用 ReaderWriterLockslim 的静态方法就足够了。
有个不错的posthere
- 如果你想在这里使用单例,你可以使用Lazy参考Implementing the Singleton Pattern in C#
顺便说一句,为什么不使用 log4net 或任何其他现有库。
最后,我通过使用后台工作者成功地实现了非阻塞、线程安全、单例日志记录 class。我正在提交我的解决方案,以防有一天有人发现它有用。
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);
我想编写一个记录器 class,它根据用户操作将输出写入日志文件。为此,我写了一条日志消息class。以下是我的。
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 单例对象调用此 SaveLogMessage()。这是正确的做法吗?
更新
我已经使用 backgroundworker 实现了这个,完整的 class 答案中提到了如何调用。感谢输入 @bret 和 @huan
对于您的 "instance" 字段,请使用 "readonly" 而不是 "volatile",因为该字段的值永远不会改变。
您甚至不需要 public 实例 属性--您可以将 SaveLogMessage 设为静态方法并直接使用它。
在 SaveLogMessage 正文中,您可以使用 Task.Run(() => { ... }) 在后台线程上执行日志记录操作以使其成为非阻塞。如果您非常频繁地记录,这可能不是非常有效。
考虑改用 System.Diagnostics.Trace,这会给您带来更大的灵活性。
- 写时最好使用ReaderWriterLockslim 只加写锁
- 我认为使用 ReaderWriterLockslim 的静态方法就足够了。 有个不错的posthere
- 如果你想在这里使用单例,你可以使用Lazy参考Implementing the Singleton Pattern in C#
顺便说一句,为什么不使用 log4net 或任何其他现有库。
最后,我通过使用后台工作者成功地实现了非阻塞、线程安全、单例日志记录 class。我正在提交我的解决方案,以防有一天有人发现它有用。
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);