upgrade:fake 8.0.5.2 fei shu notice,rem log helper

This commit is contained in:
xiaolipro 2026-02-25 11:55:02 +08:00
parent fb02eeeb21
commit 8c8647a0ca
13 changed files with 54 additions and 223 deletions

View File

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<!-- All Fake packages -->
<FakePackageVersion>8.0.2</FakePackageVersion>
<FakePackageVersion>8.0.5.2</FakePackageVersion>
</PropertyGroup>
</Project>

View File

@ -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;
@ -125,11 +121,11 @@ public class InterfaceForwardApiModule : FakeModule
app.MapControllers();
LogHelper.Info("Application started", true);
app.Services.GetRequiredService<IFeiShuNotificationService>().Send("Application started");
}
public override void Shutdown(ApplicationShutdownContext context)
{
LogHelper.Info("Application stoped", true);
context.ServiceProvider.GetRequiredService<IFeiShuNotificationService>().Send("Application stoped");
}
}

View File

@ -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();

View File

@ -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<IHttpContextAccessor>().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} ");
}
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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<AppSubscribeConfigEntity> _appSubscribeConfigRepository;
private readonly RabbitClient _rabbitClient;
public AppSubscribeHostService(IBasicRepository<AppSubscribeConfigEntity> 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)
{

View File

@ -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
{

View File

@ -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)

View File

@ -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<object>()
{
new List<object>()
{
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);
}
}

View File

@ -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<FeiShuNoticeOptions>()!);
}
}
}

View File

@ -40,7 +40,7 @@ namespace InterfaceForward.Repositories.Log.Services
/// 发飞书
/// </summary>
/// <param name="contextException"></param>
void SendFeiShu(Exception contextException);
Task SendFeiShuAsync(Exception contextException);
/// <summary>
/// 添加日志

View File

@ -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;
/// </summary>
public class LogRepository : ElasticSearchContextAbstract<LogIndex>, 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<LogIndex>, 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<LogIndex>, 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<LogIndex>, 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);
}
}