You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

344 lines
9.7 KiB

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<LogWriteThread> , IDisposable
{
/// <summary>
/// 写队列
/// 线程安全锁,注意,log本身是IO操作,比较费时间
/// 优化:开启单独线程写log,其他线程写log先写到内存队列,写log线程取出写磁盘
/// 这么做唯一的缺点是如果程序core了,有可能部分log写磁盘不成功,不过C#不容易整个进程挂掉,似乎可行
/// </summary>
private Queue<LogWriteRecord> m_logQueue = new Queue<LogWriteRecord>();
//写文件的cache
//filePathName ,LogFileCache
private Dictionary<string, LogFileCache> m_fileCache = new Dictionary<string, LogFileCache>();
private Thread m_writeThread;
private long m_lastCheckTimeoutTime;
private bool m_isClosed = false;
private object locker = new object();
/// <summary>
/// 等所有文件写完后释放
/// </summary>
public void Dispose()
{
ForceClose();
}
/// <summary>
/// 开启
/// </summary>
public void Start()
{
m_isClosed = false;
if (m_writeThread == null)
{
m_writeThread = new Thread(this.WorkThreadFun);
//设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用
m_writeThread.IsBackground = true;
m_writeThread.Start();
}
}
/// <summary>
/// 强制关闭,一般用不到
/// </summary>
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();
}
}
/// <summary>
/// 打开文件,有可能失败
/// </summary>
/// <param name="fileCache"></param>
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;
}
}
}
/// <summary>
/// shift to multi file
/// 滚动文件
/// </summary>
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);
}
}
/// <summary>
/// 关闭超时文件
/// </summary>
private void TickCloseTimeoutFile()
{
long nowSecond = AppTime.GetNowSysSecond();
//一分钟检查一次
if(nowSecond - m_lastCheckTimeoutTime < 60)
{
return;
}
m_lastCheckTimeoutTime = nowSecond;
List<string> needCloseFile = new List<string>();
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();
}
}
}