Fordítási idő naplózási forrás létrehozása

A forrásalapú naplózást úgy tervezték, hogy magas szintű használható és nagy teljesítményű naplózási megoldást biztosítson a modern .NET-alkalmazásokhoz. Az automatikusan generált forráskód a ILogger interfészre támaszkodik a LoggerMessage.Define funkcionalitással együtt.

A forrásgenerátor akkor aktiválódik, ha LoggerMessageAttribute-t használnak partial naplózási módszereken. Aktiváláskor automatikusan létrehozza az általa dekorált metódusok implementálását partial . Ha probléma merül fel, fordítási idejű diagnosztikát hoz létre a megfelelő használatra vonatkozó tippekkel. Ez a fordítási idő naplózási megoldása futásidőben jelentősen gyorsabb, mint a korábban elérhető naplózási módszerek. Kiküszöböli a boxolást, az ideiglenes foglalásokat és a másolatokat a lehető legnagyobb mértékben.

Alapszintű használat

A LoggerMessageAttribute használatához a fogyasztó osztálynak és metódusnak partial-nek kell lennie. A kódgenerátor fordításkor aktiválódik, és létrehozza a metódus implementációját partial .

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);
}

Az előző példában a naplózási módszer, static a naplószint pedig az attribútumdefinícióban van megadva. Ha statikus környezetben használja LoggerMessageAttribute , a ILogger példányt argumentumként kell átadni. Vagy adja hozzá a this módosítót a paraméterhez a ILogger metódus bővítménymetódusként való definiálásához.

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);
}

Az attribútumot nem statikus környezetben is használhatja. Vegye figyelembe az alábbi példát, ahol a naplózási módszer példánymetódusként van deklarálva. Ebben az összefüggésben a naplózási módszer a naplózót a tartalmazó osztály egyik ILogger mezőjének elérésével kapja meg.

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);
}

A .NET 9-től kezdődően a naplózási módszer emellett lekérheti a naplózót egy ILogger elsődleges konstruktorparaméterből az adott osztályban.

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);
}

Ha van egy ILogger mező és egy elsődleges konstruktor paraméter, akkor a naplózási módszer a naplózót a mezőből szerzi.

Dinamikus naplószint

Néha a naplószintnek dinamikusnak kell lennie, nem pedig statikusan beépítve a kódba. Ezt úgy teheti meg, hogy kihagyja a naplószintet az attribútumból, és ehelyett paraméterként kell megadnia a naplózási módszerhez.

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);
}

Message ingatlan

A(z) Message tulajdonság a(z) LoggerMessageAttribute esetében opcionális. Ha kihagyja, String.Empty a rendszer az üzenethez használja. Ha azonban a naplózási módszer paraméterekkel rendelkezik a megfelelő sablonhelyőrzők nélkül, a fordító SYSLIB1015 figyelmeztetést ad ki. Ezek a paraméterek naplóállapotban vannak tárolva, de nem jelennek meg a formázott naplókimenetben. Csak olyan strukturált naplózási szolgáltatók jelenítik meg azokat, amelyek felsorolják a napló állapotát.

Naplómetódus korlátozásai

A feldíszített LoggerMessageAttribute naplózási módszereknek meg kell felelniük a következő követelményeknek:

  • A naplózási módszereknek meg kell lenniük partial , és vissza kell adniuk void.
  • A naplózási metódusok neve nem kezdődhet aláhúzásjellel.
  • A naplózási módszerek paraméternevei nem kezdődnek aláhúzásjellel.
  • A naplózási módszerek támogatják az általános típusparamétereket, de a C# 13 allows ref struct korlátozásmentesség nem támogatott.
  • A naplózási metódus paraméterei nem használhatják a params, scopedvagy out módosítókat, és nem lehetnek ref struct típusok.
  • Ha naplózási módszerről van szó static, a ILogger példány paraméterként szükséges.

A kódgenerálási modell attól függ, hogy a kód egy modern C#-fordítóval, azaz 9-es vagy újabb verzióval van-e lefordítva. A nyelvi verzió módosításáról további információt a C# nyelvi verziószámozásával kapcsolatban talál.

Naplómetódus anatómiája

A ILogger.Log aláírás elfogadja a LogLevel és opcionálisan egy Exception, a következő kód példában látható módon.

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);
}

Általános szabályként a ILogger, LogLevel és Exception első példányát különleges kezelésben részesítik a forrásgenerátor naplózási metódusának szignatúrájában. A következő példányok az üzenetsablon normál paramétereiként lesznek kezelve:

// 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);

Fontos

A megjelenő figyelmeztetések részletesen ismertetik a helyes használatot LoggerMessageAttribute. Az előző példában a WarningLogMethod egy DiagnosticSeverity.WarningSYSLIB0025 értéket jelent.

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

Kis- és nagybetűket nem megkülönböztető sablonnév támogatása

A generátor kis- és nagybetűk közötti összehasonlítást végez az üzenetsablon elemei és a naplóüzenet argumentumnevei között. Ez azt jelenti, hogy amikor az ILogger enumerálja az állapotot, az argumentumot az üzenetsablon használja, így a naplók könnyebben olvashatók.

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");
    }
}

Fontolja meg a példa naplózási kimenetet a JsonConsole formázó használatakor:

{
  "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}!"
  }
}

Meghatározatlan paramétersorrend

