『贴代码』
PasteSpider
PasteApeart
精选作品
优选问答
成长笔记
【本期话题】更多
                                    我们在开发中经常遇到对方的接口请求有频率限制,比如当前接口每秒的请求不能大于100,不能大于1000,对于这样的,我们作为请求方,如何做限定?
                    
Serilog的全局处理,发生异常后,如何统一处理?只要引入一个ILogEventSink即可,一起瞧瞧去
贴代码 2025-11-11 125 0 0
在开发过程中,相信日志对于大家来说再熟悉不过了,不过我个人是不赞成到处打日志的,因为日志打得多,严重影响性能!!!那么多异常等信息,你要如何统一处理呢?

在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

解决方案的本质

不要依赖会变化的外部状态,而是通过构造函数传入确定的值!

评论列表
贴代码
0 125 0
快捷注册
用户问答更多
PasteApeart和PasteForm如何选择?
  • 不用选,后面都是PasteApeart,他们其实是一个东西

PasteApeart中有多层级继承的,表单中的字段排列顺序有什么规则?
  • 看版本,PasteApeart.Helper 26.9.2之后,就按照基类前面的顺序了,也符合阅读顺序,当然你可以使用PasteSort再次改变他!

PasteForm,神奇了啊!都说低代码,低代码,那个是对于非开发者来说的,感觉PasteForm就是开发者的低代码啊!只要配置好Model,就有对应的CRUD操作,还带一些逻辑,和生成代码是两码事了!!!问题来了,有什么局限性,或者适用哪些场景?
  • 1.语言吧,这本身只是一个开发思想,其实其他语言也可以用,不过最优雅实现的还是.NET

    2.特别适用于需要表单,表格的系统,所以哪些EMS,CRM,ERP等都合适

    3.扩展,比如这个思想,你也可以应用于C/S,理论上也是可以的

    4.这个主要针对的是管理端,附带UI的CRUD,也就是说对于业务代码,是没影响的!

    5.可能入门会麻烦点,毕竟有接近100个PasteXXXX特性要了解,幸运的是都是Paste开头,官方的也支持,比如MaxLength,Required等!

最新动态
  • 52.****.182 正在查看 文章列表页 !
  • 52.****.182 正在查看 文章列表页 !
  • 20.****.220 正在查看 主页pastespider !
  • 52.****.216 正在查看 PasteApeart框架介绍,PasteApeart为啥说是敏佳开发天花板,看这篇就够了 !
  • 216.****.137 正在查看 outer或者outers等如何弹出特定查询页面? !
  • 216.****.137 正在查看 贴代码框架PasteApeart特性介绍之markdown和richtext !
  • 216.****.137 正在查看 PasteSpider更新摘要,持续更新 !
  • 216.****.137 正在查看 最佳CRUD实践之联动管理端,API和小程序之实际案例改造分享(四) !
  • 216.****.137 正在查看 PasteSpider的V5正式版发布啦!(202504月版),更新说明一览 !
  • 216.****.137 正在查看 容器列表什么时候更新? !
  • 216.****.137 正在查看 贴代码框架PasteApeart特性介绍之datetime,daterange !
欢迎加入QQ讨论群 296245685 [PasteSpider]介绍 [PasteForm]介绍 @2022-2023 PasteCode.cn 版权所有 ICP证 闽ICP备2021013869号-2