概述
项目中要在操作数据库的异常处理中加入写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;
}
}
我们的设计图
而后我们在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);
}
运行结果:
接受Mainz的建议,改了部分代码,
Mainz:http://www.cnblogs.com/Mainz/
欢迎各位参与讨论,如果觉得对你有帮助,请点击 推荐下,万分谢谢.
作者:spring yang
出处:http://www.cnblogs.com/springyangwc/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。