DefaultTopLogger.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. using Top.Api.Util;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Text;
  7. using System.Threading;
  8. namespace Top.Api
  9. {
  10. /// <summary>
  11. /// 日志类
  12. /// 队列 可年/月/周/日/大小分割
  13. /// 调用方法:
  14. /// Log.Instance.LogDirectory=@"C:\"; 默认为程序运行目录
  15. /// Log.Instance.FileNamePrefix="cxd";默认为log_
  16. /// Log.Instance.CurrentMsgType = MsgLevel.Debug;默认为Error
  17. /// Log.Instance.logFileSplit = LogFileSplit.Daily; 日志拆分类型LogFileSplit.Sizely 大小
  18. /// Log.Instance.MaxFileSize = 1024; 只有LogFileSplit.Sizely的时候配置有效
  19. /// Log.Instance.LogWrite("aa");
  20. /// Log.Instance.LogWrite("aa", MsgLevel.Debug);
  21. /// </summary>
  22. public class Log : IDisposable, ITopLogger
  23. {
  24. private static Log _instance = null;
  25. private static readonly object _synObject = new object();
  26. /// <summary>
  27. ///单例
  28. /// </summary>
  29. public static Log Instance
  30. {
  31. get
  32. {
  33. if (null == _instance)
  34. {
  35. lock (_synObject)
  36. {
  37. if (null == _instance)
  38. {
  39. _instance = new Log();
  40. }
  41. }
  42. }
  43. return _instance;
  44. }
  45. }
  46. /// <summary>
  47. /// 日志对象的缓存队列
  48. /// </summary>
  49. private static Queue<Msg> _msgs;
  50. /// <summary>
  51. /// 日志写入线程的控制标记 ture写中|false没有写
  52. /// </summary>
  53. private bool _state;
  54. private string _logDirectory = AppDomain.CurrentDomain.BaseDirectory;
  55. private bool isDebugEnabled = false;
  56. /// <summary>
  57. /// 日志文件存放目录
  58. /// </summary>
  59. public string LogDirectory
  60. {
  61. get { return _logDirectory; }
  62. set { _logDirectory = value; }
  63. }
  64. private LogFileSplit _logFileSplit = LogFileSplit.Sizely;
  65. /// <summary>
  66. /// 日志拆分类型
  67. /// </summary>
  68. public LogFileSplit logFileSplit
  69. {
  70. get { return _logFileSplit; }
  71. set { _logFileSplit = value; }
  72. }
  73. private MsgLevel _currentLogLevel = MsgLevel.Infor;
  74. /// <summary>
  75. /// 当前日志记录等级
  76. /// </summary>
  77. public MsgLevel CurrentMsgType
  78. {
  79. get { return _currentLogLevel; }
  80. set { _currentLogLevel = value; }
  81. }
  82. /// <summary>
  83. /// 当前负责记录日志文件的名称
  84. /// </summary>
  85. private string _currentFileName = "1.log";
  86. private string _fileNamePrefix = "topsdk_";
  87. /// <summary>
  88. /// 日志的前缀名称,默认为log_
  89. /// </summary>
  90. public string FileNamePrefix
  91. {
  92. get { return _fileNamePrefix; }
  93. set { _fileNamePrefix = value; }
  94. }
  95. /// <summary>
  96. /// 日志文件生命周期的时间标记
  97. /// </summary>
  98. private DateTime _CurrentFileTimeSign = new DateTime();
  99. private int _maxFileSize = 1024;
  100. /// <summary>
  101. /// 单个日志文件默认大小(单位:兆)
  102. /// </summary>
  103. public int MaxFileSize
  104. {
  105. get { return _maxFileSize; }
  106. set { _maxFileSize = value; }
  107. }
  108. /// <summary>
  109. /// 文件后缀号
  110. /// </summary>
  111. private int _fileSymbol = 0;
  112. /// <summary>
  113. /// 当前文件大小(单位:B)
  114. /// </summary>
  115. private long _fileSize = 0;
  116. /// <summary>
  117. /// 日志文件写入流对象
  118. /// </summary>
  119. private StreamWriter _writer;
  120. /// <summary>
  121. /// 创建日志对象的新实例,根据指定的日志文件路径和指定的日志文件创建类型
  122. /// </summary>
  123. private Log()
  124. {
  125. if (_msgs == null)
  126. {
  127. GetCurrentFilename();
  128. _state = true;
  129. _msgs = new Queue<Msg>();
  130. Thread thread = new Thread(work);
  131. thread.Start();
  132. }
  133. }
  134. //日志文件写入线程执行的方法
  135. private void work()
  136. {
  137. while (true)
  138. {
  139. //判断队列中是否存在待写入的日志
  140. if (_msgs.Count > 0)
  141. {
  142. Msg msg = null;
  143. lock (_msgs)
  144. {
  145. msg = _msgs.Dequeue();
  146. if (msg != null)
  147. {
  148. FileWrite(msg);
  149. }
  150. }
  151. }
  152. else
  153. {
  154. //判断是否已经发出终止日志并关闭的消息
  155. if (_state)
  156. {
  157. Thread.Sleep(1);
  158. }
  159. else
  160. {
  161. FileClose();
  162. }
  163. }
  164. }
  165. }
  166. /// <summary>
  167. /// 根据日志类型获取日志文件名,并同时创建文件到期的时间标记
  168. /// 通过判断文件的到期时间标记将决定是否创建新文件。
  169. /// </summary>
  170. /// <returns></returns>
  171. private
  172. void GetCurrentFilename()
  173. {
  174. DateTime now = DateTime.Now;
  175. string format = "";
  176. switch (_logFileSplit)
  177. {
  178. case LogFileSplit.Daily:
  179. _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);
  180. _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(1);
  181. format = now.ToString("yyyyMMdd'.log'");
  182. break;
  183. case LogFileSplit.Weekly:
  184. _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);
  185. _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(7);
  186. format = now.ToString("yyyyMMdd'.log'");
  187. break;
  188. case LogFileSplit.Monthly:
  189. _CurrentFileTimeSign = new DateTime(now.Year, now.Month, 1);
  190. _CurrentFileTimeSign = _CurrentFileTimeSign.AddMonths(1);
  191. format = now.ToString("yyyyMM'.log'");
  192. break;
  193. case LogFileSplit.Annually:
  194. _CurrentFileTimeSign = new DateTime(now.Year, 1, 1);
  195. _CurrentFileTimeSign = _CurrentFileTimeSign.AddYears(1);
  196. format = now.ToString("yyyy'.log'");
  197. break;
  198. default:
  199. _fileSymbol++;
  200. format = _fileSymbol.ToString() + ".log";
  201. break;
  202. }
  203. if (File.Exists(Path.Combine(LogDirectory, _currentFileName)))
  204. {
  205. _fileSize = new FileInfo(Path.Combine(LogDirectory, _currentFileName)).Length;
  206. }
  207. else
  208. {
  209. _fileSize = 0;
  210. }
  211. _currentFileName = _fileNamePrefix + format.Trim();
  212. }
  213. //写入日志文本到文件的方法
  214. private void FileWrite(Msg msg)
  215. {
  216. try
  217. {
  218. if (_writer == null)
  219. {
  220. FileOpen();
  221. }
  222. if (_writer != null)
  223. {
  224. //判断文件到期标志,如果当前文件到期则关闭当前文件创建新的日志文件
  225. if ((_logFileSplit != LogFileSplit.Sizely && DateTime.Now >= _CurrentFileTimeSign) ||
  226. (_logFileSplit == LogFileSplit.Sizely && ((double)_fileSize / 1048576) > _maxFileSize))
  227. {
  228. GetCurrentFilename();
  229. FileClose();
  230. FileOpen();
  231. }
  232. _writer.Write(msg.datetime);
  233. _writer.Write('\t');
  234. _writer.Write(msg.type);
  235. _writer.Write('\t');
  236. _writer.WriteLine(msg.text);
  237. _fileSize += System.Text.Encoding.UTF8.GetBytes(msg.ToString()).Length;
  238. _writer.Flush();
  239. }
  240. }
  241. catch (Exception e)
  242. {
  243. Console.Out.Write(e);
  244. }
  245. }
  246. //打开文件准备写入
  247. private void FileOpen()
  248. {
  249. _writer = new StreamWriter(LogDirectory + _currentFileName, true, Encoding.UTF8);
  250. }
  251. //关闭打开的日志文件
  252. private void FileClose()
  253. {
  254. if (_writer != null)
  255. {
  256. _writer.Flush();
  257. _writer.Close();
  258. _writer.Dispose();
  259. _writer = null;
  260. }
  261. }
  262. /// <summary>
  263. /// 写入新日志,根据指定的日志对象Msg
  264. /// </summary>
  265. /// <param name="msg">日志内容对象</param>
  266. private void LogWrite(Msg msg)
  267. {
  268. if (msg.type < CurrentMsgType)
  269. return;
  270. if (_msgs != null)
  271. {
  272. lock (_msgs)
  273. {
  274. _msgs.Enqueue(msg);
  275. }
  276. }
  277. }
  278. /// <summary>
  279. /// 写入新日志,根据指定的日志内容和信息类型,采用当前时间为日志时间写入新日志
  280. /// </summary>
  281. /// <param name="text">日志内容</param>
  282. /// <param name="type">信息类型</param>
  283. public void LogWrite(string text, MsgLevel type)
  284. {
  285. LogWrite(new Msg(text, type));
  286. }
  287. /// <summary>
  288. /// 写入新日志,根据指定的日志内容
  289. /// </summary>
  290. /// <param name="text">日志内容</param>
  291. public void LogWrite(string text)
  292. {
  293. LogWrite(text, MsgLevel.Debug);
  294. }
  295. /// <summary>
  296. /// 写入新日志,根据指定的日志时间、日志内容和信息类型写入新日志
  297. /// </summary>
  298. /// <param name="dt">日志时间</param>
  299. /// <param name="text">日志内容</param>
  300. /// <param name="type">信息类型</param>
  301. public void LogWrite(DateTime dt, string text, MsgLevel type)
  302. {
  303. LogWrite(new Msg(dt, text, type));
  304. }
  305. /// <summary>
  306. /// 写入新日志,根据指定的异常类和信息类型写入新日志
  307. /// </summary>
  308. /// <param name="e">异常对象</param>
  309. /// <param name="type">信息类型</param>
  310. public void LogWrite(Exception e)
  311. {
  312. LogWrite(new Msg(e.Message, MsgLevel.Error));
  313. }
  314. /// <summary>
  315. /// 销毁日志对象
  316. /// </summary>
  317. public void Dispose()
  318. {
  319. _state = false;
  320. }
  321. public void TraceApiError(string appKey, string apiName, string url, System.Collections.Generic.Dictionary<string, string> parameters, double latency, string errorMessage)
  322. {
  323. StringBuilder info = new StringBuilder();
  324. info.Append(appKey);
  325. info.Append(Constants.LOG_SPLIT);
  326. info.Append(apiName);
  327. info.Append(Constants.LOG_SPLIT);
  328. info.Append(TopUtils.GetIntranetIp());
  329. info.Append(Constants.LOG_SPLIT);
  330. info.Append(System.Environment.OSVersion.VersionString);
  331. info.Append(Constants.LOG_SPLIT);
  332. info.Append(latency);
  333. info.Append(Constants.LOG_SPLIT);
  334. info.Append(url);
  335. info.Append(Constants.LOG_SPLIT);
  336. info.Append(WebUtils.BuildQuery(parameters));
  337. info.Append(Constants.LOG_SPLIT);
  338. info.Append(errorMessage);
  339. this.Error(info.ToString());
  340. }
  341. public void Error(string message)
  342. {
  343. this.LogWrite(message,MsgLevel.Error);
  344. }
  345. public void Error(string format, params object[] args)
  346. {
  347. this.LogWrite(string.Format(format, args), MsgLevel.Error);
  348. }
  349. public void Warn(string message)
  350. {
  351. this.LogWrite(message, MsgLevel.Warn);
  352. }
  353. public void Warn(string format, params object[] args)
  354. {
  355. this.LogWrite(string.Format(format, args), MsgLevel.Warn);
  356. }
  357. public void Info(string message)
  358. {
  359. this.LogWrite(message, MsgLevel.Infor);
  360. }
  361. public void Info(string format, params object[] args)
  362. {
  363. this.LogWrite(string.Format(format, args), MsgLevel.Infor);
  364. }
  365. public bool IsDebugEnabled()
  366. {
  367. return this.isDebugEnabled;
  368. }
  369. public void Debug(string message)
  370. {
  371. if (isDebugEnabled)
  372. {
  373. this.LogWrite(message, MsgLevel.Debug);
  374. }
  375. }
  376. public void Debug(string format, params object[] args)
  377. {
  378. if (isDebugEnabled)
  379. {
  380. this.LogWrite(string.Format(format, args), MsgLevel.Debug);
  381. }
  382. }
  383. }
  384. /// <summary>
  385. /// 一个日志记录的对象
  386. /// </summary>
  387. public class Msg
  388. {
  389. /// <summary>
  390. /// 创建新的日志记录实例;日志记录的内容为空,消息类型为MsgType.Unknown,日志时间为当前时间
  391. /// </summary>
  392. public Msg()
  393. : this("", MsgLevel.Debug)
  394. {
  395. }
  396. /// <summary>
  397. /// 创建新的日志记录实例;日志事件为当前时间
  398. /// </summary>
  399. /// <param name="t">日志记录的文本内容</param>
  400. /// <param name="p">日志记录的消息类型</param>
  401. public Msg(string t, MsgLevel p)
  402. : this(DateTime.Now, t, p)
  403. {
  404. }
  405. /// <summary>
  406. /// 创建新的日志记录实例;
  407. /// </summary>
  408. /// <param name="dt">日志记录的时间</param>
  409. /// <param name="t">日志记录的文本内容</param>
  410. /// <param name="p">日志记录的消息类型</param>
  411. public Msg(DateTime dt, string t, MsgLevel p)
  412. {
  413. datetime = dt;
  414. type = p;
  415. text = t;
  416. }
  417. /// <summary>
  418. /// 日志记录的时间
  419. /// </summary>
  420. public DateTime datetime { get; set; }
  421. /// <summary>
  422. ///日志记录的内容
  423. /// </summary>
  424. public string text { get; set; }
  425. /// <summary>
  426. /// 日志等级
  427. /// </summary>
  428. public MsgLevel type { get; set; }
  429. public new string ToString()
  430. {
  431. return datetime.ToString(CultureInfo.InvariantCulture) + "\t" + text + "\n";
  432. }
  433. }
  434. /// <summary>
  435. /// 日志文件拆分的枚举
  436. /// </summary>
  437. /// <remarks>日志类型枚举指示日志文件创建的方式,如果日志比较多可考虑每天创建一个日志文件
  438. /// 如果日志量比较小可考虑每周、每月或每年创建一个日志文件</remarks>
  439. public enum LogFileSplit
  440. {
  441. /// <summary>
  442. /// 此枚举指示每天创建一个新的日志文件
  443. /// </summary>
  444. Daily,
  445. /// <summary>
  446. /// 此枚举指示每周创建一个新的日志文件
  447. /// </summary>
  448. Weekly,
  449. /// <summary>
  450. /// 此枚举指示每月创建一个新的日志文件
  451. /// </summary>
  452. Monthly,
  453. /// <summary>
  454. /// 此枚举指示每年创建一个新的日志文件
  455. /// </summary>
  456. Annually,
  457. /// <summary>
  458. /// 日志文件大小超过指定的创建一个新的日志文件,MaxFileSize指定大小
  459. /// </summary>
  460. Sizely
  461. }
  462. /// <summary>
  463. /// 日志等级类型 Debug=0 Infor Warn Error
  464. /// </summary>
  465. public enum MsgLevel
  466. {
  467. /// <summary>
  468. /// 调试信息
  469. /// </summary>
  470. Debug = 0,
  471. /// <summary>
  472. /// 指示普通信息类型的日志记录
  473. /// </summary>
  474. Infor,
  475. /// <summary>
  476. /// 指示警告信息类型的日志记录
  477. /// </summary>
  478. Warn,
  479. /// <summary>
  480. /// 指示错误信息类型的日志记录
  481. /// </summary>
  482. Error
  483. }
  484. }