using HslCommunication.Core; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; namespace HslCommunication.LogNet { /// /// 日志存储类的基类,提供一些基础的服务 /// public abstract class LogNetBase : IDisposable { #region 私有字段 private HslMessageDegree m_messageDegree = HslMessageDegree.DEBUG; private Queue m_WaitForSave = new Queue(); private SimpleHybirdLock m_simpleHybirdLock = new SimpleHybirdLock(); /// /// 文件读写锁 /// protected SimpleHybirdLock m_fileSaveLock = new SimpleHybirdLock(); private int m_SaveStatus = 0; #endregion #region 事件存储处 /// /// 在存储到文件的时候将会触发的事件 /// public event EventHandler BeforeSaveToFile = null; private void OnBeforeSaveToFile(HslEventArgs args) { BeforeSaveToFile?.Invoke(this, args); } #endregion #region 公开的属性 /// /// 日志存储模式,1:单文件,2:按大小存储,3:按时间存储 /// public int LogSaveMode { get; protected set; } #endregion #region 消息记录方法 /// /// 写入一条调试信息 /// /// public void WriteDebug(string text) { RecordMessage(HslMessageDegree.DEBUG, text); } /// /// 写入一条调试信息 /// /// 关键字 /// 文本内容 public void WriteDebug(string keyWord, string text) { if (string.IsNullOrEmpty(keyWord)) { WriteDebug(text); } else { WriteDebug(keyWord + " : " + text); } } /// /// 写入一条普通信息 /// /// 文本内容 public void WriteInfo(string text) { RecordMessage(HslMessageDegree.INFO, text); } /// /// 写入一条普通信息 /// /// 关键字 /// 文本内容 public void WriteInfo(string keyWord, string text) { if (string.IsNullOrEmpty(keyWord)) { WriteInfo(text); } else { WriteInfo(keyWord + " : " + text); } } /// /// 写入一条警告信息 /// /// 文本内容 public void WriteWarn(string text) { RecordMessage(HslMessageDegree.WARN, text); } /// /// 写入一条警告信息 /// /// 关键字 /// 文本内容 public void WriteWarn(string keyWord, string text) { if (string.IsNullOrEmpty(keyWord)) { WriteWarn(text); } else { WriteWarn(keyWord + " : " + text); } } /// /// 写入一条错误消息 /// /// 文本内容 public void WriteError(string text) { RecordMessage(HslMessageDegree.ERROR, text); } /// /// 写入一条错误消息 /// /// 关键字 /// 文本内容 public void WriteError(string keyWord, string text) { if (string.IsNullOrEmpty(keyWord)) { WriteError(text); } else { WriteError(keyWord + " : " + text); } } /// /// 写入一条致命错误信息 /// /// 文本内容 public void WriteFatal(string text) { RecordMessage(HslMessageDegree.FATAL, text); } /// /// 写入一条致命错误信息 /// /// 关键字 /// 文本内容 public void WriteFatal(string keyWord, string text) { if (string.IsNullOrEmpty(keyWord)) { WriteFatal(text); } else { WriteFatal(keyWord + " : " + text); } } /// /// 写入一条异常信息 /// /// 关键字 /// public void WriteException(string keyWord, Exception ex) { RecordMessage(HslMessageDegree.FATAL, LogNetManagment.GetSaveStringFromException(keyWord, ex)); } /// /// 写入一条异常信息 /// /// 关键字 /// 内容 /// 异常 public void WriteException(string keyWord, string text, Exception ex) { if (string.IsNullOrEmpty(keyWord)) { WriteException(text, ex); } else { WriteException(keyWord + " : " + text, ex); } } /// /// 记录一条自定义的消息 /// /// /// public void RecordMessage(HslMessageDegree degree, string text) { WriteToFile(degree, text); } /// /// 写入一条解释性的消息,不需要带有回车键 /// /// public void WriteDescrition(string description) { if (string.IsNullOrEmpty(description)) return; // 和上面的文本之间追加一行空行 StringBuilder stringBuilder = new StringBuilder("\u0002"); stringBuilder.Append(Environment.NewLine); stringBuilder.Append("\u0002/"); int count = 118 - CalculateStringOccupyLength(description); if (count >= 8) { int count_1 = (count - 8) / 2; AppendCharToStringBuilder(stringBuilder, '*', count_1); stringBuilder.Append(" "); stringBuilder.Append(description); stringBuilder.Append(" "); if (count % 2 == 0) { AppendCharToStringBuilder(stringBuilder, '*', count_1); } else { AppendCharToStringBuilder(stringBuilder, '*', count_1 + 1); } } else if (count >= 2) { int count_1 = (count - 2) / 2; AppendCharToStringBuilder(stringBuilder, '*', count_1); stringBuilder.Append(description); if (count % 2 == 0) { AppendCharToStringBuilder(stringBuilder, '*', count_1); } else { AppendCharToStringBuilder(stringBuilder, '*', count_1 + 1); } } else { stringBuilder.Append(description); } stringBuilder.Append("/"); stringBuilder.Append(Environment.NewLine); ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), stringBuilder.ToString()); } /// /// 写入一条换行符 /// public void WriteNewLine() { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), "\u0002" + Environment.NewLine); } /// /// 设置日志的存储等级,高于该等级的才会被存储 /// /// public void SetMessageDegree(HslMessageDegree degree) { m_messageDegree = degree; } #endregion #region 文件读写块 private void WriteToFile(HslMessageDegree degree, string text) { // 过滤事件 if (degree <= m_messageDegree) { // 需要记录数据 HslMessageItem item = GetHslMessageItem(degree, text); AddItemToCache(item); } } private void AddItemToCache(HslMessageItem item) { m_simpleHybirdLock.Enter(); m_WaitForSave.Enqueue(item); m_simpleHybirdLock.Leave(); StartSaveFile(); } private void StartSaveFile() { if (Interlocked.CompareExchange(ref m_SaveStatus, 1, 0) == 0) { //启动存储 ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveFile), null); } } private HslMessageItem GetAndRemoveLogItem() { HslMessageItem result = null; m_simpleHybirdLock.Enter(); result = m_WaitForSave.Count > 0 ? m_WaitForSave.Dequeue() : null; m_simpleHybirdLock.Leave(); return result; } private void ThreadPoolSaveFile(object obj) { // 获取需要存储的日志 HslMessageItem current = GetAndRemoveLogItem(); // 进入文件操作的锁 m_fileSaveLock.Enter(); // 获取要存储的文件名称 string LogSaveFileName = GetFileSaveName(); if (!string.IsNullOrEmpty(LogSaveFileName)) { // 保存 StreamWriter sw = null; try { sw = new StreamWriter(LogSaveFileName, true, Encoding.UTF8); while (current != null) { // 触发事件 OnBeforeSaveToFile( new HslEventArgs( ) { HslMessage = current } ); sw.Write(HslMessageFormate(current)); sw.Write(Environment.NewLine); current = GetAndRemoveLogItem(); } } catch (Exception ex) { AddItemToCache(current); AddItemToCache(new HslMessageItem() { Degree = HslMessageDegree.FATAL, Text = LogNetManagment.GetSaveStringFromException("LogNetSelf", ex), }); } finally { sw?.Dispose(); } } // 释放锁 m_fileSaveLock.Leave(); Interlocked.Exchange(ref m_SaveStatus, 0); // 再次检测锁是否释放完成 if (m_WaitForSave.Count > 0) { StartSaveFile(); } } private string HslMessageFormate(HslMessageItem hslMessage) { StringBuilder stringBuilder = new StringBuilder("\u0002"); stringBuilder.Append("["); stringBuilder.Append(LogNetManagment.GetDegreeDescription(hslMessage.Degree)); stringBuilder.Append("] "); stringBuilder.Append(hslMessage.Time.ToString("yyyy-MM-dd HH:mm:ss.fff")); stringBuilder.Append(" thread:["); stringBuilder.Append(hslMessage.ThreadId.ToString("D2")); stringBuilder.Append("] "); stringBuilder.Append(hslMessage.Text); return stringBuilder.ToString(); } private void ThreadPoolSaveText(object obj) { // 进入文件操作的锁 m_fileSaveLock.Enter(); //获取要存储的文件名称 string LogSaveFileName = GetFileSaveName(); if (!string.IsNullOrEmpty(LogSaveFileName)) { // 保存 StreamWriter sw = null; try { sw = new StreamWriter(LogSaveFileName, true, Encoding.UTF8); string str = obj as string; sw.Write(str); } catch (Exception ex) { AddItemToCache(new HslMessageItem() { Degree = HslMessageDegree.FATAL, Text = LogNetManagment.GetSaveStringFromException("LogNetSelf", ex), }); } finally { sw?.Dispose(); } } // 释放锁 m_fileSaveLock.Leave(); } #endregion #region 辅助方法 /// /// 获取要存储的文件的名称 /// /// protected virtual string GetFileSaveName() { return string.Empty; } /// /// 返回检查的路径名称,将会包含反斜杠 /// /// /// protected string CheckPathEndWithSprit(string filePath) { if (!string.IsNullOrEmpty(filePath)) { if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } if (!filePath.EndsWith(@"\")) { return filePath + @"\"; } } return filePath; } private HslMessageItem GetHslMessageItem(HslMessageDegree degree, string text) { return new HslMessageItem() { Degree = degree, Text = text, ThreadId = Thread.CurrentThread.ManagedThreadId, Time = DateTime.Now, }; } private int CalculateStringOccupyLength(string str) { if (string.IsNullOrEmpty(str)) return 0; int result = 0; for (int i = 0; i < str.Length; i++) { if (str[i] >= 0x4e00 && str[i] <= 0x9fbb) { result += 2; } else { result += 1; } } return result; } private void AppendCharToStringBuilder(StringBuilder sb, char c, int count) { for (int i = 0; i < count; i++) { sb.Append(c); } } #endregion #region IDisposable Support private bool disposedValue = false; // 要检测冗余调用 /// /// 释放资源 /// /// protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 释放托管状态(托管对象)。 m_simpleHybirdLock.Dispose(); m_WaitForSave.Clear(); m_fileSaveLock.Dispose(); } // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。 // TODO: 将大型字段设置为 null。 m_simpleHybirdLock = null; m_WaitForSave = null; m_fileSaveLock = null; disposedValue = true; } } // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。 // ~LogNetBase() { // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 // Dispose(false); // } // 添加此代码以正确实现可处置模式。 /// /// 释放资源 /// public void Dispose() { // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。 // GC.SuppressFinalize(this); } #endregion } }