使用记录器添加自定义日志消息

已完成

.NET 提供了可用于记录自定义遥测数据的 API。 OpenTelemetry 可以导出该数据。

在本单元中,你将了解如何编写高效的代码,将事件发送到结构化日志。

ILogger 对象

当你基于模板创建项目或将现有项目添加到 .NET Aspire 业务流程时,.NET Aspire 工具会自动设置 OpenTelemetry API。 当你需要记录遥测数据时,无需创建自己的日志记录、指标或跟踪对象。 相反,可以在微服务中使用依赖项注入来检索它们。

例如,在以下 BasketService 类中,类声明中包含了一个 ILogger 对象。 可以在类中的任意位置使用该记录器来写入事件:

public class BasketService(
    IBasketRepository repository,
    ILogger<BasketService> logger) : Basket.BasketBase
{
    [AllowAnonymous]
    public override async Task<CustomerBasketResponse> GetBasket(
	    GetBasketRequest request, ServerCallContext context)
    {
        var userId = context.GetUserIdentity();

        // Use the logger to write events
        if (logger.IsEnabled(LogLevel.Debug))
        {
            logger.LogDebug("Begin GetBasketById call from method {Method} for basket id {userId}", context.Method, userId);
        }

        var data = await repository.GetBasketAsync(userId);

        return new();
    }
}

高效日志记录

日志记录有助于使微服务具有可观测性。 当应用经过测试、暂存并部署到生产环境时,全面的日志代码可帮助快速诊断故障或瓶颈。 因此,尝试记录一切内容。 不过,虽然日志记录速度很快,但并不是零成本,因此应注意高效记录。

供应商通常根据引入的数据量对应用程序性能管理 (APM) 系统计费。 为消息选择适当的日志级别和默认收集级别可以对月度账单产生很大影响。 日志收集级别可以按提供程序设置,这通常是 ILogger<T> 中使用的类型名称。

每次记录时都使用以下方法:

  • 确保想要使用的日志记录级别已启用。 可用级别包括信息、警告、错误和严重。 管理员可以在测试、暂存和部署到生产环境时启用不同的级别。 日志输出通过 IConfiguration 进行控制,通常使用 appsettings.json 或环境变量。
  • 记录的消息中避免出现字符串内插。 内插字符串使用 $ 符号定义,即使未启用所选日志记录级别,也会进行评估。 请改用 LogInformation()LogDebug() 等日志方法,并传递参数列表中的参数。
  • 使用编译时源生成进一步优化日志记录性能,并为每个日志消息创建唯一标识符,这对于在 APM 中查询日志消息非常有用。

编译时源生成

使用 ILogger 对象的编译时源生成执行一次字符串分析,而不是针对每个日志记录请求来执行,从而降低了日志记录成本。 它还包括每种类型的日志消息的 ID。 若要使用此方法,请使用日志记录参数定义部分日志记录方法,并向其应用 LoggerMessageAttribute。 .NET 在代码编译过程中会自动生成完整的日志记录方法。

请注意,在 .NET Aspire 中,无需创建 ILogger,而是可以从依赖项注入获取它:

public partial class BasketService(
    IBasketRepository repository,
    ILogger<BasketService> logger) : Basket.BasketBase
{
    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Information,
        Message = "Obtaining a basket from method {Method} for basket {basketId}")]
    public partial void LogGetBasket(string Method, int basketId);
}

了解详细信息