다음을 통해 공유


.NET 라이브러리 작성자용 로깅 지침

라이브러리 작성자로서 로깅 노출은 소비자에게 라이브러리의 내부 작동에 대한 인사이트를 제공하는 좋은 방법입니다. 이 지침은 다른 .NET 라이브러리 및 프레임워크와 일치하는 방식으로 로깅을 노출하는 데 도움이 됩니다. 또한 달리 명확하지 않을 수 있는 일반적인 성능 병목 상태를 방지하는 데 도움이 됩니다.

ILoggerFactory 인터페이스를 언제 사용해야 하는지

로그를 내보내는 라이브러리를 작성할 때 로그를 ILogger 기록하려면 개체가 필요합니다. 해당 개체를 가져오기 위해 API는 ILogger<TCategoryName> 매개변수를 수락하거나, ILoggerFactory를 호출한 후에 ILoggerFactory.CreateLogger을 수락할 수 있습니다. 어떤 방법을 선호해야 하나요?

  • 모든 클래스가 로그를 내보낼 수 있도록 여러 클래스에 전달할 수 있는 로깅 개체가 필요한 경우 사용합니다 ILoggerFactory. 각 클래스는 클래스와 동일한 이름을 가진 별도의 범주를 사용하여 로그를 만드는 것이 좋습니다. 이렇게 하려면 로그를 내보내는 각 클래스에 대해 고유한 ILogger<TCategoryName> 개체를 생성하도록 팩터리가 필요합니다. 일반적인 예로는 라이브러리에 대한 공용 진입점 API 또는 내부적으로 도우미 클래스를 만들 수 있는 형식의 공용 생성자가 포함됩니다.

  • 한 클래스 내에서만 사용되며 공유되지 않는 로깅 객체가 필요할 때는 로그를 생성하는 형식 ILogger<TCategoryName>을 사용합니다. 이 예제의 일반적인 예는 종속성 주입으로 만든 클래스에 대한 생성자입니다.

시간이 지남에 따라 안정적으로 유지해야 하는 공용 API를 디자인하는 경우 나중에 내부 구현을 리팩터링할 수 있습니다. 클래스가 처음에 내부 도우미 형식을 만들지 않더라도 코드가 진화함에 따라 변경될 수 있습니다. 공용 ILoggerFactory API를 변경하지 않고 새 클래스에 대한 새 ILogger<TCategoryName> 개체를 만들 수 있습니다.

자세한 내용은 필터링 규칙이 적용되는 방법을 참조하세요.

소스 생성 로그를 선호합니다

API는 ILogger API를 사용하는 두 가지 방법을 지원합니다. LoggerExtensions.LogErrorLoggerExtensions.LogInformation와 같은 메서드를 호출하거나 로깅 소스 생성기를 사용하여 엄격히 형식화된 로깅 메서드를 정의할 수 있습니다. 대부분의 경우 원본 생성기는 뛰어난 성능과 더 강력한 입력을 제공하므로 권장됩니다. 또한 메시지 템플릿, ID 및 로그 수준과 같은 로깅 관련 문제를 호출 코드에서 격리합니다. 소스에서 생성되지 않은 접근 방식은 코드를 보다 간결하게 만들기 위해 이러한 이점을 포기하려는 시나리오에 주로 유용합니다.

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

앞의 코드는 다음과 같습니다.

  • partial class 형식에 대한 확장 메서드를 정의할 수 있도록 LogMessages 유형에 대해 명명된 static를 정의하는 ILogger입니다.
  • LogProductSaleDetails 확장 메서드를 LoggerMessage 특성 및 Message 템플릿으로 데코레이트합니다.
  • LogProductSaleDetails를 선언하여 ILogger을 확장하고 quantitydescription를 수락합니다.

팁 (조언)

디버깅하는 동안 소스 생성 코드는 호출하는 코드와 동일한 어셈블리의 일부이기 때문에 한 단계씩 실행할 수 있습니다.

비용이 많이 드는 매개 변수 평가를 방지하는 데 사용 IsEnabled

매개 변수를 평가하는 데 비용이 많이 드는 경우가 있을 수 있습니다. 이전 예제를 확장하면 매개 변수가 descriptionstring 계산에 비용이 많이 든다고 가정합니다. 판매 중인 제품이 사용자 친화적인 제품 설명을 가지고 데이터베이스 쿼리 또는 파일 읽기에 의존할 수 있습니다. 이러한 상황에서는 소스 생성기에 IsEnabled 가드를 건너뛰고 호출 사이트에서 IsEnabled 가드를 수동으로 추가하도록 지시할 수 있습니다. 이렇게 하면 사용자가 가드가 호출되는 위치를 확인할 수 있으며 컴퓨팅 비용이 많이 들 수 있는 매개 변수가 진정으로 필요한 경우에만 평가되도록 할 수 있습니다. 다음 코드를 고려합니다.

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages
{
    [LoggerMessage(
        Message = "Sold {Quantity} of {Description}",
        Level = LogLevel.Information,
        SkipEnabledCheck = true)]
    internal static partial void LogProductSaleDetails(
        this ILogger logger,
        int quantity,
        string description);
}

LogProductSaleDetails 확장 메서드가 호출될 때, IsEnabled 가드가 수동으로 실행되며, 비용이 많이 드는 매개변수 평가는 필요할 때만 제한됩니다. 다음 코드를 고려합니다.

if (_logger.IsEnabled(LogLevel.Information))
{
    // Expensive parameter evaluation
    var description = product.GetFriendlyProductDescription();

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

자세한 내용은 .NET의 컴파일 시간 로깅 원본 생성고성능 로깅을 참조하세요.

로깅에서 문자열 보간 방지

일반적인 실수는 문자열 보간 을 사용하여 로그 메시지를 작성하는 것입니다. 로깅의 문자열 보간은 해당 LogLevel 문자열이 활성화되지 않은 경우에도 평가되므로 성능에 문제가 있습니다. 문자열 보간 대신 로그 메시지 템플릿, 서식 및 인수 목록을 사용합니다. 자세한 내용은 .NET: 로그 메시지 템플릿의 로깅을 참조하세요.

no-op 로깅 기본값 사용

라이브러리가 ILogger 또는 ILoggerFactory 둘 중 하나를 요구하는 로깅 API를 노출할 때, 로거를 제공하고 싶지 않을 경우가 있을 수 있습니다. 이러한 경우 Microsoft.Extensions.Logging.Abstractions NuGet 패키지는 no-op 로깅 기본값을 제공합니다.

라이브러리 소비자는 이(가) 제공되지 않은 경우 기본값으로 ILoggerFactory을 사용할 수 있습니다. null 로깅의 사용은 형식이 null이 아니므로 형식을 nullable(ILoggerFactory?)로 정의하는 것과 다릅니다. 이러한 편의 기반 형식은 아무것도 기록하지 않으며 기본적으로 no-ops입니다. 해당하는 경우 사용 가능한 추상화 형식을 사용하는 것이 좋습니다.