LogBase.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. using HslCommunication.Core;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. namespace HslCommunication.LogNet
  9. {
  10. /// <summary>
  11. /// 日志存储类的基类,提供一些基础的服务
  12. /// </summary>
  13. public abstract class LogNetBase : IDisposable
  14. {
  15. #region 私有字段
  16. private HslMessageDegree m_messageDegree = HslMessageDegree.DEBUG;
  17. private Queue<HslMessageItem> m_WaitForSave = new Queue<HslMessageItem>();
  18. private SimpleHybirdLock m_simpleHybirdLock = new SimpleHybirdLock();
  19. /// <summary>
  20. /// 文件读写锁
  21. /// </summary>
  22. protected SimpleHybirdLock m_fileSaveLock = new SimpleHybirdLock();
  23. private int m_SaveStatus = 0;
  24. #endregion
  25. #region 事件存储处
  26. /// <summary>
  27. /// 在存储到文件的时候将会触发的事件
  28. /// </summary>
  29. public event EventHandler<HslEventArgs> BeforeSaveToFile = null;
  30. private void OnBeforeSaveToFile(HslEventArgs args)
  31. {
  32. BeforeSaveToFile?.Invoke(this, args);
  33. }
  34. #endregion
  35. #region 公开的属性
  36. /// <summary>
  37. /// 日志存储模式,1:单文件,2:按大小存储,3:按时间存储
  38. /// </summary>
  39. public int LogSaveMode { get; protected set; }
  40. #endregion
  41. #region 消息记录方法
  42. /// <summary>
  43. /// 写入一条调试信息
  44. /// </summary>
  45. /// <param name="text"></param>
  46. public void WriteDebug(string text)
  47. {
  48. RecordMessage(HslMessageDegree.DEBUG, text);
  49. }
  50. /// <summary>
  51. /// 写入一条调试信息
  52. /// </summary>
  53. /// <param name="keyWord">关键字</param>
  54. /// <param name="text">文本内容</param>
  55. public void WriteDebug(string keyWord, string text)
  56. {
  57. if (string.IsNullOrEmpty(keyWord))
  58. {
  59. WriteDebug(text);
  60. }
  61. else
  62. {
  63. WriteDebug(keyWord + " : " + text);
  64. }
  65. }
  66. /// <summary>
  67. /// 写入一条普通信息
  68. /// </summary>
  69. /// <param name="text">文本内容</param>
  70. public void WriteInfo(string text)
  71. {
  72. RecordMessage(HslMessageDegree.INFO, text);
  73. }
  74. /// <summary>
  75. /// 写入一条普通信息
  76. /// </summary>
  77. /// <param name="keyWord">关键字</param>
  78. /// <param name="text">文本内容</param>
  79. public void WriteInfo(string keyWord, string text)
  80. {
  81. if (string.IsNullOrEmpty(keyWord))
  82. {
  83. WriteInfo(text);
  84. }
  85. else
  86. {
  87. WriteInfo(keyWord + " : " + text);
  88. }
  89. }
  90. /// <summary>
  91. /// 写入一条警告信息
  92. /// </summary>
  93. /// <param name="text">文本内容</param>
  94. public void WriteWarn(string text)
  95. {
  96. RecordMessage(HslMessageDegree.WARN, text);
  97. }
  98. /// <summary>
  99. /// 写入一条警告信息
  100. /// </summary>
  101. /// <param name="keyWord">关键字</param>
  102. /// <param name="text">文本内容</param>
  103. public void WriteWarn(string keyWord, string text)
  104. {
  105. if (string.IsNullOrEmpty(keyWord))
  106. {
  107. WriteWarn(text);
  108. }
  109. else
  110. {
  111. WriteWarn(keyWord + " : " + text);
  112. }
  113. }
  114. /// <summary>
  115. /// 写入一条错误消息
  116. /// </summary>
  117. /// <param name="text">文本内容</param>
  118. public void WriteError(string text)
  119. {
  120. RecordMessage(HslMessageDegree.ERROR, text);
  121. }
  122. /// <summary>
  123. /// 写入一条错误消息
  124. /// </summary>
  125. /// <param name="keyWord">关键字</param>
  126. /// <param name="text">文本内容</param>
  127. public void WriteError(string keyWord, string text)
  128. {
  129. if (string.IsNullOrEmpty(keyWord))
  130. {
  131. WriteError(text);
  132. }
  133. else
  134. {
  135. WriteError(keyWord + " : " + text);
  136. }
  137. }
  138. /// <summary>
  139. /// 写入一条致命错误信息
  140. /// </summary>
  141. /// <param name="text">文本内容</param>
  142. public void WriteFatal(string text)
  143. {
  144. RecordMessage(HslMessageDegree.FATAL, text);
  145. }
  146. /// <summary>
  147. /// 写入一条致命错误信息
  148. /// </summary>
  149. /// <param name="keyWord">关键字</param>
  150. /// <param name="text">文本内容</param>
  151. public void WriteFatal(string keyWord, string text)
  152. {
  153. if (string.IsNullOrEmpty(keyWord))
  154. {
  155. WriteFatal(text);
  156. }
  157. else
  158. {
  159. WriteFatal(keyWord + " : " + text);
  160. }
  161. }
  162. /// <summary>
  163. /// 写入一条异常信息
  164. /// </summary>
  165. /// <param name="keyWord">关键字</param>
  166. /// <param name="ex"></param>
  167. public void WriteException(string keyWord, Exception ex)
  168. {
  169. RecordMessage(HslMessageDegree.FATAL, LogNetManagment.GetSaveStringFromException(keyWord, ex));
  170. }
  171. /// <summary>
  172. /// 写入一条异常信息
  173. /// </summary>
  174. /// <param name="keyWord">关键字</param>
  175. /// <param name="text">内容</param>
  176. /// <param name="ex">异常</param>
  177. public void WriteException(string keyWord, string text, Exception ex)
  178. {
  179. if (string.IsNullOrEmpty(keyWord))
  180. {
  181. WriteException(text, ex);
  182. }
  183. else
  184. {
  185. WriteException(keyWord + " : " + text, ex);
  186. }
  187. }
  188. /// <summary>
  189. /// 记录一条自定义的消息
  190. /// </summary>
  191. /// <param name="degree"></param>
  192. /// <param name="text"></param>
  193. public void RecordMessage(HslMessageDegree degree, string text)
  194. {
  195. WriteToFile(degree, text);
  196. }
  197. /// <summary>
  198. /// 写入一条解释性的消息,不需要带有回车键
  199. /// </summary>
  200. /// <param name="description"></param>
  201. public void WriteDescrition(string description)
  202. {
  203. if (string.IsNullOrEmpty(description)) return;
  204. // 和上面的文本之间追加一行空行
  205. StringBuilder stringBuilder = new StringBuilder("\u0002");
  206. stringBuilder.Append(Environment.NewLine);
  207. stringBuilder.Append("\u0002/");
  208. int count = 118 - CalculateStringOccupyLength(description);
  209. if (count >= 8)
  210. {
  211. int count_1 = (count - 8) / 2;
  212. AppendCharToStringBuilder(stringBuilder, '*', count_1);
  213. stringBuilder.Append(" ");
  214. stringBuilder.Append(description);
  215. stringBuilder.Append(" ");
  216. if (count % 2 == 0)
  217. {
  218. AppendCharToStringBuilder(stringBuilder, '*', count_1);
  219. }
  220. else
  221. {
  222. AppendCharToStringBuilder(stringBuilder, '*', count_1 + 1);
  223. }
  224. }
  225. else if (count >= 2)
  226. {
  227. int count_1 = (count - 2) / 2;
  228. AppendCharToStringBuilder(stringBuilder, '*', count_1);
  229. stringBuilder.Append(description);
  230. if (count % 2 == 0)
  231. {
  232. AppendCharToStringBuilder(stringBuilder, '*', count_1);
  233. }
  234. else
  235. {
  236. AppendCharToStringBuilder(stringBuilder, '*', count_1 + 1);
  237. }
  238. }
  239. else
  240. {
  241. stringBuilder.Append(description);
  242. }
  243. stringBuilder.Append("/");
  244. stringBuilder.Append(Environment.NewLine);
  245. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), stringBuilder.ToString());
  246. }
  247. /// <summary>
  248. /// 写入一条换行符
  249. /// </summary>
  250. public void WriteNewLine()
  251. {
  252. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveText), "\u0002" + Environment.NewLine);
  253. }
  254. /// <summary>
  255. /// 设置日志的存储等级,高于该等级的才会被存储
  256. /// </summary>
  257. /// <param name="degree"></param>
  258. public void SetMessageDegree(HslMessageDegree degree)
  259. {
  260. m_messageDegree = degree;
  261. }
  262. #endregion
  263. #region 文件读写块
  264. private void WriteToFile(HslMessageDegree degree, string text)
  265. {
  266. // 过滤事件
  267. if (degree <= m_messageDegree)
  268. {
  269. // 需要记录数据
  270. HslMessageItem item = GetHslMessageItem(degree, text);
  271. AddItemToCache(item);
  272. }
  273. }
  274. private void AddItemToCache(HslMessageItem item)
  275. {
  276. m_simpleHybirdLock.Enter();
  277. m_WaitForSave.Enqueue(item);
  278. m_simpleHybirdLock.Leave();
  279. StartSaveFile();
  280. }
  281. private void StartSaveFile()
  282. {
  283. if (Interlocked.CompareExchange(ref m_SaveStatus, 1, 0) == 0)
  284. {
  285. //启动存储
  286. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolSaveFile), null);
  287. }
  288. }
  289. private HslMessageItem GetAndRemoveLogItem()
  290. {
  291. HslMessageItem result = null;
  292. m_simpleHybirdLock.Enter();
  293. result = m_WaitForSave.Count > 0 ? m_WaitForSave.Dequeue() : null;
  294. m_simpleHybirdLock.Leave();
  295. return result;
  296. }
  297. private void ThreadPoolSaveFile(object obj)
  298. {
  299. // 获取需要存储的日志
  300. HslMessageItem current = GetAndRemoveLogItem();
  301. // 进入文件操作的锁
  302. m_fileSaveLock.Enter();
  303. // 获取要存储的文件名称
  304. string LogSaveFileName = GetFileSaveName();
  305. if (!string.IsNullOrEmpty(LogSaveFileName))
  306. {
  307. // 保存
  308. StreamWriter sw = null;
  309. try
  310. {
  311. sw = new StreamWriter(LogSaveFileName, true, Encoding.UTF8);
  312. while (current != null)
  313. {
  314. // 触发事件
  315. OnBeforeSaveToFile( new HslEventArgs( ) { HslMessage = current } );
  316. sw.Write(HslMessageFormate(current));
  317. sw.Write(Environment.NewLine);
  318. current = GetAndRemoveLogItem();
  319. }
  320. }
  321. catch (Exception ex)
  322. {
  323. AddItemToCache(current);
  324. AddItemToCache(new HslMessageItem()
  325. {
  326. Degree = HslMessageDegree.FATAL,
  327. Text = LogNetManagment.GetSaveStringFromException("LogNetSelf", ex),
  328. });
  329. }
  330. finally
  331. {
  332. sw?.Dispose();
  333. }
  334. }
  335. // 释放锁
  336. m_fileSaveLock.Leave();
  337. Interlocked.Exchange(ref m_SaveStatus, 0);
  338. // 再次检测锁是否释放完成
  339. if (m_WaitForSave.Count > 0)
  340. {
  341. StartSaveFile();
  342. }
  343. }
  344. private string HslMessageFormate(HslMessageItem hslMessage)
  345. {
  346. StringBuilder stringBuilder = new StringBuilder("\u0002");
  347. stringBuilder.Append("[");
  348. stringBuilder.Append(LogNetManagment.GetDegreeDescription(hslMessage.Degree));
  349. stringBuilder.Append("] ");
  350. stringBuilder.Append(hslMessage.Time.ToString("yyyy-MM-dd HH:mm:ss.fff"));
  351. stringBuilder.Append(" thread:[");
  352. stringBuilder.Append(hslMessage.ThreadId.ToString("D2"));
  353. stringBuilder.Append("] ");
  354. stringBuilder.Append(hslMessage.Text);
  355. return stringBuilder.ToString();
  356. }
  357. private void ThreadPoolSaveText(object obj)
  358. {
  359. // 进入文件操作的锁
  360. m_fileSaveLock.Enter();
  361. //获取要存储的文件名称
  362. string LogSaveFileName = GetFileSaveName();
  363. if (!string.IsNullOrEmpty(LogSaveFileName))
  364. {
  365. // 保存
  366. StreamWriter sw = null;
  367. try
  368. {
  369. sw = new StreamWriter(LogSaveFileName, true, Encoding.UTF8);
  370. string str = obj as string;
  371. sw.Write(str);
  372. }
  373. catch (Exception ex)
  374. {
  375. AddItemToCache(new HslMessageItem()
  376. {
  377. Degree = HslMessageDegree.FATAL,
  378. Text = LogNetManagment.GetSaveStringFromException("LogNetSelf", ex),
  379. });
  380. }
  381. finally
  382. {
  383. sw?.Dispose();
  384. }
  385. }
  386. // 释放锁
  387. m_fileSaveLock.Leave();
  388. }
  389. #endregion
  390. #region 辅助方法
  391. /// <summary>
  392. /// 获取要存储的文件的名称
  393. /// </summary>
  394. /// <returns></returns>
  395. protected virtual string GetFileSaveName()
  396. {
  397. return string.Empty;
  398. }
  399. /// <summary>
  400. /// 返回检查的路径名称,将会包含反斜杠
  401. /// </summary>
  402. /// <param name="filePath"></param>
  403. /// <returns></returns>
  404. protected string CheckPathEndWithSprit(string filePath)
  405. {
  406. if (!string.IsNullOrEmpty(filePath))
  407. {
  408. if (!Directory.Exists(filePath))
  409. {
  410. Directory.CreateDirectory(filePath);
  411. }
  412. if (!filePath.EndsWith(@"\"))
  413. {
  414. return filePath + @"\";
  415. }
  416. }
  417. return filePath;
  418. }
  419. private HslMessageItem GetHslMessageItem(HslMessageDegree degree, string text)
  420. {
  421. return new HslMessageItem()
  422. {
  423. Degree = degree,
  424. Text = text,
  425. ThreadId = Thread.CurrentThread.ManagedThreadId,
  426. Time = DateTime.Now,
  427. };
  428. }
  429. private int CalculateStringOccupyLength(string str)
  430. {
  431. if (string.IsNullOrEmpty(str)) return 0;
  432. int result = 0;
  433. for (int i = 0; i < str.Length; i++)
  434. {
  435. if (str[i] >= 0x4e00 && str[i] <= 0x9fbb)
  436. {
  437. result += 2;
  438. }
  439. else
  440. {
  441. result += 1;
  442. }
  443. }
  444. return result;
  445. }
  446. private void AppendCharToStringBuilder(StringBuilder sb, char c, int count)
  447. {
  448. for (int i = 0; i < count; i++)
  449. {
  450. sb.Append(c);
  451. }
  452. }
  453. #endregion
  454. #region IDisposable Support
  455. private bool disposedValue = false; // 要检测冗余调用
  456. /// <summary>
  457. /// 释放资源
  458. /// </summary>
  459. /// <param name="disposing"></param>
  460. protected virtual void Dispose(bool disposing)
  461. {
  462. if (!disposedValue)
  463. {
  464. if (disposing)
  465. {
  466. // TODO: 释放托管状态(托管对象)。
  467. m_simpleHybirdLock.Dispose();
  468. m_WaitForSave.Clear();
  469. m_fileSaveLock.Dispose();
  470. }
  471. // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
  472. // TODO: 将大型字段设置为 null。
  473. m_simpleHybirdLock = null;
  474. m_WaitForSave = null;
  475. m_fileSaveLock = null;
  476. disposedValue = true;
  477. }
  478. }
  479. // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
  480. // ~LogNetBase() {
  481. // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
  482. // Dispose(false);
  483. // }
  484. // 添加此代码以正确实现可处置模式。
  485. /// <summary>
  486. /// 释放资源
  487. /// </summary>
  488. public void Dispose()
  489. {
  490. // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
  491. Dispose(true);
  492. // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
  493. // GC.SuppressFinalize(this);
  494. }
  495. #endregion
  496. }
  497. }