Aracılığıyla paylaş


Derleme zamanı günlüğü kaynak oluşturma

.NET 6 türü tanıtır LoggerMessageAttribute . Bu öznitelik ad alanının bir parçasıdır Microsoft.Extensions.Logging ve kullanıldığında kaynak olarak performans gösteren günlük API'leri oluşturur. Kaynak oluşturma günlük desteği, modern .NET uygulamaları için yüksek oranda kullanılabilir ve yüksek performanslı bir günlük çözümü sunmak üzere tasarlanmıştır. Otomatik olarak oluşturulan kaynak kodu, işlevlerle ILogger birlikte arabirime LoggerMessage.Define dayanır.

Kaynak oluşturucu, günlüğe kaydetme yöntemlerinde LoggerMessageAttribute kullanıldığında tetikleniyorpartial. Tetiklendiğinde, süslenen partial yöntemlerinin uygulanmasını otomatik olarak oluşturabilir veya uygun kullanım hakkında ipuçları içeren derleme zamanı teşhisleri üretebilir. Derleme zamanı günlüğe kaydetme çözümü, çalışma zamanında mevcut günlükleme yaklaşımlarından oldukça daha hızlıdır. Bu, mümkün olan en yüksek ölçüde kutulama, geçici ayırmalar ve kopyalar ortadan kaldırarak bunu başarır.

Temel kullanım

kullanmak LoggerMessageAttributeiçin, tüketen sınıfın ve yönteminin olması partialgerekir. Kod oluşturucu derleme zamanında tetikler ve yönteminin partial bir uygulamasını oluşturur.

public static partial class Log
{
    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Critical,
        Message = "Could not open socket to `{HostName}`")]
    public static partial void CouldNotOpenSocket(
        ILogger logger, string hostName);
}

Yukarıdaki örnekte günlük yöntemidir static ve günlük düzeyi öznitelik tanımında belirtilir. Özniteliği statik bağlamda kullanırken, ILogger örneğin parametre olarak kullanılması gerekir veya yöntemi bir uzantı yöntemi olarak tanımlamak için anahtar sözcüğünü kullanacak this şekilde tanımı değiştirin.

public static partial class Log
{
    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Critical,
        Message = "Could not open socket to `{HostName}`")]
    public static partial void CouldNotOpenSocket(
        this ILogger logger, string hostName);
}

Özniteliği statik olmayan bir bağlamda da kullanmayı seçebilirsiniz. Günlüğe kaydetme yönteminin örnek yöntemi olarak bildirildiği aşağıdaki örneği göz önünde bulundurun. Bu bağlamda günlük yöntemi, içeren sınıftaki bir ILogger alana erişerek günlükçüye sahip olur.

public partial class InstanceLoggingExample
{
    private readonly ILogger _logger;

    public InstanceLoggingExample(ILogger logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Critical,
        Message = "Could not open socket to `{HostName}`")]
    public partial void CouldNotOpenSocket(string hostName);
}

.NET 9'dan başlayarak günlüğe kaydetme yöntemi ek olarak, günlükçü'leri içeren sınıftaki bir ILogger birincil oluşturucu parametresinden alabilir.

public partial class InstanceLoggingExample(ILogger logger)
{
    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Critical,
        Message = "Could not open socket to `{HostName}`")]
    public partial void CouldNotOpenSocket(string hostName);
}

Hem ILogger alanı hem de birincil oluşturucu parametresi varsa, günlük kaydı metodu günlükçüyü alandan alır.

Bazen günlük düzeyinin kodda statik olarak yerleşik olarak değil dinamik olması gerekir. Bunu, günlük düzeyini özniteliğinden çıkararak ve bunun yerine günlük yöntemine parametre olarak gerektirerek yapabilirsiniz.

public static partial class Log
{
    [LoggerMessage(
        EventId = 0,
        Message = "Could not open socket to `{HostName}`")]
    public static partial void CouldNotOpenSocket(
        ILogger logger,
        LogLevel level, /* Dynamic log level as parameter, rather than defined in attribute. */
        string hostName);
}

