LogManager.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using static System.DateTime;
  10. namespace Masuit.Tools.Logging;
  11. /// <summary>
  12. /// 日志组件
  13. /// </summary>
  14. public class LogManager
  15. {
  16. private static readonly ConcurrentQueue<(string logPath, string msg)> LogQueue = new();
  17. /// <summary>
  18. /// 自定义事件
  19. /// </summary>
  20. public static event Action<LogInfo> Event;
  21. static LogManager()
  22. {
  23. Task.Factory.StartNew(() =>
  24. {
  25. while (true)
  26. {
  27. Pause.WaitOne(1000, true);
  28. var temp = new List<string[]>();
  29. foreach (var (logPath, msg) in LogQueue)
  30. {
  31. var logMergeContent = string.Concat(msg, Environment.NewLine, "----------------------------------------------------------------------------------------------------------------------", Environment.NewLine);
  32. var logArr = temp.FirstOrDefault(d => d[0].Equals(logPath));
  33. if (logArr != null)
  34. {
  35. logArr[1] = string.Concat(logArr[1], logMergeContent);
  36. }
  37. else
  38. {
  39. logArr = new[]
  40. {
  41. logPath,
  42. logMergeContent
  43. };
  44. temp.Add(logArr);
  45. }
  46. LogQueue.TryDequeue(out _);
  47. }
  48. foreach (var item in temp)
  49. {
  50. WriteText(item[0], item[1]);
  51. }
  52. }
  53. }, TaskCreationOptions.LongRunning);
  54. }
  55. private static AutoResetEvent Pause => new(false);
  56. private static string _logDirectory;
  57. /// <summary>
  58. /// 日志存放目录,默认日志放在当前应用程序运行目录下的logs文件夹中
  59. /// </summary>
  60. public static string LogDirectory
  61. {
  62. get => _logDirectory ?? (Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory).Any(s => s.Contains("Web.config")) ? AppDomain.CurrentDomain.BaseDirectory + @"App_Data\Logs\" : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"));
  63. set
  64. {
  65. //自定义目录
  66. if (!Directory.Exists(value))
  67. {
  68. Directory.CreateDirectory(value);
  69. }
  70. _logDirectory = value;
  71. }
  72. }
  73. /// <summary>
  74. /// 写入Info级别的日志
  75. /// </summary>
  76. /// <param name="info"></param>
  77. public static void Info(string info)
  78. {
  79. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {info}"));
  80. var log = new LogInfo()
  81. {
  82. LogLevel = LogLevel.Info,
  83. Message = info,
  84. Time = Now,
  85. ThreadId = Thread.CurrentThread.ManagedThreadId
  86. };
  87. Event?.Invoke(log);
  88. }
  89. /// <summary>
  90. /// 写入Info级别的日志
  91. /// </summary>
  92. /// <param name="source"></param>
  93. /// <param name="info"></param>
  94. public static void Info(string source, string info)
  95. {
  96. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {source} {info}"));
  97. var log = new LogInfo()
  98. {
  99. LogLevel = LogLevel.Info,
  100. Message = info,
  101. Time = Now,
  102. ThreadId = Thread.CurrentThread.ManagedThreadId,
  103. Source = source
  104. };
  105. Event?.Invoke(log);
  106. }
  107. /// <summary>
  108. /// 写入Info级别的日志
  109. /// </summary>
  110. /// <param name="source"></param>
  111. /// <param name="info"></param>
  112. public static void Info(Type source, string info)
  113. {
  114. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {source.FullName} {info}"));
  115. var log = new LogInfo()
  116. {
  117. LogLevel = LogLevel.Info,
  118. Message = info,
  119. Time = Now,
  120. ThreadId = Thread.CurrentThread.ManagedThreadId,
  121. Source = source.FullName
  122. };
  123. Event?.Invoke(log);
  124. }
  125. /// <summary>
  126. /// 写入debug级别日志
  127. /// </summary>
  128. /// <param name="debug">异常对象</param>
  129. public static void Debug(string debug)
  130. {
  131. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {debug}"));
  132. var log = new LogInfo()
  133. {
  134. LogLevel = LogLevel.Debug,
  135. Message = debug,
  136. Time = Now,
  137. ThreadId = Thread.CurrentThread.ManagedThreadId
  138. };
  139. Event?.Invoke(log);
  140. }
  141. /// <summary>
  142. /// 写入debug级别日志
  143. /// </summary>
  144. /// <param name="source">异常源的类型</param>
  145. /// <param name="debug">异常对象</param>
  146. public static void Debug(string source, string debug)
  147. {
  148. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {source} {debug}"));
  149. var log = new LogInfo()
  150. {
  151. LogLevel = LogLevel.Debug,
  152. Message = debug,
  153. Time = Now,
  154. ThreadId = Thread.CurrentThread.ManagedThreadId,
  155. Source = source
  156. };
  157. Event?.Invoke(log);
  158. }
  159. /// <summary>
  160. /// 写入debug级别日志
  161. /// </summary>
  162. /// <param name="source">异常源的类型</param>
  163. /// <param name="debug">异常对象</param>
  164. public static void Debug(Type source, string debug)
  165. {
  166. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {source.FullName} {debug}"));
  167. var log = new LogInfo()
  168. {
  169. LogLevel = LogLevel.Debug,
  170. Message = debug,
  171. Time = Now,
  172. ThreadId = Thread.CurrentThread.ManagedThreadId,
  173. Source = source.FullName
  174. };
  175. Event?.Invoke(log);
  176. }
  177. /// <summary>
  178. /// 写入error级别日志
  179. /// </summary>
  180. /// <param name="error">异常对象</param>
  181. public static void Error(Exception error)
  182. {
  183. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {error.Source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
  184. var log = new LogInfo()
  185. {
  186. LogLevel = LogLevel.Error,
  187. Message = error.Message,
  188. Time = Now,
  189. ThreadId = Thread.CurrentThread.ManagedThreadId,
  190. Source = error.Source,
  191. Exception = error,
  192. ExceptionType = error.GetType().Name
  193. };
  194. Event?.Invoke(log);
  195. }
  196. /// <summary>
  197. /// 写入error级别日志
  198. /// </summary>
  199. /// <param name="source">异常源的类型</param>
  200. /// <param name="error">异常对象</param>
  201. public static void Error(Type source, Exception error)
  202. {
  203. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error.Message}{Environment.NewLine}{error.StackTrace}"));
  204. var log = new LogInfo()
  205. {
  206. LogLevel = LogLevel.Error,
  207. Message = error.Message,
  208. Time = Now,
  209. ThreadId = Thread.CurrentThread.ManagedThreadId,
  210. Source = source.FullName,
  211. Exception = error,
  212. ExceptionType = error.GetType().Name
  213. };
  214. Event?.Invoke(log);
  215. }
  216. /// <summary>
  217. /// 写入error级别日志
  218. /// </summary>
  219. /// <param name="source">异常源的类型</param>
  220. /// <param name="error">异常信息</param>
  221. public static void Error(Type source, string error)
  222. {
  223. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error}"));
  224. var log = new LogInfo()
  225. {
  226. LogLevel = LogLevel.Error,
  227. Message = error,
  228. Time = Now,
  229. ThreadId = Thread.CurrentThread.ManagedThreadId,
  230. Source = source.FullName,
  231. //Exception = error,
  232. ExceptionType = error.GetType().Name
  233. };
  234. Event?.Invoke(log);
  235. }
  236. /// <summary>
  237. /// 写入error级别日志
  238. /// </summary>
  239. /// <param name="source">异常源的类型</param>
  240. /// <param name="error">异常对象</param>
  241. public static void Error(string source, Exception error)
  242. {
  243. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
  244. var log = new LogInfo()
  245. {
  246. LogLevel = LogLevel.Error,
  247. Message = error.Message,
  248. Time = Now,
  249. ThreadId = Thread.CurrentThread.ManagedThreadId,
  250. Source = source,
  251. Exception = error,
  252. ExceptionType = error.GetType().Name
  253. };
  254. Event?.Invoke(log);
  255. }
  256. /// <summary>
  257. /// 写入error级别日志
  258. /// </summary>
  259. /// <param name="source">异常源的类型</param>
  260. /// <param name="error">异常信息</param>
  261. public static void Error(string source, string error)
  262. {
  263. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source} {error}"));
  264. var log = new LogInfo()
  265. {
  266. LogLevel = LogLevel.Error,
  267. Message = error,
  268. Time = Now,
  269. ThreadId = Thread.CurrentThread.ManagedThreadId,
  270. Source = source,
  271. //Exception = error,
  272. ExceptionType = error.GetType().Name
  273. };
  274. Event?.Invoke(log);
  275. }
  276. /// <summary>
  277. /// 写入fatal级别日志
  278. /// </summary>
  279. /// <param name="fatal">异常对象</param>
  280. public static void Fatal(Exception fatal)
  281. {
  282. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {fatal.Source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
  283. var log = new LogInfo()
  284. {
  285. LogLevel = LogLevel.Fatal,
  286. Message = fatal.Message,
  287. Time = Now,
  288. ThreadId = Thread.CurrentThread.ManagedThreadId,
  289. Source = fatal.Source,
  290. Exception = fatal,
  291. ExceptionType = fatal.GetType().Name
  292. };
  293. Event?.Invoke(log);
  294. }
  295. /// <summary>
  296. /// 写入fatal级别日志
  297. /// </summary>
  298. /// <param name="source">异常源的类型</param>
  299. /// <param name="fatal">异常对象</param>
  300. public static void Fatal(Type source, Exception fatal)
  301. {
  302. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
  303. var log = new LogInfo()
  304. {
  305. LogLevel = LogLevel.Fatal,
  306. Message = fatal.Message,
  307. Time = Now,
  308. ThreadId = Thread.CurrentThread.ManagedThreadId,
  309. Source = source.FullName,
  310. Exception = fatal,
  311. ExceptionType = fatal.GetType().Name
  312. };
  313. Event?.Invoke(log);
  314. }
  315. /// <summary>
  316. /// 写入fatal级别日志
  317. /// </summary>
  318. /// <param name="source">异常源的类型</param>
  319. /// <param name="fatal">异常对象</param>
  320. public static void Fatal(Type source, string fatal)
  321. {
  322. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal}"));
  323. var log = new LogInfo()
  324. {
  325. LogLevel = LogLevel.Fatal,
  326. Message = fatal,
  327. Time = Now,
  328. ThreadId = Thread.CurrentThread.ManagedThreadId,
  329. Source = source.FullName,
  330. //Exception = fatal,
  331. ExceptionType = fatal.GetType().Name
  332. };
  333. Event?.Invoke(log);
  334. }
  335. /// <summary>
  336. /// 写入fatal级别日志
  337. /// </summary>
  338. /// <param name="source">异常源的类型</param>
  339. /// <param name="fatal">异常对象</param>
  340. public static void Fatal(string source, Exception fatal)
  341. {
  342. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
  343. var log = new LogInfo()
  344. {
  345. LogLevel = LogLevel.Fatal,
  346. Message = fatal.Message,
  347. Time = Now,
  348. ThreadId = Thread.CurrentThread.ManagedThreadId,
  349. Source = source,
  350. Exception = fatal,
  351. ExceptionType = fatal.GetType().Name
  352. };
  353. Event?.Invoke(log);
  354. }
  355. /// <summary>
  356. /// 写入fatal级别日志
  357. /// </summary>
  358. /// <param name="source">异常源的类型</param>
  359. /// <param name="fatal">异常对象</param>
  360. public static void Fatal(string source, string fatal)
  361. {
  362. LogQueue.Enqueue((GetLogPath(), $"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal}"));
  363. LogInfo log = new LogInfo()
  364. {
  365. LogLevel = LogLevel.Fatal,
  366. Message = fatal,
  367. Time = Now,
  368. ThreadId = Thread.CurrentThread.ManagedThreadId,
  369. Source = source,
  370. ExceptionType = fatal.GetType().Name
  371. };
  372. Event?.Invoke(log);
  373. }
  374. private static string GetLogPath()
  375. {
  376. string newFilePath;
  377. var logDir = string.IsNullOrEmpty(LogDirectory) ? Path.Combine(Environment.CurrentDirectory, "logs") : LogDirectory;
  378. Directory.CreateDirectory(logDir);
  379. const string extension = ".log";
  380. var fileNameNotExt = Now.ToString("yyyyMMdd");
  381. var fileNamePattern = string.Concat(fileNameNotExt, "(*)", extension);
  382. var filePaths = Directory.GetFiles(logDir, fileNamePattern, SearchOption.TopDirectoryOnly).ToList();
  383. if (filePaths.Count > 0)
  384. {
  385. int fileMaxLen = filePaths.Max(d => d.Length);
  386. string lastFilePath = filePaths.Where(d => d.Length == fileMaxLen).OrderByDescending(d => d).FirstOrDefault();
  387. if (new FileInfo(lastFilePath).Length > 1024 * 1024)
  388. {
  389. var no = new Regex(@"(?is)(?<=\()(.*)(?=\))").Match(Path.GetFileName(lastFilePath)).Value;
  390. var parse = int.TryParse(no, out int tempno);
  391. var formatno = $"({(parse ? (tempno + 1) : tempno)})";
  392. var newFileName = string.Concat(fileNameNotExt, formatno, extension);
  393. newFilePath = Path.Combine(logDir, newFileName);
  394. }
  395. else
  396. {
  397. newFilePath = lastFilePath;
  398. }
  399. }
  400. else
  401. {
  402. var newFileName = string.Concat(fileNameNotExt, $"({0})", extension);
  403. newFilePath = Path.Combine(logDir, newFileName);
  404. }
  405. return newFilePath;
  406. }
  407. private static void WriteText(string logPath, string logContent)
  408. {
  409. try
  410. {
  411. if (!File.Exists(logPath))
  412. {
  413. File.CreateText(logPath).Close();
  414. }
  415. using var sw = File.AppendText(logPath);
  416. sw.Write(logContent);
  417. }
  418. catch (Exception)
  419. {
  420. // ignored
  421. }
  422. }
  423. }