我们在开发中经常遇到对方的接口请求有频率限制,比如当前接口每秒的请求不能大于100,不能大于1000,对于这样的,我们作为请求方,如何做限定?
在Serilog中有这么一个东西ILogEventSink
简单意思,就是日志得一个事件!
所以我们可以通过这个事件来干点我们想要的事!
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.WriteTo.Sink(new SerilogCustomHandler(builder.Services), Serilog.Events.LogEventLevel.Error)
.Enrich.FromLogContext()
.CreateLogger();
直接上代码,注意看上面得SerilogCustomHandler,有没有发现我带入了一个参数builder.Services,为啥不用注入得方式呢???
这是一个坑,而且我走过了!!!
先看看SerilogCustomHandler得代码
public class SerilogCustomHandler : ILogEventSink
{
/// <summary>
///
/// </summary>
public IServiceProvider ServiceProvider { get; set; }
/// <summary>
///
/// </summary>
/// <param name="serviceProvider"></param>
public SerilogCustomHandler(IServiceProvider _serviceProvider)
{
ServiceProvider = _serviceProvider;
}
/// <summary>
///
/// </summary>
/// <param name="logEvent"></param>
public void Emit(LogEvent logEvent)
{
try
{
if (ServiceProvider != null)
{
var _channel = ServiceProvider.GetRequiredService<ChannelHelper>();
Console.WriteLine($"channel is null:{(_channel == null)}!");
}
Console.WriteLine("--------- -- ----------- happend error wait to push!" + logEvent.MessageTemplate.ToString());
}
catch (Exception exl)
{
Console.WriteLine(exl.ToString());
}
}
}
可以看到注释部分,我是想使用Channel推送出去的,这个看你的需求改成自己i想要的样子即可!
一起来看看DeepSeek如何说
你的困惑很有道理!让我用一个更直观的方式来解释这个问题。
想象一下程序启动的时间线:
时间点1: Program.Main 开始执行
↓
时间点2: 执行 Log.Logger = new LoggerConfiguration()...
↓ 此时创建了 SerilogCustomSink,但 ServiceProvider 还是 null!
时间点3: 构建 Host,ServiceProvider 被赋值
↓
时间点4: 应用程序运行
关键问题:SerilogCustomSink 在 时间点2 就被创建并”固化”在 Serilog 中,而 ServiceProvider 在 时间点3 才被赋值。
public class Program
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Main(string[] args)
{
Console.WriteLine("时间点1: Main开始");
// 时间点2: 初始化Serilog
Log.Logger = new LoggerConfiguration()
.WriteTo.Sink(new SerilogCustomSink(), LogEventLevel.Error)
.CreateLogger();
Console.WriteLine($"Serilog初始化时 ServiceProvider = {ServiceProvider}"); // null!
// 时间点3: 构建Host
var host = CreateHostBuilder(args).Build();
ServiceProvider = host.Services; // 现在才有值!
Console.WriteLine($"Host构建后 ServiceProvider = {ServiceProvider}"); // 有值了
// 但问题是:SerilogCustomSink 早在时间点2就创建好了!
// 它内部的 ServiceProvider 引用还是 null
host.Run();
}
}
public class SerilogCustomSink : ILogEventSink
{
public SerilogCustomSink()
{
// 构造函数在时间点2执行,此时 ServiceProvider 是 null
Console.WriteLine($"Sink创建时 ServiceProvider = {Program.ServiceProvider}"); // null!
}
public void Emit(LogEvent logEvent)
{
// 即使后来 ServiceProvider 有值了,但这里的引用还是创建时的状态
Console.WriteLine($"日志输出时 ServiceProvider = {Program.ServiceProvider}");
}
}
把 SerilogCustomSink 想象成一个”照片”:
// 就像拍照一样,这一刻的状态被定格了
var sink = new SerilogCustomSink(); // "拍照"时刻:ServiceProvider = null
// 后来改变了原场景
ServiceProvider = host.Services; // 改变了现实
// 但照片不会自动更新!sink 里面看到的还是拍照时的 null
不要依赖会变化的外部状态,而是通过构造函数传入确定的值!
贴代码
已经升级了,主要是left join的查询的时候没有过滤,其实这个问题,多租户也是一样的!