软件系统在运行过程中,会产生大量的运行时中间数据,中间数据是整个数据流向和异常后的回溯,一个好的日志记录系统,能记录下整个软件运行周期内的所有相关中间数据流。
日志记录系统首先要保证的是与软件的正常运行无关性,他不会随着软件异常的抛出,而崩溃或阻塞正常的系统运行,日志系统还要高效性,不能因为记录日志影响系统的整体资源调度。
此模型的设计思路是,异常消息记录队列+周期性处理定时器+消息关闭回调消息中心,所有的日志,统一先压入异常消息记录队列,周期性处理定时器触发后,负责将消息队列整体锁定,更换队列的处理引用地址,在定时器处理线程栈上,进行数据的写入处理,因为是通过定时器周期性的写入数据到存储介质,在软件关闭时刻可能存在还未写入存储介质的数据,因此需要消息中心的回调处理,进行数据的收尾保存。
此模型的设计,简单易于实现,但是也存在很大的弊端是,在高并发场合的写入会存在很高的锁争用。定时器控制写入到存储介质,吞吐和效率不好调整。如果存在日志保存到比较慢速的介质中,缓存不够。
/// <summary>
///SQL操作日志记录服务
/// </summary>
internal static class SQLLogService
{
/// <summary>
/// 线程互斥锁
/// </summary>
private static object syncObj = new object();
/// <summary>
/// 写入锁
/// </summary>
private static object writeLock = new object();
/// <summary>
/// 日志记录消息队列
/// </summary>
private static Queue<SQLLogMessage> logMessageQueue = new Queue<SQLLogMessage>();
/// <summary>
/// 处理定时器
/// </summary>
private static System.Timers.Timer timer = null;
/// <summary>
/// 消息中心停止通知
/// </summary>
private static MessageCenterStopCallBack stopCallBack = null;
/// <summary>
/// 启动日志记录服务
/// </summary>
public static void Start()
{
if (timer == null)
{
timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = SQLLogFactory.Config.LogPeriod;//日志保存周期,三分钟
timer.Start();
if (stopCallBack == null)
{
stopCallBack = new MessageCenterStopCallBack(stopNotify);
MessageCenterManage.Register(typeof(SQLLogService).FullName, stopCallBack);
}
}
}
/// <summary>
/// 回调处理函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
List<SQLLogMessage> tempList = null;
lock (syncObj)
{
try
{
Int32 count = logMessageQueue.Count;
if (count > 0)
{
tempList = new List<SQLLogMessage>();
for (int i = 0; i < count; i++)
{
tempList.Add(logMessageQueue.Dequeue());
}
}
}
catch
{
}
}
if (tempList != null && tempList.Count > 0)
{
lock (writeLock)
{
try
{
Dictionary<string, StreamWriter> dic = new Dictionary<string, StreamWriter>();
string filePath = string.Format("{0}\\SQLLog\\{1}\\",SQLLogFactory.Config.LogPath, DateTime.Now.ToString("yyyyMMdd"));
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
//开始记录日志数据
for (int i = 0; i < tempList.Count; i++)
{
SQLLogMessage logMessage = tempList[i];
string fileFullName = string.Format("{0}{1}", filePath, logMessage.FileName);
if (dic.ContainsKey(fileFullName))
{
StreamWriter sw = dic[fileFullName];
sw.WriteLine(logMessage.Message);
}
else
{
StreamWriter sw = new StreamWriter(fileFullName, true);
dic[fileFullName] = sw;
sw.WriteLine(logMessage.Message);
}
}
foreach (var item in dic.Keys)
{
StreamWriter sw = dic[item];
sw.Flush();
sw.Close();
}
dic.Clear();
}
catch
{
}
}
}
}
/// <summary>
/// 停止日志记录服务
/// </summary>
public static void Stop()
{
List<SQLLogMessage> tempList = null;
lock (syncObj)
{
Int32 count = logMessageQueue.Count;
if (count > 0)
{
tempList = new List<SQLLogMessage>();
for (int i = 0; i < count; i++)
{
tempList.Add(logMessageQueue.Dequeue());
}
}
}
if (tempList != null && tempList.Count > 0)
{
lock (writeLock)
{
try
{
Dictionary<string, StreamWriter> dic = new Dictionary<string, StreamWriter>();
string filePath = string.Format("{0}\\SQLLog\\{1}\\", SQLLogFactory.Config.LogPath, DateTime.Now.ToString("yyyyMMdd"));
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
//开始记录日志数据
for (int i = 0; i < tempList.Count; i++)
{
SQLLogMessage logMessage = tempList[i];
string fileFullName = string.Format("{0}{1}", filePath, logMessage.FileName);
if (dic.ContainsKey(fileFullName))
{
StreamWriter sw = dic[fileFullName];
sw.WriteLine(logMessage.Message);
}
else
{
StreamWriter sw = new StreamWriter(fileFullName, true);
dic[fileFullName] = sw;
sw.WriteLine(logMessage.Message);
}
}
foreach (var item in dic.Keys)
{
StreamWriter sw = dic[item];
sw.WriteLine("停止日志记录");
sw.Flush();
sw.Close();
}
dic.Clear();
}
catch
{
}
}
}
if (timer != null)
{
if (timer.Enabled == true)
{
timer.Stop();
}
timer = null;
}
}
/// <summary>
/// 要记录的日志数据压队队列
/// </summary>
/// <param name="logType"></param>
/// <param name="message"></param>
public static void LogSystem(string header,string message)
{
try
{
SQLLogMessage logMessage = new SQLLogMessage();
logMessage.FileName = string.Format("{0}.Log", header);
logMessage.Message = string.Format("{0} {1}", DateTime.Now.ToString(), message);
lock (syncObj)
{
logMessageQueue.Enqueue(logMessage);
}
if (timer == null)
{
timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = SQLLogFactory.Config.LogPeriod;//日志保存周期,三分钟
timer.Start();
if (stopCallBack == null)
{
stopCallBack = new MessageCenterStopCallBack(stopNotify);
MessageCenterManage.Register(typeof(SQLLogService).FullName, stopCallBack);
}
}
else if (timer.Enabled == false)
{
timer.Start();
if (stopCallBack == null)
{
stopCallBack = new MessageCenterStopCallBack(stopNotify);
MessageCenterManage.Register(typeof(SQLLogService).FullName, stopCallBack);
}
}
}
catch
{
}
}
/// <summary>
/// 获取消息中心的通知
/// </summary>
/// <param name="signal"></param>
private static void stopNotify(bool signal)
{
if (signal == true)
{
Stop();
}
}
/// <summary>
/// 获取日志记录服务的状态
/// </summary>
/// <returns></returns>
public static bool GetLogServiceStatus()
{
if (timer == null)
{
return false;
}
else
{
if (timer.Enabled == true)
{
return true;
}
else
{
return false;
}
}
}
}