diff --git a/Directory.Build.props b/Directory.Build.props index 6bdeda8..e2e64cd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 8.0.2 + 8.0.5.2 diff --git a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs index 1a39f30..9eeb619 100644 --- a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs +++ b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs @@ -1,22 +1,18 @@ using Fake.AspNetCore; using Fake.AspNetCore.Auditing; using Fake.AspNetCore.Mvc; -using Fake.AspNetCore.Mvc.Conventions; -using Fake.AspNetCore.Mvc.Filters; using Fake.Authorization; using Fake.Autofac; +using Fake.FeiShu; using Fake.Modularity; using Fake.Security.Claims; +using Fake.SyncEx; using InterfaceForward.Application; -using InterfaceForward.Application.Filters; using InterfaceForward.Domain.Shared; -using InterfaceForward.Domain.Shared.Helpers; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; namespace InterfaceForward.Api; @@ -124,12 +120,12 @@ public class InterfaceForwardApiModule : FakeModule app.UseForwardedHeaders();//Nginx代理的话获取真实IP app.MapControllers(); - - LogHelper.Info("Application started", true); + + app.Services.GetRequiredService().Send("Application started"); } public override void Shutdown(ApplicationShutdownContext context) { - LogHelper.Info("Application stoped", true); + context.ServiceProvider.GetRequiredService().Send("Application stoped"); } } \ No newline at end of file diff --git a/src/InterfaceForward.Api/Program.cs b/src/InterfaceForward.Api/Program.cs index 9474ca4..c84b449 100644 --- a/src/InterfaceForward.Api/Program.cs +++ b/src/InterfaceForward.Api/Program.cs @@ -4,7 +4,7 @@ using Serilog; var builder = WebApplication.CreateSlimBuilder(args); var configuration = builder.Configuration; builder.Configuration.AddConsul("idwms/itfxapi", source => source.ReloadOnChange = true); -builder.Configuration.AddConsul("idwms/common"); +builder.Configuration.AddConsul("idwms/common", source => source.ReloadOnChange = true); Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .CreateLogger(); diff --git a/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs b/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs deleted file mode 100644 index ffa0b49..0000000 --- a/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text; -using Fake.DependencyInjection; -using Fake.ExceptionHandling; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; - -namespace InterfaceForward.Application.Filters; - -public class ExceptionNotifier : IExceptionNotifier, ITransientDependency -{ - public async Task NotifyAsync(ExceptionNotificationContext context) - { - var httpContext = context.ServiceProvider.GetRequiredService().HttpContext; - if (httpContext is null) - { - LogHelper.Error($@" - |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff} - |-异常堆栈:{context.Exception} "); - } - - var request = httpContext!.Request; - - // 1. 读取请求路径 - var path = request.Path; // 例如: /api/users - var queryString = request.QueryString; // 例如: ?id=123 - var fullPath = $"{path}{queryString}"; - - // 2. 读取请求方法 - var method = request.Method; // GET, POST, PUT, etc. - - // 3. 读取请求头 - var headers = request.Headers; - - // 4. 读取 Body (重要:需要启用缓冲) - request.EnableBuffering(); - - // 如果 Body 已经被其他中间件读取过,需要先重置: - request.Body.Position = 0; - - string body; - using (var reader = new StreamReader( - request.Body, - encoding: Encoding.UTF8, - detectEncodingFromByteOrderMarks: false, - leaveOpen: true)) - { - body = await reader.ReadToEndAsync(); - request.Body.Position = 0; // 重置流位置,以便后续中间件可以读取 - } - - if (context.Exception is BusinessException) - { - LogHelper.Warn($@" - |-请求路径:{method} {fullPath} - |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff} - |-请求用户:{headers.Authorization} - |-请求参数:{body} - |-业务异常:{context.Exception}", true); - } - else - { - LogHelper.Error($@" - |-请求路径:{method} {fullPath} - |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff} - |-请求用户:{headers.Authorization} - |-请求参数:{body} - |-异常堆栈:{context.Exception} "); - } - } -} \ No newline at end of file diff --git a/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs b/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs index 85cfbfb..78bd56d 100644 --- a/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs +++ b/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs @@ -17,9 +17,9 @@ public class ForwardExceptionFilter( { private readonly InterfaceRelayOptions _options = options.Value; - public Task OnExceptionAsync(ExceptionContext context) + public async Task OnExceptionAsync(ExceptionContext context) { - if (context.ExceptionHandled) return Task.CompletedTask; + if (context.ExceptionHandled) return; /* * ServiceProviderRequestLog设计的意义是: @@ -50,7 +50,7 @@ public class ForwardExceptionFilter( } else //未处理异常 { - logRepository.SendFeiShu(context.Exception); + await logRepository.SendFeiShuAsync(context.Exception); res.Code = StatusCodes.Status500InternalServerError.ToString(); res.Msg = "服务器发生未处理异常"; context.Result = new JsonResult(res); @@ -65,7 +65,6 @@ public class ForwardExceptionFilter( logRepository.AppRequestLog.Response = JsonConvert.SerializeObject(res); _ = logRepository.WriteAppLog(context.Exception); context.ExceptionHandled = true; - return Task.CompletedTask; } private bool NeedPush(ExceptionContext context, out string subscribeName) diff --git a/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs b/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs index 88f32ee..7529f86 100644 --- a/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs +++ b/src/InterfaceForward.Application/HostServices/AppSubscribeEventHandler.cs @@ -1,5 +1,6 @@ using System.Net; using Fake.DependencyInjection; +using Fake.FeiShu; using InterfaceForward.Application.Rabbit; using InterfaceForward.Domain.Shared; using InterfaceForward.Domain.Shared.Enum; @@ -7,7 +8,7 @@ using RabbitMQ.Client; namespace InterfaceForward.Application.HostServices; -public class AppSubscribeEventHandler(RabbitClient rabbitClient) : ITransientDependency +public class AppSubscribeEventHandler(RabbitClient rabbitClient, IFeiShuNotificationService feiShuNotification) : ITransientDependency { public const string AppSubscribeEventSubscribe = "interface.relay.app.subscribe"; @@ -19,15 +20,15 @@ public class AppSubscribeEventHandler(RabbitClient rabbitClient) : ITransientDep break; case OptionType.新增: var res = DoAdd(@event); - LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res}"); + feiShuNotification.Send($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res}"); break; case OptionType.修改: var res1 = DoUpdate(@event); - LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res1}"); + feiShuNotification.Send($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res1}"); break; case OptionType.删除: var res2 = DoDelete(@event); - LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res2}"); + feiShuNotification.Send($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res2}"); break; default: throw new ArgumentOutOfRangeException(); diff --git a/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs b/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs index ed79018..5eff450 100644 --- a/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs +++ b/src/InterfaceForward.Application/HostServices/AppSubscribeHostService.cs @@ -1,4 +1,5 @@ -using Fake.SyncEx; +using Fake.FeiShu; +using Fake.SyncEx; using InterfaceForward.Application.Helpers; using InterfaceForward.Application.Rabbit; using InterfaceForward.Domain.Shared; @@ -13,15 +14,17 @@ namespace InterfaceForward.Application.HostServices; public class AppSubscribeHostService : IHostedService { private readonly AppSubscribeEventHandler _appSubscribeEventHandler; + private readonly IFeiShuNotificationService _feishuNotificationService; private readonly IBasicRepository _appSubscribeConfigRepository; private readonly RabbitClient _rabbitClient; public AppSubscribeHostService(IBasicRepository appSubscribeConfigRepository, - RabbitClient rabbitClient, AppSubscribeEventHandler appSubscribeEventHandler) + RabbitClient rabbitClient, AppSubscribeEventHandler appSubscribeEventHandler, IFeiShuNotificationService feishuNotificationService) { _appSubscribeConfigRepository = appSubscribeConfigRepository; _rabbitClient = rabbitClient; _appSubscribeEventHandler = appSubscribeEventHandler; + _feishuNotificationService = feishuNotificationService; } public async Task StartAsync(CancellationToken cancellationToken) @@ -59,7 +62,7 @@ public class AppSubscribeHostService : IHostedService public void AppSubScribe(string chan, object msg) { #if DEBUG - LogHelper.Info($"接受到来自redis的订阅:{chan}\n{msg.ToJson()}", true); + _feishuNotificationService.Send($"接受到来自redis的订阅:{chan}\n{msg.ToJson()}"); #endif if (chan == AppSubscribeEventHandler.AppSubscribeEventSubscribe && msg is string str) { diff --git a/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs b/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs index 50390fd..a89cd9d 100644 --- a/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs +++ b/src/InterfaceForward.Application/Rabbit/BatchForwardEventHandler.cs @@ -1,5 +1,6 @@ using System.Text; using Fake.DependencyInjection; +using Fake.FeiShu; using Fake.UnitOfWork; using InterfaceForward.Application.Contracts.ForwardCore; using InterfaceForward.Application.Services; @@ -15,7 +16,8 @@ namespace InterfaceForward.Application.Rabbit; public class BatchForwardEventHandler( InterfaceForwardCommon forwardCommon, RabbitClient rabbitClient, - ILogRepository logRepository) + ILogRepository logRepository, + IFeiShuNotificationService feiShuNotificationService) : ITransientDependency, IRabbitHandler { public const string BatchForwardEventSubscribe = "interface.relay.batch"; @@ -86,7 +88,7 @@ public class BatchForwardEventHandler( catch (Exception ex) { replayEvent.Message = ex.Message; - LogHelper.Error(ex.ToString(), false); + feiShuNotificationService.Send(ex.ToString()); } finally { diff --git a/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs index 817821a..31cc81f 100644 --- a/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs +++ b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs @@ -17,9 +17,9 @@ public class ShopeeForwardExceptionFilter( { private readonly InterfaceRelayOptions _options = options.Value; - public Task OnExceptionAsync(ExceptionContext context) + public async Task OnExceptionAsync(ExceptionContext context) { - if (context.ExceptionHandled) return Task.CompletedTask; + if (context.ExceptionHandled) return; /* * ServiceProviderRequestLog设计的意义是: @@ -50,7 +50,7 @@ public class ShopeeForwardExceptionFilter( } else //未处理异常 { - logRepository.SendFeiShu(context.Exception); + await logRepository.SendFeiShuAsync(context.Exception); res.Code = StatusCodes.Status500InternalServerError.ToString(); res.Msg = "服务器发生未处理异常"; context.Result = new JsonResult(res); @@ -65,7 +65,6 @@ public class ShopeeForwardExceptionFilter( logRepository.AppRequestLog.Response = JsonConvert.SerializeObject(res); _ = logRepository.WriteAppLog(context.Exception); context.ExceptionHandled = true; - return Task.CompletedTask; } private bool NeedPush(ExceptionContext context, out string subscribeName) diff --git a/src/InterfaceForward.Domain.Shared/Helpers/LogHelper.cs b/src/InterfaceForward.Domain.Shared/Helpers/LogHelper.cs deleted file mode 100644 index 4787f1f..0000000 --- a/src/InterfaceForward.Domain.Shared/Helpers/LogHelper.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Net.Http.Headers; -using System.Text; -using System.Text.Json; -using Fake.SyncEx; -using InterfaceForward.Domain.Shared.Options; -using Serilog; - -namespace InterfaceForward.Domain.Shared.Helpers; - -public static class LogHelper -{ - private static FeiShuNoticeOptions _options = null!; - - public static void Init(FeiShuNoticeOptions options) - { - _options = options; - } - public static void Info(string msg, bool isSend = false) - { - Log.Information(msg); - if (!isSend) - return; - SyncContext.Run(() => NoticeAsync(msg, isSend, "Info")); - } - - public static void Warn(string msg, bool isSend = false) - { - Log.Warning(msg); - if (!isSend) - return; - SyncContext.Run(() => NoticeAsync(msg, isSend, "Warn")); - } - - public static void Error(string msg, bool isSend = true) - { - Log.Error(msg); - SyncContext.Run(() => NoticeAsync(msg, isSend, "Error")); - } - - public static async Task NoticeAsync(string content, bool isSend, string subTitle = "") - { -#if DEBUG - // 调试模式下不发送通知 - return; -#endif - if (!isSend) - { - return; - } - - if (_options.Webhook.IsNullOrWhiteSpace()) - { - Log.Warning("未配置飞书webhook"); - return; - } - - var title = subTitle.IsNullOrWhiteSpace() - ? _options.TitlePrefix - : _options.TitlePrefix + "_" + subTitle; - - await HandleNoticeAsync(content, title); - } - - private static async Task HandleNoticeAsync(string content, string title) - { - var message = new - { - msg_type = "post", - content = new - { - post = new - { - zh_cn = new - { - title, - content = new List() - { - new List() - { - new - { - tag = "text", - text = content + Environment.NewLine - } - } - } - } - } - } - }; - - using var httpClient = new HttpClient(); - httpClient.Timeout = TimeSpan.FromSeconds(_options.Timeout); - using var httpContent = new StringContent(JsonSerializer.Serialize(message), Encoding.UTF8); - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - await httpClient.PostAsync(_options.Webhook, httpContent); - } -} \ No newline at end of file diff --git a/src/InterfaceForward.Domain.Shared/InterfaceForwardDomainSharedModule.cs b/src/InterfaceForward.Domain.Shared/InterfaceForwardDomainSharedModule.cs index 17a6217..765c79c 100644 --- a/src/InterfaceForward.Domain.Shared/InterfaceForwardDomainSharedModule.cs +++ b/src/InterfaceForward.Domain.Shared/InterfaceForwardDomainSharedModule.cs @@ -1,9 +1,5 @@ using Fake.DomainDrivenDesign; using Fake.Modularity; -using InterfaceForward.Domain.Shared.Helpers; -using InterfaceForward.Domain.Shared.Options; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; namespace InterfaceForward.Domain.Shared { @@ -12,8 +8,6 @@ namespace InterfaceForward.Domain.Shared { public override void ConfigureServices(ServiceConfigurationContext context) { - var configuration = context.Services.GetConfiguration(); - LogHelper.Init(configuration.GetSection("FeiShuNotice").Get()!); } } } \ No newline at end of file diff --git a/src/InterfaceForward.Repositories/Log/Services/ILogRepository.cs b/src/InterfaceForward.Repositories/Log/Services/ILogRepository.cs index d87fe46..bb0b89c 100644 --- a/src/InterfaceForward.Repositories/Log/Services/ILogRepository.cs +++ b/src/InterfaceForward.Repositories/Log/Services/ILogRepository.cs @@ -40,7 +40,7 @@ namespace InterfaceForward.Repositories.Log.Services /// 发飞书 /// /// - void SendFeiShu(Exception contextException); + Task SendFeiShuAsync(Exception contextException); /// /// 添加日志 diff --git a/src/InterfaceForward.Repositories/Log/Services/LogRepository.cs b/src/InterfaceForward.Repositories/Log/Services/LogRepository.cs index 4793c3a..9fd4368 100644 --- a/src/InterfaceForward.Repositories/Log/Services/LogRepository.cs +++ b/src/InterfaceForward.Repositories/Log/Services/LogRepository.cs @@ -1,11 +1,12 @@ -using InterfaceForward.Domain.Shared.Dtos; +using Fake.FeiShu; +using InterfaceForward.Domain.Shared.Dtos; using InterfaceForward.Domain.Shared.Enum; using InterfaceForward.Domain.Shared.Helpers; using InterfaceForward.Repositories.Log.Entitys; using InterfaceForward.Repositories.Log.ES; using InterfaceForward.Repositories.Log.ValueObjects; using Nest; -using LogHelper = InterfaceForward.Domain.Shared.Helpers.LogHelper; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace InterfaceForward.Repositories.Log.Services; @@ -14,12 +15,16 @@ namespace InterfaceForward.Repositories.Log.Services; /// public class LogRepository : ElasticSearchContextAbstract, ILogRepository, IScopedDependency { - public LogRepository(ConnectionFactory connection) : base(connection) + private readonly IFeiShuNotificationService _feiShuNotificationService; + + public LogRepository(ConnectionFactory connection, IFeiShuNotificationService feiShuNotificationService) : + base(connection) { var guid = Guid.NewGuid(); AppRequestLog = new RequestLogDto(guid); AppRequestLog.IsSuccess = true; // 只有接口通发生未处理异常才算应用失败 ServiceProviderRequestLog = new RequestLogDto(guid); + _feiShuNotificationService = feiShuNotificationService; } public int AppId { get; set; } @@ -28,7 +33,6 @@ public class LogRepository : ElasticSearchContextAbstract, ILogReposit public async Task WriteAppLog(Exception? exception = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (exception != null) { AppRequestLog.IsSuccess = false; @@ -40,20 +44,20 @@ public class LogRepository : ElasticSearchContextAbstract, ILogReposit await AddLogAsync(AppRequestLog); } - public void SendFeiShu(Exception contextException) + public async Task SendFeiShuAsync(Exception contextException) { - LogHelper.Error($""" - 日志请求id:{ServiceProviderRequestLog.RequestId} - 服务商--接口:{ServiceProviderRequestLog.Name}--{ServiceProviderRequestLog.InterfaceName} - 异常原因:{Truncate(contextException.ToString())} - 请求地址:{ServiceProviderRequestLog.Address} - 原始报文: - {Truncate(AppRequestLog.Content)} - 请求报文: - {Truncate(ServiceProviderRequestLog.Content)} - 响应报文: - {Truncate(ServiceProviderRequestLog.Response)} - """); + await _feiShuNotificationService.SendAsync($""" + 日志请求id:{ServiceProviderRequestLog.RequestId} + 服务商--接口:{ServiceProviderRequestLog.Name}--{ServiceProviderRequestLog.InterfaceName} + 异常原因:{Truncate(contextException.ToString())} + 请求地址:{ServiceProviderRequestLog.Address} + 原始报文: + {Truncate(AppRequestLog.Content)} + 请求报文: + {Truncate(ServiceProviderRequestLog.Content)} + 响应报文: + {Truncate(ServiceProviderRequestLog.Response)} + """, LogLevel.Error, CancellationToken.None); } @@ -85,11 +89,12 @@ public class LogRepository : ElasticSearchContextAbstract, ILogReposit //await Context.IndexDocumentAsync(idx); IndexResponse response = await Context.IndexAsync(idx, request => request.Index($"interface_forward-{DateTime.Now:yyyy.MM.dd}")); - if (!response.IsValid) LogHelper.Error("数据添加失败,原因:" + response.DebugInformation); + if (!response.IsValid) + await _feiShuNotificationService.SendAsync("数据添加失败,原因:" + response.DebugInformation, LogLevel.Error, CancellationToken.None); } catch (Exception ex) { - LogHelper.Error($"日志插入ES失败:{ex.Message}。{idx.ToJson()}"); + await _feiShuNotificationService.SendAsync($"日志插入ES失败:{ex.Message}。{idx.ToJson()}", LogLevel.Error, CancellationToken.None); } } @@ -234,7 +239,7 @@ public class LogRepository : ElasticSearchContextAbstract, ILogReposit IsApp = x.IsApp }).ToList(); } - + private static string? Truncate(string? str, int maxLen = 500) { if (str.IsNullOrWhiteSpace()) return str;