You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多串口项目中如何为同一服务注入不同Logger?

多串口场景下服务日志隔离的优化方案

需求梳理

我正在开发一个多串口通信项目,每个Procedure对应一个串口,调用相同服务时,希望日志能按Procedure(串口)输出到不同文件——比如Procedure 1调用服务的日志进文件1,Procedure 2的进文件2。现有两个方案都有明显缺点,想找更优解或者判断哪个方案更合适。

现有方案问题分析

方案1:每个Procedure创建带专属Logger的服务实例

代码如下:

public IProcedure CreateProcedure(int portNumber)
{
    Serilog.ILogger logger = CreateLogger(portNumber);

    Service1 service1 = ServiceFactory1.Create(logger);
    Service2 service2 = ServiceFactory2.Create(logger);
    Service3 service3 = ServiceFactory3.Create(logger);
    Service4 service4 = ServiceFactory4.Create(logger);
    Service5 service5 = ServiceFactory5.Create(logger);

    List<IStage> stages = new()
    {
        new Stage1(logger.As<Stage1>(), service1),
        new Stage2(logger.As<Stage2>(), service1),
        new Stage3(logger.As<Stage3>(), service1),
        new Stage4(logger.As<Stage4>(), service1),
        new Stage5(logger.As<Stage5>(), service1, service2, service3),
        new Stage6(logger.As<Stage6>(), service1, service4),
        new Stage7(logger.As<Stage7>(), service1, service2),
        new Stage8(logger.As<Stage8>(), service1, service5),
    };
        
    return new Procedure(logger.As<Procedure>(), service1, stages);
}

问题:每个服务都要写工厂类,重复代码冗余;串口数量多的时候,服务实例会线性增长,资源开销变大,可能影响性能。

方案2:单服务实例+LoggerCollection路由

代码如下:

public class Service1 : IService1
{
    ILoggerCollection _loggers;

    public Service1(ILoggerCollection loggers)
    {
        _loggers = loggers;
    }

    public void Method1(string value, int comport)
    {
        loggers.Get(comport).LogWarning(comport);

        // Do thing.
    }
}

问题:服务里混入了日志路由逻辑,违反单一职责;每个方法都要额外传串口参数,污染了服务API,后续维护成本高。

最优方案:用日志上下文(Log Context)实现无侵入隔离

利用Serilog的LogContext(其他主流日志框架也有类似功能),在Procedure的执行上下文里打上串口标识,日志框架自动根据这个标识把日志分到对应文件,完美解决前两个方案的痛点。

具体实现步骤

  1. 配置Serilog,按上下文字段分文件输出
    先在日志配置里,指定根据ComPortNumber这个上下文字段来路由日志文件:

    Log.Logger = new LoggerConfiguration()
        .Enrich.FromLogContext() // 启用上下文 enrichment
        .WriteTo.Map(
            keyPropertyName: "ComPortNumber", // 按这个字段的值分文件
            defaultKey: "default", // 默认文件(无上下文时使用)
            (key, writeTo) => writeTo.File($"Logs/ComPort-{key}.txt") // 每个key对应独立文件
        )
        .CreateLogger();
    
  2. 在Procedure执行前注入上下文
    每个Procedure启动执行时,用LogContext.PushProperty把串口号注入到当前上下文,这个上下文会覆盖整个Procedure的执行过程,包括调用的所有服务、Stage:

    public class Procedure
    {
        private readonly int _portNumber;
        private readonly IService1 _service1;
        private readonly ILogger<Procedure> _logger;
    
        public Procedure(int portNumber, IService1 service1, ILogger<Procedure> logger)
        {
            _portNumber = portNumber;
            _service1 = service1;
            _logger = logger;
        }
    
        public async Task ExecuteAsync()
        {
            // 注入串口上下文,using块确保执行完自动清理
            using (LogContext.PushProperty("ComPortNumber", _portNumber))
            {
                _logger.LogInformation("Procedure 开始执行");
                await _service1.Method1("test value");
                // 其他Stage的执行逻辑...
            }
        }
    }
    
  3. 服务保持纯净,无需修改
    服务里只需要注入普通的Logger,完全不用管日志要输出到哪个文件,业务逻辑和日志逻辑彻底分离:

    public class Service1 : IService1
    {
        private readonly ILogger<Service1> _logger;
    
        public Service1(ILogger<Service1> logger)
        {
            _logger = logger;
        }
    
        public async Task Method1(string value)
        {
            _logger.LogWarning("Service1.Method1 被调用,参数:{Value}", value);
            // 正常业务逻辑...
        }
    }
    

这个方案的优势

  • 资源开销最小:所有Procedure共享同一个服务实例,不会因串口数量增加导致实例膨胀。
  • 代码最干净:服务API无需添加多余参数,也不用写日志路由逻辑,完全符合单一职责原则。
  • 上下文自动传递:只要在Procedure的作用域内注入了标识,所有嵌套调用的代码都会自动继承这个上下文,日志自动分到对应文件,无需手动传递参数。
  • 扩展性强:后续如果要按其他维度分日志(比如用户、任务类型),只需修改上下文字段和日志配置,无需改动业务代码。

方案选择建议

如果你的串口数量很少(比如个位数),方案1其实也能凑合用,毕竟实现简单、逻辑直观;但如果串口数量多,或者你追求代码整洁和性能最优,日志上下文方案绝对是最佳选择,完美解决了前两个方案的所有问题。

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:33:13