Günlük mesajını atlayabilirsiniz ve mesaj için String.Empty sağlanmıştır. durum nesnesi, anahtar-değer çiftleri olarak biçimlendirilmiş bağımsız değişkenleri içerir.

using System.Text.Json;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(
    builder =>
    builder.AddJsonConsole(
        options =>
        options.JsonWriterOptions = new JsonWriterOptions()
        {
            Indented = true
        }));

ILogger<SampleObject> logger = loggerFactory.CreateLogger<SampleObject>();
logger.PlaceOfResidence(logLevel: LogLevel.Information, name: "Liana", city: "Seattle");

readonly file record struct SampleObject { }

public static partial class Log
{
    [LoggerMessage(EventId = 23, Message = "{Name} lives in {City}.")]
    public static partial void PlaceOfResidence(
        this ILogger logger,
        LogLevel logLevel,
        string name,
        string city);
}

Biçimlendiriciyi kullanırken örnek günlüğe kaydetme çıkışını JsonConsole göz önünde bulundurun.

{
  "EventId": 23,
  "LogLevel": "Information",
  "Category": "\u003CProgram\u003EF...9CB42__SampleObject",
  "Message": "Liana lives in Seattle.",
  "State": {
    "Message": "Liana lives in Seattle.",
    "name": "Liana",
    "city": "Seattle",
    "{OriginalFormat}": "{Name} lives in {City}."
  }
}

Günlük yöntemi kısıtlamaları

Günlük oluşturma yöntemlerini kullanırken LoggerMessageAttribute bazı kısıtlamalara uyulmalıdır:

  • Günlük yöntemleri olmalıdır partial ve döndürmelidir void.
  • Günlüğe kaydetme yöntemi adları alt çizgiyle başlamamalıdır.
  • Günlük yöntemlerinin parametre adları alt çizgiyle başlamamalıdır.
  • Günlüğe kaydetme yöntemleri genel olamaz.
  • Günlük yöntemi isestaticILogger, örnek parametre olarak gereklidir.

Kod oluşturma modeli, kodun modern bir C# derleyicisi, sürüm 9 veya üzeri ile derlendiğine bağlıdır. C# 9.0 derleyicisi .NET 5 ile kullanılabilir hale geldi. Modern bir C# derleyicisine yükseltmek için proje dosyanızı C# 9.0 hedefine göre düzenleyin.

<PropertyGroup>
  <LangVersion>9.0</LangVersion>
</PropertyGroup>

Daha fazla bilgi için bkz . C# dil sürümü oluşturma.

Log yöntemi anatomisi

İmza, ILogger.Log aşağıdaki kod örneğinde gösterildiği gibi ve isteğe bağlı olarak bir LogLeveldeğerini kabul ederException.

public interface ILogger
{
    void Log<TState>(
        Microsoft.Extensions.Logging.LogLevel logLevel,
        Microsoft.Extensions.Logging.EventId eventId,
        TState state,
        System.Exception? exception,
        Func<TState, System.Exception?, string> formatter);
}

Genel bir kural olarak, ilk , ILoggerve LogLevel örneğiException, kaynak oluşturucunun günlük yöntemi imzasında özel olarak işlenir. Sonraki örnekler, ileti şablonunda normal parametreler olarak değerlendirilir:

// This is a valid attribute usage
[LoggerMessage(
    EventId = 110, Level = LogLevel.Debug, Message = "M1 {Ex3} {Ex2}")]
public static partial void ValidLogMethod(
    ILogger logger,
    Exception ex,
    Exception ex2,
    Exception ex3);

// This causes a warning
[LoggerMessage(
    EventId = 0, Level = LogLevel.Debug, Message = "M1 {Ex} {Ex2}")]
public static partial void WarningLogMethod(
    ILogger logger,
    Exception ex,
    Exception ex2);

