// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 // // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! using System.Net.Http.Headers; namespace Admin.NET.Core; /// /// http日志处理 /// public class HttpLoggingHandler : DelegatingHandler, ITransient { private static readonly string IgnoreLogKey = CommonConst.HttpRemoteHeaderKeyPrefix + "IGNORE_LOG__"; private static readonly string ApiDescKey = CommonConst.HttpRemoteHeaderKeyPrefix + "API_DESC__"; private static readonly string HttpNameKey = CommonConst.HttpRemoteHeaderKeyPrefix + "NAME__"; private static readonly Lazy _userManager = new(() => App.GetService()); private readonly Dictionary _enabledLogMap; private readonly SysConfigService _sysConfigService; private readonly IEventPublisher _eventPublisher; public HttpLoggingHandler(IEventPublisher eventPublisher, SysConfigService sysConfigService, IOptions options) { _eventPublisher = eventPublisher; HttpRemotesOptions httpRemotesOptions = options.Value; _sysConfigService = sysConfigService; _enabledLogMap = typeof(HttpRemotesOptions).GetProperties() .Where(u => u.PropertyType == typeof(HttpRemoteItem)) .ToDictionary(u => u.GetValue(httpRemotesOptions) is HttpRemoteItem opt ? opt.HttpName : "", u => u.GetValue(httpRemotesOptions) is HttpRemoteItem { EnabledLog: true }); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 判断全局Http日志开关 var enabledLog = await _sysConfigService.GetConfigValueByCode(ConfigConst.SysLogHttp); if (!enabledLog) return await base.SendAsync(request, cancellationToken); // 判断当前配置日志开关 (string apiDesc, bool ignoreLog) = GetRemoteApiAttrAndRemove(request.Headers); _ = request.Options.TryGetValue(HttpNameKey, out var httpName); if (!string.IsNullOrWhiteSpace(httpName)) enabledLog = _enabledLogMap.GetOrDefault(httpName); if (!enabledLog || ignoreLog) return await base.SendAsync(request, cancellationToken); var stopWatch = Stopwatch.StartNew(); var urlList = request.RequestUri?.LocalPath.Split("/") ?? []; var sysLogHttp = new SysLogHttp { HttpClientName = httpName, HttpApiDesc = apiDesc, HttpMethod = request.Method.Method, ActionName = urlList.Length >= 2 ? $"{urlList[^2]}/{urlList[^1]}" : urlList.Length >= 1 ? urlList[^1] : null, RequestUrl = request.RequestUri?.ToString(), RequestHeaders = request.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson(), TenantId = _userManager.Value?.TenantId, CreateUserId = _userManager.Value?.UserId, CreateUserName = _userManager.Value?.RealName, }; // _userManager if (request.Content != null) sysLogHttp.RequestBody = await request.Content.ReadAsStringAsync(cancellationToken); sysLogHttp.StartTime = DateTime.Now; try { var response = await base.SendAsync(request, cancellationToken); stopWatch.Stop(); sysLogHttp.EndTime = DateTime.Now; sysLogHttp.StatusCode = response.StatusCode; sysLogHttp.ResponseHeaders = response.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson(); sysLogHttp.IsSuccessStatusCode = response.IsSuccessStatusCode ? YesNoEnum.Y : YesNoEnum.N; sysLogHttp.ResponseBody = await response.Content.ReadAsStringAsync(cancellationToken); return response; } catch (Exception ex) { stopWatch.Stop(); sysLogHttp.EndTime = DateTime.Now; sysLogHttp.IsSuccessStatusCode = YesNoEnum.N; sysLogHttp.Exception = JSON.Serialize(SerializableException.FromException(ex)); throw; } finally { sysLogHttp.Elapsed = stopWatch.ElapsedMilliseconds; await _eventPublisher.PublishAsync(nameof(AppEventSubscriber.CreateHttpLog), sysLogHttp, cancellationToken); } } /// /// 获取接口描述相关属性 /// /// /// private static (string apiDesc, bool ignoreLog) GetRemoteApiAttrAndRemove(HttpRequestHeaders headers) { var result = new { ApiDesc = headers?.FirstOrDefault(u => u.Key == ApiDescKey).Value?.FirstOrDefault()?.ToString(), IgnoreLog = headers?.FirstOrDefault(u => u.Key == IgnoreLogKey).Value?.ToBoolean() ?? false, }; RemoveRemoteApiAttr(headers); return (result.ApiDesc, result.IgnoreLog); } /// /// 移除接口描述相关属性 /// /// /// private static void RemoveRemoteApiAttr(HttpRequestHeaders headers) { if (headers == null) return; var keys = headers .Where(kv => kv.Key.StartsWith(CommonConst.HttpRemoteHeaderKeyPrefix)) .Select(u => u.Key) .ToList(); foreach (var key in keys) headers.Remove(key); } /// /// 设置接口描述相关属性 /// /// /// /// /// public static HttpRequestBuilder SetRemoteApiAttr(HttpRequestBuilder builder, string httpName, HttpRemoteApiAttribute attr) { builder.WithHeader(IgnoreLogKey, attr.IgnoreLog, replace:true); builder.WithHeader(ApiDescKey, attr.Desc, replace:true); builder.SetHttpClientName(httpName); return builder; } }