UNIVPLMDataIntegration/Admin.NET/Admin.NET.Core/Logging/HttpLoggingHandler.cs
2025-08-26 09:05:05 +08:00

140 lines
6.3 KiB
C#

// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using System.Net.Http.Headers;
namespace Admin.NET.Core;
/// <summary>
/// http日志处理
/// </summary>
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> _userManager = new(() => App.GetService<UserManager>());
private readonly Dictionary<string, bool> _enabledLogMap;
private readonly SysConfigService _sysConfigService;
private readonly IEventPublisher _eventPublisher;
public HttpLoggingHandler(IEventPublisher eventPublisher, SysConfigService sysConfigService)
{
_eventPublisher = eventPublisher;
_sysConfigService = sysConfigService;
var options = App.GetConfig<object>("HttpRemotes") ?? throw new Exception("未正确配置HttpRemotes.json");
_enabledLogMap = options.GetType().GetProperties()
.Where(u => u.PropertyType == typeof(HttpRemoteItem))
.ToDictionary(u => u.GetValue(options) is HttpRemoteItem opt ? opt.HttpName : "",
u => u.GetValue(options) is HttpRemoteItem { EnabledLog: true });
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
// 判断全局Http日志开关
var enabledLog = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysLogHttp);
if (!enabledLog) return await base.SendAsync(request, cancellationToken);
// 判断当前配置日志开关
(string apiDesc, bool ignoreLog) = GetRemoteApiAttrAndRemove(request.Headers);
_ = request.Options.TryGetValue<string>(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);
}
}
/// <summary>
/// 获取接口描述相关属性
/// </summary>
/// <param name="headers"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 移除接口描述相关属性
/// </summary>
/// <param name="headers"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 设置接口描述相关属性
/// </summary>
/// <param name="builder"></param>
/// <param name="httpName"></param>
/// <param name="attr"></param>
/// <returns></returns>
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;
}
}