面试题答案
一键面试优化日志系统以避免性能瓶颈
- 异步日志记录
- 在 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>
- 异步记录将日志写入操作放到后台线程,避免阻塞主线程,提高高并发场景下应用的响应速度。
- 在 C# 中,使用 Serilog 时,可以利用
- 批量写入
- 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>
- 批量写入减少了与存储介质的交互次数,提升性能。
- Serilog:一些 Sinks 支持批量写入,如
- 日志级别过滤
- 在代码中,只记录必要级别的日志。例如,在开发环境可以记录
Debug
级别日志,而在生产环境只记录Information
及以上级别日志。 - Serilog:
Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .WriteTo.Console() .CreateLogger();
- NLog:在配置文件中:
<rules> <logger name="*" minlevel="Info" writeTo="console" /> </rules>
- 减少不必要的日志记录,降低系统开销。
- 在代码中,只记录必要级别的日志。例如,在开发环境可以记录
- 缓存日志
- 可以使用内存缓存(如
MemoryCache
在 .NET 中)来临时存储日志,达到一定阈值或时间间隔后批量写入存储介质。这可以进一步减少与存储介质的频繁交互。
- 可以使用内存缓存(如
根据不同业务场景记录到不同存储介质并保证完整性和一致性
- 设计
- 定义业务场景标识:在应用中,为每个业务场景定义唯一标识。例如,订单处理业务场景标识为
OrderProcessing
,用户登录业务场景标识为UserLogin
。 - 配置不同存储介质的 Sinks:根据业务场景和存储介质的对应关系,配置 Serilog 或 NLog 的 Sinks。
- 确保日志完整性:每个日志记录应包含足够的上下文信息,如时间戳、请求 ID(用于跟踪整个请求流程)等,以便在不同存储介质中也能完整还原业务操作。
- 保证一致性:可以使用分布式事务(如果存储介质支持,如某些云存储的事务特性)或者通过消息队列来确保日志在不同存储介质写入的一致性。例如,使用 Azure Service Bus 作为消息队列,将日志消息发送到队列,然后由不同的消费者将消息写入相应的存储介质。
- 定义业务场景标识:在应用中,为每个业务场景定义唯一标识。例如,订单处理业务场景标识为
- 实现(以 Serilog 为例)
- 安装相关包:
- 对于 Elasticsearch:
Serilog.Sinks.Elasticsearch
- 对于 Azure Blob Storage:
Serilog.Sinks.AzureBlobStorage
- 对于 Elasticsearch:
- 配置 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);
- 安装相关包:
- 实现(以 NLog 为例)
- 安装相关包:
- 对于 Elasticsearch:
NLog.Targets.ElasticSearch
- 对于 Azure Blob Storage:
NLog.Targets.AzureBlobStorage
- 对于 Elasticsearch:
- 配置 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);
- 安装相关包: