using HslCommunication.Core; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; namespace HslCommunication.LogNet { /// <summary> /// 日志存储类的基类,提供一些基础的服务 /// </summary> /// <remarks> /// 基于此类可以实现任意的规则的日志存储规则,欢迎大家补充实现,本组件实现了3个日志类 /// <list type="number"> /// <item>单文件日志类 <see cref="LogNetSingle"/></item> /// <item>根据文件大小的类 <see cref="LogNetFileSize"/></item> /// <item>根据时间进行存储的类 <see cref="LogNetDateTime"/></item> /// </list> /// </remarks> public abstract class LogNetBase : IDisposable { #region Constructor /// <summary> /// 实例化一个日志对象 /// </summary> public LogNetBase() { m_fileSaveLock = new SimpleHybirdLock(); m_simpleHybirdLock = new SimpleHybirdLock(); m_WaitForSave = new Queue<HslMessageItem>(); filtrateKeyword = new List<string>(); filtrateLock = new SimpleHybirdLock(); } #endregion #region Private Member private HslMessageDegree m_messageDegree = HslMessageDegree.DEBUG; // 默认的存储规则 private Queue<HslMessageItem> m_WaitForSave; // 待存储数据的缓存 private SimpleHybirdLock m_simpleHybirdLock; // 缓存列表的锁 private int m_SaveStatus = 0; // 存储状态 private List<string> filtrateKeyword; // 需要过滤的存储对象 private SimpleHybirdLock filtrateLock; // 过滤列表的锁 #endregion #region Protect Member /// <summary> /// 文件存储的锁 /// </summary> protected SimpleHybirdLock m_fileSaveLock; // 文件的锁 #endregion #region Event Handle /// <summary> /// 在存储到文件的时候将会触发的事件 /// </summary> public event EventHandler<HslEventArgs> BeforeSaveToFile = null; private void OnBeforeSaveToFile(HslEventArgs args) { BeforeSaveToFile?.Invoke(this, args); } #endregion #region Public Member /// <summary> /// 日志存储模式,1:单文件,2:按大小存储,3:按时间存储 /// </summary> public int LogSaveMode { get; protected set; } #endregion #region Log Method /// <summary> /// 写入一条调试信息 /// </summary> /// <param name="text"></param> public void WriteDebug(string text) { WriteDebug(string.Empty, text); } /// <summary> /// 写入一条调试信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">文本内容</param> public void WriteDebug(string keyWord, string text) { RecordMessage(HslMessageDegree.DEBUG, keyWord, text); } /// <summary> /// 写入一条普通信息 /// </summary> /// <param name="text">文本内容</param> public void WriteInfo(string text) { WriteInfo(string.Empty, text); } /// <summary> /// 写入一条普通信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">文本内容</param> public void WriteInfo(string keyWord, string text) { RecordMessage(HslMessageDegree.INFO, keyWord, text); } /// <summary> /// 写入一条警告信息 /// </summary> /// <param name="text">文本内容</param> public void WriteWarn(string text) { WriteWarn(string.Empty, text); } /// <summary> /// 写入一条警告信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">文本内容</param> public void WriteWarn(string keyWord, string text) { RecordMessage(HslMessageDegree.WARN, keyWord, text); } /// <summary> /// 写入一条错误消息 /// </summary> /// <param name="text">文本内容</param> public void WriteError(string text) { WriteError(string.Empty, text); } /// <summary> /// 写入一条错误消息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">文本内容</param> public void WriteError(string keyWord, string text) { RecordMessage(HslMessageDegree.ERROR, keyWord, text); } /// <summary> /// 写入一条致命错误信息 /// </summary> /// <param name="text">文本内容</param> public void WriteFatal(string text) { WriteFatal(string.Empty, text); } /// <summary> /// 写入一条致命错误信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">文本内容</param> public void WriteFatal(string keyWord, string text) { RecordMessage(HslMessageDegree.FATAL, keyWord, text); } /// <summary> /// 写入一条异常信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="ex">异常信息</param> public void WriteException(string keyWord, Exception ex) { WriteException(keyWord, string.Empty, ex); } /// <summary> /// 写入一条异常信息 /// </summary> /// <param name="keyWord">关键字</param> /// <param name="text">内容</param> /// <param name="ex">异常</param> public void WriteException(string keyWord, string text, Exception ex) { RecordMessage(HslMessageDegree.FATAL, keyWord, LogNetManagment.GetSaveStringFromException(text, ex)); } /// <summary> /// 记录一条自定义的消息 /// </summary> /// <param name="degree">消息的等级</param> /// <param name="keyWord">关键字</param> /// <param name="text">文本</param> public void RecordMessage(HslMessageDegree degree, string keyWord, string text) { WriteToFile(degree, keyWord, text); } /// <summary> /// 写入一条解释性的消息,不需要带有回车键 /// </summary> /// <param name="description">解释性的文本</param> 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); RecordMessage(HslMessageDegree.None, string.Empty, stringBuilder.ToString()); //ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), stringBuilder.ToString()); } /// <summary> /// 写入一条任意字符 /// </summary> /// <param name="text">内容</param> public void WriteAnyString(string text) { RecordMessage(HslMessageDegree.None, string.Empty, text); } /// <summary> /// 写入一条换行符 /// </summary> public void WriteNewLine() { RecordMessage(HslMessageDegree.None, string.Empty, "\u0002" + Environment.NewLine); //ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), "\u0002" + Environment.NewLine); } /// <summary> /// 设置日志的存储等级,高于该等级的才会被存储 /// </summary> /// <param name="degree">消息等级</param> public void SetMessageDegree(HslMessageDegree degree) { m_messageDegree = degree; } #endregion #region Filtrate Keyword /// <summary> /// 过滤指定的关键字存储 /// </summary> /// <param name="keyWord">关键字</param> public void FiltrateKeyword(string keyWord) { filtrateLock.Enter(); if (!filtrateKeyword.Contains(keyWord)) { filtrateKeyword.Add(keyWord); } filtrateLock.Leave(); } #endregion #region File Write private void WriteToFile(HslMessageDegree degree, string keyWord, string text) { // 过滤事件 if (degree <= m_messageDegree) { // 需要记录数据 HslMessageItem item = GetHslMessageItem(degree, keyWord, 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 }); // 检查是否需要真的进行存储 bool isSave = true; filtrateLock.Enter(); isSave = !filtrateKeyword.Contains(current.KeyWord); filtrateLock.Leave(); // 检查是否被设置为强制不存储 if (current.Cancel) isSave = false; // 如果需要存储的就过滤掉 if (isSave) { 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(); if (hslMessage.Degree != HslMessageDegree.None) { stringBuilder.Append("\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("] "); if (!string.IsNullOrEmpty(hslMessage.KeyWord)) { stringBuilder.Append(hslMessage.KeyWord); 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 Helper Method /// <summary> /// 获取要存储的文件的名称 /// </summary> /// <returns>完整的文件路径信息,带文件名</returns> protected virtual string GetFileSaveName() { return string.Empty; } /// <summary> /// 返回检查的路径名称,将会包含反斜杠 /// </summary> /// <param name="filePath">路径信息</param> /// <returns>检查后的结果对象</returns> 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 keyWord, string text) { return new HslMessageItem() { KeyWord = keyWord, Degree = degree, Text = text, ThreadId = Thread.CurrentThread.ManagedThreadId, }; } 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; // 要检测冗余调用 /// <summary> /// 释放资源 /// </summary> /// <param name="disposing">是否初次调用</param> protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 释放托管状态(托管对象)。 m_simpleHybirdLock.Dispose(); m_WaitForSave.Clear(); m_fileSaveLock.Dispose(); } // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。 // TODO: 将大型字段设置为 null。 disposedValue = true; } } // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。 // ~LogNetBase() { // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 // Dispose(false); // } // 添加此代码以正确实现可处置模式。 /// <summary> /// 释放资源 /// </summary> public void Dispose() { // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。 // GC.SuppressFinalize(this); } #endregion } }