Önemli

Gösterilen uyarılar, doğru kullanımıyla LoggerMessageAttributeilgili ayrıntıları sağlar. Yukarıdaki örnekte, WarningLogMethod bir DiagnosticSeverity.Warning değerini SYSLIB0025raporlar.

Don't include a template for `ex` in the logging message since it is implicitly taken care of.

Büyük/küçük harfe duyarlı olmayan şablon adı desteği

Oluşturucu, ileti şablonundaki öğelerle günlük iletisindeki bağımsız değişken adları arasında büyük/küçük harfe duyarlı olmayan bir karşılaştırma yapar. Bu, durum numaralandırıldığında ILogger bağımsız değişkenin ileti şablonu tarafından alınıp günlüklerin daha iyi bir şekilde tüketilebileceği anlamına gelir:

public partial class LoggingExample
{
    private readonly ILogger _logger;

    public LoggingExample(ILogger logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 10,
        Level = LogLevel.Information,
        Message = "Welcome to {City} {Province}!")]
    public partial void LogMethodSupportsPascalCasingOfNames(
        string city, string province);

    public void TestLogging()
    {
        LogMethodSupportsPascalCasingOfNames("Vancouver", "BC");
    }
}

Biçimlendiriciyi kullanırken örnek günlüğe kaydetme çıkışını JsonConsole göz önünde bulundurun:

{
  "EventId": 13,
  "LogLevel": "Information",
  "Category": "LoggingExample",
  "Message": "Welcome to Vancouver BC!",
  "State": {
    "Message": "Welcome to Vancouver BC!",
    "City": "Vancouver",
    "Province": "BC",
    "{OriginalFormat}": "Welcome to {City} {Province}!"
  }
}

Belirsiz parametre sırası

Günlük yöntemi parametrelerinin sıralanmasında herhangi bir kısıtlama yoktur. Geliştirici, ILogger öğesini son parametre olarak tanımlayabilir ancak bu biraz garip görünebilir.

[LoggerMessage(
    EventId = 110,
    Level = LogLevel.Debug,
    Message = "M1 {Ex3} {Ex2}")]
static partial void LogMethod(
    Exception ex,
    Exception ex2,
    Exception ex3,
    ILogger logger);

İpucu

Bir log yöntemindeki parametrelerin sırasının, şablon yer tutucularının sırasına karşılık gelmesi gerekmez. Bunun yerine, şablondaki yer tutucu adlarının parametrelerle eşleşmesi beklenir. Aşağıdaki JsonConsole çıkışı ve hataların sırasını göz önünde bulundurun.

{
  "EventId": 110,
  "LogLevel": "Debug",
  "Category": "ConsoleApp.Program",
  "Message": "M1 System.Exception: Third time's the charm. System.Exception: This is the second error.",
  "State": {
    "Message": "M1 System.Exception: Third time's the charm. System.Exception: This is the second error.",
    "ex2": "System.Exception: This is the second error.",
    "ex3": "System.Exception: Third time's the charm.",
    "{OriginalFormat}": "M1 {Ex3} {Ex2}"
  }
}

Daha fazla günlük örneği

Aşağıdaki örneklerde olay adını alma, günlük düzeyini dinamik olarak ayarlama ve günlüğe kaydetme parametrelerini biçimlendirme işlemleri gösterilmektedir. Günlüğe kaydetme yöntemleri şunlardır:

  • LogWithCustomEventName: Öznitelik aracılığıyla LoggerMessage olay adını alın.
  • LogWithDynamicLogLevel: Günlük düzeyinin yapılandırma girişine göre ayarlanmasına izin vermek için günlük düzeyini dinamik olarak ayarlayın.
  • UsingFormatSpecifier: Günlük parametrelerini biçimlendirmek için biçim tanımlayıcılarını kullanın.
public partial class LoggingSample
{
    private readonly ILogger _logger;

