using System; using System.Collections.Generic; using System.Threading; using System.IO; namespace Sog.Log { internal class LogWriteRecord { public string FilePathName; public string LogMessage; public bool ShiftFile; } internal class LogFileCache { public bool OpenFailed; public string FilePathName; public long FileLength; public long LastWriteTime; public StreamWriter Writer; } internal class LogWriteThread : Singleton , IDisposable { /// /// 写队列 /// 线程安全锁,注意,log本身是IO操作,比较费时间 /// 优化:开启单独线程写log,其他线程写log先写到内存队列,写log线程取出写磁盘 /// 这么做唯一的缺点是如果程序core了,有可能部分log写磁盘不成功,不过C#不容易整个进程挂掉,似乎可行 /// private Queue m_logQueue = new Queue(); //写文件的cache //filePathName ,LogFileCache private Dictionary m_fileCache = new Dictionary(); private Thread m_writeThread; private long m_lastCheckTimeoutTime; private bool m_isClosed = false; private object locker = new object(); /// /// 等所有文件写完后释放 /// public void Dispose() { ForceClose(); } /// /// 开启 /// public void Start() { m_isClosed = false; if (m_writeThread == null) { m_writeThread = new Thread(this.WorkThreadFun); //设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用 m_writeThread.IsBackground = true; m_writeThread.Start(); } } /// /// 强制关闭,一般用不到 /// public void ForceClose() { m_isClosed = true; if (m_writeThread == null) { return; } while(m_writeThread.IsAlive) { Thread.Sleep(1); } m_writeThread = null; WriteAllLogInQueue(); CloseAllFile(); } public void WriteLog(string filepathname, string message, bool shiftFile) { //有可能没开启 //有可能关闭了 if(m_isClosed) { return; } LogWriteRecord record = new LogWriteRecord(); record.FilePathName = filepathname; record.LogMessage = message; record.ShiftFile = shiftFile; lock (locker) { m_logQueue.Enqueue(record); } } private void WorkThreadFun() { while (!m_isClosed) { try { //写文件 int iCount = WriteAllLogInQueue(); if(iCount == 0) { Thread.Sleep(10); //continue; } //关闭长时间不写的文件 TickCloseTimeoutFile(); } catch (Exception) { } } } private int WriteAllLogInQueue() { int writeRecordCount = 0; while(m_logQueue.Count > 0) { LogWriteRecord record; lock (locker) { record = m_logQueue.Dequeue(); } writeRecordCount++; WriteLogRecord(record); } return writeRecordCount; } private void WriteLogRecord(LogWriteRecord record) { LogFileCache fileCache; if(false == m_fileCache.TryGetValue(record.FilePathName, out fileCache)) { fileCache = new LogFileCache(); fileCache.FilePathName = record.FilePathName; fileCache.FileLength = 0; fileCache.OpenFailed = false; m_fileCache.Add(record.FilePathName, fileCache); } OpenFile(fileCache); if(record.ShiftFile) { ShiftFile(fileCache); } if(fileCache.Writer != null) { fileCache.Writer.Write(record.LogMessage); fileCache.FileLength += record.LogMessage.Length; //flush可以优化吗 fileCache.Writer.Flush(); fileCache.LastWriteTime = AppTime.GetNowSysSecond(); } } /// /// 打开文件,有可能失败 /// /// private void OpenFile(LogFileCache fileCache) { if (fileCache.OpenFailed == false && fileCache.Writer == null) { if (! File.Exists(fileCache.FilePathName)) { // Create a file to write to. using (File.Create(fileCache.FilePathName)) { } } FileStream fileStream = new FileStream(fileCache.FilePathName, FileMode.Append, FileAccess.Write, FileShare.Read); fileCache.Writer = new StreamWriter(fileStream); FileInfo fileinfo = new FileInfo(fileCache.FilePathName); fileCache.FileLength = fileinfo.Length; if (fileCache.Writer == null) { fileCache.OpenFailed = true; } } } /// /// shift to multi file /// 滚动文件 /// private void ShiftFile(LogFileCache fileCache) { if (fileCache.OpenFailed == true || fileCache.Writer == null) { return; } //为0表示关闭shift功能 if (LogDefaultParam.ShiftFileSize == 0) { return; } if (fileCache.FileLength > LogDefaultParam.ShiftFileSize) { //先关闭文件 fileCache.Writer.Dispose(); fileCache.Writer = null; // try { for (int i = LogDefaultParam.ShiftFileCount; i > 0; i--) { string shiftDst = fileCache.FilePathName; if (i > 0 && i < 10) { shiftDst += ".0" + i.ToString(); } else { shiftDst += "." + i.ToString(); } string shiftSrc = fileCache.FilePathName; int srcIndex = i - 1; if (srcIndex > 0 && srcIndex < 10) { shiftSrc += ".0" + srcIndex.ToString(); } else if(srcIndex >= 10) { shiftSrc += "." + srcIndex.ToString(); } if (File.Exists(shiftSrc)) { if (File.Exists(shiftDst)) { File.Delete(shiftDst); } File.Move(shiftSrc, shiftDst); } } } catch { } fileCache.FileLength = 0; OpenFile(fileCache); } } /// /// 关闭超时文件 /// private void TickCloseTimeoutFile() { long nowSecond = AppTime.GetNowSysSecond(); //一分钟检查一次 if(nowSecond - m_lastCheckTimeoutTime < 60) { return; } m_lastCheckTimeoutTime = nowSecond; List needCloseFile = new List(); foreach(var item in m_fileCache.Values) { //5分钟超时 if (nowSecond - item.LastWriteTime >= LogDefaultParam.CloseTimeoutLogFileTime) { if (item.Writer != null) { item.Writer.Flush(); item.Writer.Dispose(); } item.Writer = null; needCloseFile.Add(item.FilePathName); } } if(needCloseFile.Count > 0) { foreach(var filename in needCloseFile) { m_fileCache.Remove(filename); } } } private void CloseAllFile() { foreach (var item in m_fileCache.Values) { if (item.Writer != null) { item.Writer.Flush(); item.Writer.Dispose(); item.Writer = null; } } m_fileCache.Clear(); } } }