MST
星途 面试题库

面试题:C#日志系统(Serilog/NLog)性能优化与复杂场景处理

在一个高并发的C# Web应用中,使用Serilog或NLog进行日志记录。当每秒有上千条日志需要处理时,如何优化日志系统以避免性能瓶颈?如果需要根据不同的业务场景,将日志分别记录到不同的存储介质(如Elasticsearch、Azure Blob Storage),同时还要保证日志的完整性和一致性,该如何设计和实现?
26.5万 热度难度
编程语言C#

知识考点

AI 面试

面试题答案

一键面试

优化日志系统以避免性能瓶颈

  1. 异步日志记录
    • 在 C# 中,使用 Serilog 时,可以利用 Serilog.Sinks.Async 包。例如:
    Log.Logger = new LoggerConfiguration()
       .WriteTo.Async(a => a.Console())
       .CreateLogger();
    
    • 对于 NLog,配置文件中可以这样设置异步日志记录:
    <targets>
       <target name="asyncConsole" xsi:type="AsyncWrapper">
          <target xsi:type="Console" />
       </target>
    </targets>
    <rules>
       <logger name="*" minlevel="Trace" writeTo="asyncConsole" />
    </rules>
    
    • 异步记录将日志写入操作放到后台线程,避免阻塞主线程,提高高并发场景下应用的响应速度。
  2. 批量写入
    • Serilog:一些 Sinks 支持批量写入,如 Serilog.Sinks.Elasticsearch。可以设置 batchPostingLimit 参数,例如:
    Log.Logger = new LoggerConfiguration()
       .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
        {
           AutoRegisterTemplate = true,
           BatchPostingLimit = 50,
           EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog | EmitEventFailureHandling.RaiseCallback,
        })
       .CreateLogger();
    
    • NLog:对于一些存储介质(如 Elasticsearch)的 Target 也支持批量写入配置。在配置文件中:
    <target xsi:type="ElasticSearch"
            name="elasticsearch"
            index="myindex-${shortdate}"
            batchSize="50">
    </target>
    
    • 批量写入减少了与存储介质的交互次数,提升性能。
  3. 日志级别过滤
    • 在代码中,只记录必要级别的日志。例如,在开发环境可以记录 Debug 级别日志,而在生产环境只记录 Information 及以上级别日志。
    • Serilog:
    Log.Logger = new LoggerConfiguration()
       .MinimumLevel.Information()
       .WriteTo.Console()
       .CreateLogger();
    
    • NLog:在配置文件中:
    <rules>
       <logger name="*" minlevel="Info" writeTo="console" />
    </rules>
    
    • 减少不必要的日志记录,降低系统开销。
  4. 缓存日志
    • 可以使用内存缓存(如 MemoryCache 在 .NET 中)来临时存储日志,达到一定阈值或时间间隔后批量写入存储介质。这可以进一步减少与存储介质的频繁交互。

根据不同业务场景记录到不同存储介质并保证完整性和一致性

  1. 设计
    • 定义业务场景标识:在应用中,为每个业务场景定义唯一标识。例如,订单处理业务场景标识为 OrderProcessing,用户登录业务场景标识为 UserLogin
    • 配置不同存储介质的 Sinks:根据业务场景和存储介质的对应关系,配置 Serilog 或 NLog 的 Sinks。
    • 确保日志完整性:每个日志记录应包含足够的上下文信息,如时间戳、请求 ID(用于跟踪整个请求流程)等,以便在不同存储介质中也能完整还原业务操作。
    • 保证一致性:可以使用分布式事务(如果存储介质支持,如某些云存储的事务特性)或者通过消息队列来确保日志在不同存储介质写入的一致性。例如,使用 Azure Service Bus 作为消息队列,将日志消息发送到队列,然后由不同的消费者将消息写入相应的存储介质。
  2. 实现(以 Serilog 为例)
    • 安装相关包
      • 对于 Elasticsearch:Serilog.Sinks.Elasticsearch
      • 对于 Azure Blob Storage:Serilog.Sinks.AzureBlobStorage
    • 配置 Serilog
    var elasticSearchSinkOptions = new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
    {
       AutoRegisterTemplate = true,
       BatchPostingLimit = 50,
       EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog | EmitEventFailureHandling.RaiseCallback,
    };
    var azureBlobStorageSinkOptions = new AzureBlobStorageSinkOptions
    {
       ConnectionString = "your - connection - string",
       ContainerName = "your - container - name",
       BatchSizeLimit = 50,
       Period = TimeSpan.FromSeconds(30)
    };
    Log.Logger = new LoggerConfiguration()
       .WriteTo.Conditional(
            (logEvent, propertyValue) => propertyValue?.ToString() == "OrderProcessing",
            w => w.Elasticsearch(elasticSearchSinkOptions))
       .WriteTo.Conditional(
            (logEvent, propertyValue) => propertyValue?.ToString() == "UserLogin",
            w => w.AzureBlobStorage(azureBlobStorageSinkOptions))
       .CreateLogger();
    
    • 记录日志时带上业务场景标识
    Log.ForContext("BusinessScenario", "OrderProcessing")
       .Information("Order {OrderId} has been processed", orderId);
    
  3. 实现(以 NLog 为例)
    • 安装相关包
      • 对于 Elasticsearch:NLog.Targets.ElasticSearch
      • 对于 Azure Blob Storage:NLog.Targets.AzureBlobStorage
    • 配置 NLog
    <targets>
       <target name="elasticOrder" xsi:type="ElasticSearch"
               index="order - logs - ${shortdate}"
               batchSize="50">
          <filter condition="contains('${event-properties:item=BusinessScenario}','OrderProcessing')" />
       </target>
       <target name="blobUserLogin" xsi:type="AzureBlobStorage"
               connectionString="your - connection - string"
               container="user - login - logs"
               batchSize="50">
          <filter condition="contains('${event-properties:item=BusinessScenario}','UserLogin')" />
       </target>
    </targets>
    <rules>
       <logger name="*" minlevel="Info" writeTo="elasticOrder" />
       <logger name="*" minlevel="Info" writeTo="blobUserLogin" />
    </rules>
    
    • 记录日志时带上业务场景标识
    var logger = LogManager.GetCurrentClassLogger();
    logger.WithProperty("BusinessScenario", "UserLogin")
       .Info("User {Username} has logged in", username);