A naplómetódus paramétereinek sorrendjére nincsenek korlátozások. A fejlesztő definiálhatja az ILogger utolsó paramétert, bár kissé kínosnak tűnhet.

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

Jótanács

A naplómetódus paramétereinek sorrendje nem szükséges ahhoz, hogy megfeleljen a sablon helyőrzőinek sorrendjének. A sablon helyőrző neveinek elvárás szerint meg kell felelniük a paramétereknek. Vegye figyelembe a következő JsonConsole kimenetet és a hibák sorrendjét.

{
  "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}"
  }
}

További naplózási példák

Az alábbi minták bemutatják, hogyan kérhető le az esemény neve, hogyan állíthatja be dinamikusan a naplószintet, és hogyan formázhatja a naplózási paramétereket. A naplózási módszerek a következők:

  • LogWithCustomEventName: Eseménynév lekérése attribútumon keresztül LoggerMessage .
  • LogWithDynamicLogLevel: Dinamikusan állítsa be a naplószintet, hogy a naplószint a konfigurációs bemenet alapján legyen beállítva.
  • UsingFormatSpecifier: A naplózási paraméterek formázásához használjon formátumjelölőket.
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);
    }
}

Fontolja meg a példa naplózási kimenetet a SimpleConsole formázó használatakor:

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

Fontolja meg a példa naplózási kimenetet a JsonConsole formázó használatakor:

{
  "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}"
  }
}

Bizalmas adatok kitakarása a naplókban

Bizalmas adatok naplózásakor fontos megelőzni a véletlen expozíciót. A nyers bizalmas értékek naplózása még fordítási időben létrehozott naplózási módszerek esetén is adatszivárgáshoz és megfelelőségi problémákhoz vezethet.

A Microsoft.Extensions.Telemetria kódtár fejlett naplózási és telemetriai bővítési képességeket biztosít .NET alkalmazásokhoz. Kibővíti a naplózási folyamatot, hogy automatikusan alkalmazza a titkosított adatokra a naplók írásakor történő újraírást. Lehetővé teszi az adatvédelmi szabályzatok alkalmazáson keresztüli kikényszerítését a naplózási munkafolyamatba való újralépés integrálásával. Kifinomult telemetriai és naplózási megállapításokat igénylő alkalmazásokhoz készült.

A redaction engedélyezéséhez használja a Microsoft.Extensions.Compliance.Redaction kódtárat . Ez a kódtár olyan összetevőket biztosít, amelyek átalakítják a bizalmas adatokat (például törléssel, maszkolással vagy kivonatolással), hogy biztonságos legyen a kimenet. A redaktorok az adatbesorolás alapján vannak kiválasztva, amely lehetővé teszi az adatok bizalmassági (például személyes, privát vagy nyilvános) szerinti címkézését.

Forrás által generált naplózási módszerek használatához a szerkesztésekhez, tegye a következőket:

  1. Osztályozza a bizalmas adatokat egy adatbesorolási rendszer használatával.
  2. Regisztrálja és konfigurálja a redaktorokat a DI-tárolóban lévő egyes besorolásokhoz.
  3. Engedélyezze az érzékeny adatok elrejtését a naplózási folyamatban.
  4. Ellenőrizze a naplókat, hogy ne tegyenek ki bizalmas adatokat.

Ha például van egy naplóüzenete, amely egy privátnak tekintett paraméterrel rendelkezik:

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

Ehhez hasonló beállításra lesz szüksége:

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");
}

A kimenetnek a következőhöz hasonlónak kell lennie:

User SSN: *****

Ez a megközelítés biztosítja, hogy csak az anonimizált adatok legyenek naplózva, még fordítási idő alatt generált naplózási API-k használata esetén is. A különböző adattípusokhoz vagy besorolásokhoz különböző redaktorokat használhat, és központilag frissítheti a redaction logikát.

Az adatok besorolásáról további információt a Adatbesorolás .NET című témakörben talál. További információ a kitakarásról és a kitakaró eszközökről: Adatok kitakarása a .NET-ben.

Összefoglalás

A C#-forrásgenerátorok megjelenésével a nagy teljesítményű naplózási API-k írása egyszerűbb. A forrásgenerátor megközelítésének számos fő előnye van:

  • Lehetővé teszi a naplózási struktúra megőrzését, és lehetővé teszi az üzenetsablonok által megkövetelt pontos formátumszintaxis megőrzését.
  • Lehetővé teszi alternatív nevek megadását a sablon helyőrzőinek és formátumjelölők használatával.
  • Lehetővé teszi az összes eredeti adat átadását, komplikációk nélkül a tárolásuk módjával kapcsolatban, mielőtt bármit tesznek velük (a string létrehozását leszámítva).
  • Naplózásspecifikus diagnosztikát biztosít, és figyelmeztetéseket ad ki ismétlődő eseményazonosítókhoz.

Emellett a manuális használatnak LoggerMessage.Defineis vannak előnyei:

  • Rövidebb és egyszerűbb szintaxis: Deklaratív attribútumhasználat a sablon kódolása helyett.
  • Irányított fejlesztői élmény: A generátor figyelmeztetéseket ad, amelyek segítenek a fejlesztőknek a helyes lépésben.
  • Tetszőleges számú naplózási paraméter támogatása. LoggerMessage.Define legfeljebb hatot támogat.
  • Dinamikus naplószint támogatása. Ez egyedül nem lehetséges LoggerMessage.Define .

Lásd még