    public LoggingSample(ILogger logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 20,
        Level = LogLevel.Critical,
        Message = "Value is {Value:E}")]
    public static partial void UsingFormatSpecifier(
        ILogger logger, double value);

    [LoggerMessage(
        EventId = 9,
        Level = LogLevel.Trace,
        Message = "Fixed message",
        EventName = "CustomEventName")]
    public partial void LogWithCustomEventName();

    [LoggerMessage(
        EventId = 10,
        Message = "Welcome to {City} {Province}!")]
    public partial void LogWithDynamicLogLevel(
        string city, LogLevel level, string province);

    public void TestLogging()
    {
        LogWithCustomEventName();

        LogWithDynamicLogLevel("Vancouver", LogLevel.Warning, "BC");
        LogWithDynamicLogLevel("Vancouver", LogLevel.Information, "BC");

        UsingFormatSpecifier(logger, 12345.6789);
    }
}

Biçimlendiriciyi kullanırken örnek günlüğe kaydetme çıkışını SimpleConsole göz önünde bulundurun:

trce: LoggingExample[9]
      Fixed message
warn: LoggingExample[10]
      Welcome to Vancouver BC!
info: LoggingExample[10]
      Welcome to Vancouver BC!
crit: LoggingExample[20]
      Value is 1.234568E+004

Biçimlendiriciyi kullanırken örnek günlüğe kaydetme çıkışını JsonConsole göz önünde bulundurun:

{
  "EventId": 9,
  "LogLevel": "Trace",
  "Category": "LoggingExample",
  "Message": "Fixed message",
  "State": {
    "Message": "Fixed message",
    "{OriginalFormat}": "Fixed message"
  }
}
{
  "EventId": 10,
  "LogLevel": "Warning",
  "Category": "LoggingExample",
  "Message": "Welcome to Vancouver BC!",
  "State": {
    "Message": "Welcome to Vancouver BC!",
    "city": "Vancouver",
    "province": "BC",
    "{OriginalFormat}": "Welcome to {City} {Province}!"
  }
}
{
  "EventId": 10,
  "LogLevel": "Information",
  "Category": "LoggingExample",
  "Message": "Welcome to Vancouver BC!",
  "State": {
    "Message": "Welcome to Vancouver BC!",
    "city": "Vancouver",
    "province": "BC",
    "{OriginalFormat}": "Welcome to {City} {Province}!"
  }
}
{
  "EventId": 20,
  "LogLevel": "Critical",
  "Category": "LoggingExample",
  "Message": "Value is 1.234568E+004",
  "State": {
    "Message": "Value is 1.234568E+004",
    "value": 12345.6789,
    "{OriginalFormat}": "Value is {Value:E}"
  }
}

Günlüklerde hassas bilgileri sansürleme

Hassas verilerin günlüğe kaydedilmesi sırasında, yanlışlıkla açığa çıkmasını önlemek önemlidir. Derleme zamanında üretilen günlük yöntemlerinde bile ham hassas değerlerin günlüğe kaydedilmesi veri sızıntılarına ve uyumluluk sorunlarına yol açabilir.

Microsoft.Extensions.Telemetry kitaplığı, .NET uygulamaları için gelişmiş günlük ve telemetri zenginleştirme özellikleri sağlar. Günlükleri yazarken sınıflandırılmış verileri otomatik olarak gizlemek için günlük işlem hattını genişletir. Günlük iş akışınıza redaksiyon uygulamasını tümleştirerek uygulamanız genelinde veri koruma ilkelerini zorunlu kılmanıza olanak tanır. Gelişmiş telemetri ve kayıt yönetimi içgörülerine ihtiyaç duyan uygulamalar için tasarlanmıştır.

