DatabaseLoggingWriter.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // 大名科技(天津)有限公司版权所有 电话:18020030720 QQ:515096995
  2. //
  3. // 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
  4. using IPTools.Core;
  5. namespace Admin.NET.Core;
  6. /// <summary>
  7. /// 数据库日志写入器
  8. /// </summary>
  9. public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
  10. {
  11. private readonly IServiceScope _serviceScope;
  12. private readonly ISqlSugarClient _db;
  13. private readonly SysConfigService _sysConfigService; // 参数配置服务
  14. private readonly ILogger<DatabaseLoggingWriter> _logger; // 日志组件
  15. public DatabaseLoggingWriter(IServiceScopeFactory scopeFactory)
  16. {
  17. _serviceScope = scopeFactory.CreateScope();
  18. _db = _serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
  19. _sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
  20. _logger = _serviceScope.ServiceProvider.GetRequiredService<ILogger<DatabaseLoggingWriter>>();
  21. }
  22. public async Task WriteAsync(LogMessage logMsg, bool flush)
  23. {
  24. var jsonStr = logMsg.Context?.Get("loggingMonitor")?.ToString();
  25. if (jsonStr == null) return;
  26. var loggingMonitor = JSON.Deserialize<dynamic>(jsonStr);
  27. // 不记录数据校验日志
  28. if (loggingMonitor.validation != null) return;
  29. // 获取当前操作者
  30. string account = "", realName = "", userId = "", tenantId = "";
  31. if (loggingMonitor.authorizationClaims != null)
  32. {
  33. foreach (var item in loggingMonitor.authorizationClaims)
  34. {
  35. if (item.type == ClaimConst.Account)
  36. account = item.value;
  37. if (item.type == ClaimConst.RealName)
  38. realName = item.value;
  39. if (item.type == ClaimConst.TenantId)
  40. tenantId = item.value;
  41. if (item.type == ClaimConst.UserId)
  42. userId = item.value;
  43. }
  44. }
  45. string remoteIPv4 = loggingMonitor.remoteIPv4;
  46. (string ipLocation, double? longitude, double? latitude) = GetIpAddress(remoteIPv4);
  47. var client = Parser.GetDefault().Parse(loggingMonitor.userAgent.ToString());
  48. var browser = $"{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}";
  49. var os = $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}";
  50. // 捕捉异常,否则会由于 unhandled exception 导致程序崩溃
  51. try
  52. {
  53. // 记录异常日志并发送邮件
  54. if (logMsg.Exception != null || loggingMonitor.exception != null)
  55. {
  56. await _db.Insertable(new SysLogEx
  57. {
  58. ControllerName = loggingMonitor.controllerName,
  59. ActionName = loggingMonitor.actionTypeName,
  60. DisplayTitle = loggingMonitor.displayTitle,
  61. Status = loggingMonitor.returnInformation?.httpStatusCode,
  62. RemoteIp = remoteIPv4,
  63. Location = ipLocation,
  64. Longitude = longitude,
  65. Latitude = latitude,
  66. Browser = browser, // loggingMonitor.userAgent,
  67. Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
  68. Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
  69. LogDateTime = logMsg.LogDateTime,
  70. Account = account,
  71. RealName = realName,
  72. HttpMethod = loggingMonitor.httpMethod,
  73. RequestUrl = loggingMonitor.requestUrl,
  74. RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value),
  75. ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation),
  76. EventId = logMsg.EventId.Id,
  77. ThreadId = logMsg.ThreadId,
  78. TraceId = logMsg.TraceId,
  79. Exception = JSON.Serialize(loggingMonitor.exception),
  80. Message = logMsg.Message,
  81. CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
  82. TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
  83. LogLevel = logMsg.LogLevel
  84. }).ExecuteCommandAsync();
  85. // 将异常日志发送到邮件
  86. await App.GetRequiredService<IEventPublisher>().PublishAsync("Send:ErrorMail", loggingMonitor.exception);
  87. return;
  88. }
  89. // 记录访问日志-登录退出
  90. if (loggingMonitor.actionName == "userInfo" || loggingMonitor.actionName == "logout")
  91. {
  92. await _db.Insertable(new SysLogVis
  93. {
  94. ControllerName = loggingMonitor.controllerName,
  95. ActionName = loggingMonitor.actionTypeName,
  96. DisplayTitle = loggingMonitor.displayTitle,
  97. Status = loggingMonitor.returnInformation?.httpStatusCode,
  98. RemoteIp = remoteIPv4,
  99. Location = ipLocation,
  100. Longitude = longitude,
  101. Latitude = latitude,
  102. Browser = browser, // loggingMonitor.userAgent,
  103. Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
  104. Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
  105. LogDateTime = logMsg.LogDateTime,
  106. Account = account,
  107. RealName = realName,
  108. CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
  109. TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
  110. LogLevel = logMsg.LogLevel
  111. }).ExecuteCommandAsync();
  112. return;
  113. }
  114. // 记录操作日志
  115. var enabledSysOpLog = await _sysConfigService.GetConfigValue<bool>(CommonConst.SysOpLog);
  116. if (!enabledSysOpLog) return;
  117. await _db.Insertable(new SysLogOp
  118. {
  119. ControllerName = loggingMonitor.controllerName,
  120. ActionName = loggingMonitor.actionTypeName,
  121. DisplayTitle = loggingMonitor.displayTitle,
  122. Status = loggingMonitor.returnInformation?.httpStatusCode,
  123. RemoteIp = remoteIPv4,
  124. Location = ipLocation,
  125. Longitude = longitude,
  126. Latitude = latitude,
  127. Browser = browser, // loggingMonitor.userAgent,
  128. Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
  129. Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
  130. LogDateTime = logMsg.LogDateTime,
  131. Account = account,
  132. RealName = realName,
  133. HttpMethod = loggingMonitor.httpMethod,
  134. RequestUrl = loggingMonitor.requestUrl,
  135. RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value),
  136. ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation),
  137. EventId = logMsg.EventId.Id,
  138. ThreadId = logMsg.ThreadId,
  139. TraceId = logMsg.TraceId,
  140. Exception = loggingMonitor.exception == null ? null : JSON.Serialize(loggingMonitor.exception),
  141. Message = logMsg.Message,
  142. CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
  143. TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
  144. LogLevel = logMsg.LogLevel
  145. }).ExecuteCommandAsync();
  146. await Task.Delay(50); // 延迟 0.05 秒写入数据库,有效减少高频写入数据库导致死锁问题
  147. }
  148. catch (Exception ex)
  149. {
  150. _logger.LogError(ex, ex.Message);
  151. }
  152. }
  153. /// <summary>
  154. /// 解析IP地址
  155. /// </summary>
  156. /// <param name="ip"></param>
  157. /// <returns></returns>
  158. internal static (string ipLocation, double? longitude, double? latitude) GetIpAddress(string ip)
  159. {
  160. try
  161. {
  162. var ipInfo = IpTool.Search(ip);
  163. var addressList = new List<string>() { ipInfo.Country, ipInfo.Province, ipInfo.City, ipInfo.NetworkOperator };
  164. return (string.Join("|", addressList.Where(it => it != "0").ToList()), ipInfo.Longitude, ipInfo.Latitude); // 去掉0并用|连接
  165. }
  166. catch
  167. {
  168. // 不做处理
  169. }
  170. return ("未知", 0, 0);
  171. }
  172. /// <summary>
  173. /// 释放服务作用域
  174. /// </summary>
  175. public void Dispose()
  176. {
  177. _serviceScope.Dispose();
  178. }
  179. }