调用外部API时添加自定义前置/后置逻辑的最优实践是什么?
针对你场景的方案优化
你当前用自定义Sink的方案不是过度设计,恰恰是Serilog官方推荐的标准扩展方式,甚至可以再简化到不用单独定义Sink类:
// 直接在Serilog配置里写逻辑,不用单独实现CheckLevelSink和扩展方法 loggerConfig.WriteTo.Sink(new DelegatingSink(logEvent => { LoggingUtils.CheckForceExiting(logEvent.Level); }), LogEventLevel.Error);
因为你已经给Sink配置了restrictedToMinimumLevel = Error,低于Error级别的日志根本不会进入这个Sink,CheckForceExiting里的等级判断逻辑也可以直接删掉,进一步简化代码。
外部API追加自定义逻辑的通用最优实践
按优先级从高到低排列:
- 优先使用框架/库自带的扩展点
几乎所有成熟的第三方库都会预留扩展入口,比如Serilog的Sink、ASP.NET Core的中间件、Entity Framework的拦截器、HttpClient的委托处理器等。这类扩展是原生设计支持的,侵入性最低,也不会和库的版本更新产生冲突,是首选方案。 - 装饰器模式
如果第三方库没有预留扩展点,就定义和第三方API一致的抽象接口,自己实现一个装饰器类,在类内部调用原生API的前后插入自定义逻辑。比如你用的日志库是ILogger接口,就实现ForceExitLogger : ILogger,构造函数注入原生的ILogger实例,在Log方法里先调用自定义检查逻辑,再调用原生ILogger的Log方法,之后依赖注入的时候注册你的装饰器实现即可,原有业务代码的调用不需要做任何修改。 - 工具类封装
如果第三方API是静态类(比如你最开始遇到的Serilog.Log静态类),没法用接口装饰,就自己封装一个静态工具类,只暴露你需要用到的方法,在方法内部调用原生静态API前后加自定义逻辑,把所有业务侧的调用从原生API改成你自己的工具类即可,不需要全量复制原生类的所有代码,只需要封装你用到的方法。 - AOP面向切面编程
如果调用点非常多,挨个改调用成本太高,可以用AOP框架(比如Castle DynamicProxy、.NET Source Generator生成代理代码),在编译或运行时自动给目标API的调用织入自定义前后逻辑,不需要修改原有业务代码。 - 继承/修改源码是优先级最低的方案,仅在前几种方案都无法实现时考虑,尤其是密封类无法继承,修改源码会导致后续版本更新维护成本极高,不推荐使用。
内容的提问来源于stack exchange,提问作者TRex
相关产品推荐
相关产品推荐