Yeniden düzeltmeyi etkinleştirmek için Microsoft.Extensions.Compliance.Redaction kitaplığını kullanın. Bu kitaplık, örneğin silme, maskeleme veya karma oluşturma yoluyla hassas verileri dönüştüren bileşenler sağlayan düzenleyiciler içerir ve böylece verileri güvenli bir şekilde sunar. Redaktörler, verileri duyarlılığına göre (kişisel, özel veya genel gibi) etiketlemenizi sağlayan veri sınıflandırmasına göre seçilir.

Kaynak tarafından oluşturulan loglama yöntemleriyle sansür veya düzenleme yapmak için şunları izlemeniz gerekir:

  1. Veri sınıflandırma sistemi kullanarak hassas verilerinizi sınıflandırma.
  2. DI kapsayıcınızdaki her sınıflandırma için redaktörleri kaydedin ve yapılandırın.
  3. Günlük işlem hattında yeniden düzeltmeyi etkinleştirin.
  4. Hassas verilerin gösterilmediğinden emin olmak için günlüklerinizi denetleyin.

Örneğin, özel olarak kabul edilen bir parametresi olan bir günlük iletiniz varsa:

[LoggerMessage(0, LogLevel.Information, "User SSN: {SSN}")]
public static partial void LogPrivateInformation(
    this ILogger logger,
    [MyTaxonomyClassifications.Private] string SSN);

Şuna benzer bir ayara sahip olmanız gerekir:

using Microsoft.Extensions.Telemetry;
using Microsoft.Extensions.Compliance.Redaction;

var services = new ServiceCollection();
services.AddLogging(builder =>
{
    // Enable redaction.
    builder.EnableRedaction();
});

services.AddRedaction(builder =>
{
    // configure redactors for your data classifications
    builder.SetRedactor<StarRedactor>(MyTaxonomyClassifications.Private);
});

public void TestLogging()
{
    LogPrivateInformation("MySSN");
}

Çıkış şu şekilde olmalıdır:

User SSN: *****

Bu yaklaşım, derleme zamanında oluşturulan günlük API'leri kullanılırken bile yalnızca gizlenmiş verilerin günlüğe kaydedilmesini sağlar. Farklı veri türleri veya sınıflandırmalar için farklı redaktörler kullanabilir ve redaksiyon mantığınızı merkezi olarak güncelleştirebilirsiniz.

Verilerinizi sınıflandırma hakkında daha fazla bilgi için bkz. .NET'te veri sınıflandırma. Redaksiyon ve redaktörler hakkında daha fazla bilgi için bkz. .NET'te veri redaksiyon.

Özet

C# kaynak oluşturucularının ortaya çıkmasıyla birlikte, yüksek performanslı günlük API'leri yazmak daha kolaydır. Kaynak oluşturucu yaklaşımını kullanmanın çeşitli temel avantajları vardır:

  • Günlük yapısının korunmasına izin verir ve İleti Şablonları için gereken tam biçim söz dizimini etkinleştirir.
  • Şablon yer tutucuları için alternatif adlar sağlamanıza ve biçim tanımlayıcılarını kullanmanıza izin verir.
  • Tüm özgün verilerin olduğu gibi geçirilmesine izin verir. Bu veriler, bir işlem yapılmadan önce nasıl depolandığıyla ilgili herhangi bir stringsorun yaşamadan (oluşturma dışında).
  • Günlüğe özgü tanılamalar sağlar ve yinelenen olay kimlikleri için uyarılar yayar.

Ayrıca, el ile kullanmanın LoggerMessage.Defineavantajları da vardır:

  • Daha kısa ve daha basit söz dizimi: Ortak kodlama yerine bildirim temelli öznitelik kullanımı.
  • Destekli geliştirici deneyimi: Oluşturucu, geliştiricilerin doğru şeyi yapmalarına yardımcı olmak için uyarılar verir.
  • Rastgele sayıda günlük parametresi desteği. LoggerMessage.Define en fazla altıyı destekler.
  • Dinamik günlük düzeyi desteği. Bu, LoggerMessage.Define tek başına mümkün değildir.

Ayrıca bkz.