Megosztás:


Nagy teljesítményű naplózás a .NET-ben

A .NET 6-os és újabb verzióiban a nagy teljesítményű naplózási forgatókönyvekhez használja a LoggerMessageAttribute és a fordítási idejű forrásgenerálást. Ez a módszer a legjobb teljesítményt nyújtja a futásidőben történő boxolás, az ideiglenes foglalások és az üzenetsablonok elemzésének megszüntetésével.

A forrás által létrehozott naplózás a következő teljesítménybeli előnyöket nyújtja a naplózóbővítmény-metódusokkal szemben, példáulLogInformation:LogDebug

  • Kiküszöböli a boxolást: A naplózási bővítmény metódusaihoz "boxing" (konvertálás) típusú értéktípusokra van szükség, például int: object. A forrás által generált naplózás az erősen beírt paraméterek használatával elkerüli a boxolást.
  • Sablonok elemzése fordításkor: A naplózó bővítmény metódusainak minden alkalommal elemezniük kell az üzenetsablont (elnevezett formátumsztringet), amikor naplóüzenetet írnak. A forráskód alapú naplózás a fordítási idő alatt egyszer elemzi a sablonokat.
  • Csökkenti a foglalásokat: A forrásgenerátor optimalizált kódot hoz létre, amely minimalizálja az objektumfoglalásokat és az ideiglenes memóriahasználatot.

A mintaalkalmazás nagy teljesítményű naplózási funkciókat mutat be egy prioritási üzenetsor-feldolgozó szolgáltatással. Az alkalmazás prioritási sorrendben dolgozza fel a munkaelemeket. A műveletek során a rendszer a naplóüzeneteket forrás által generált naplózással hozza létre.

Jótanács

Az összes naplózási példa forráskódja letölthető a Mintaböngészőben . További információ: Kódminták tallózása: Naplózás a .NET-ben.

Naplózóüzenetek definiálása forrásgenerálással

Nagy teljesítményű naplóüzenetek létrehozásához a .NET 6-ban és későbbi verziókban, definiáljon partial metódusokat, melyek LoggerMessageAttribute díszítéssel vannak ellátva. A forrásgenerátor fordításkor hozza létre a megvalósítást.

Alapszintű naplózási módszer

Egy egyszerű naplóüzenethez adjon meg egy részleges metódust az eseményazonosítót, a naplószintet és az üzenetsablont meghatározó attribútummal:

public static partial class Log
{
    [LoggerMessage(
        EventId = 13,
        Level = LogLevel.Critical,
        Message = "Epic failure processing item!")]
    public static partial void FailedToProcessWorkItem(
        ILogger logger, Exception ex);
}

Az üzenetsablon metódusparaméterekkel kitöltött helyőrzőket használ. A helyőrző neveknek leíróknak és konzisztensnek kell lenniük a sablonok között. Tulajdonságnévként szolgálnak a strukturált naplóadatokban. A helykitöltők neveihez a PascalCase használatát javasoljuk. Például: {Item}. {DateTime}

Hívja meg a naplózási módszert a kódból. Ha például kivétel történik a munkaelem feldolgozása során:

try
{
    // Process work item.
}
catch (Exception ex)
{
    Log.FailedToProcessWorkItem(logger, ex);
}

Ez a kód a következőhöz hasonló konzolkimenetet hoz létre:

crit: WorkerServiceOptions.Example.Worker[13]
      Epic failure processing item!
      System.Exception: Failed to verify communications.

Naplózás paraméterekkel

Ha paramétereket szeretne átadni egy naplóüzenetnek, adja hozzá őket metódusparaméterekként. A paraméternevek megegyeznek az üzenetsablon helyőrzőivel:

public static partial class Log
{
    [LoggerMessage(
        EventId = 1,
        Level = LogLevel.Information,
        Message = "Processing priority item: {Item}")]
    public static partial void PriorityItemProcessed(
        ILogger logger, WorkItem item);
}

Hívja meg a metódust a naplózó és a paraméterértékek megadásával:

var workItem = queue.Dequeue();
Log.PriorityItemProcessed(logger, workItem);

Ez a kód a következőhöz hasonló konzolkimenetet hoz létre:

info: WorkerServiceOptions.Example.Worker[1]
      Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'

A strukturált naplózási tárolók akkor használhatják az eseménynevet, ha az eseményazonosítóval együtt adják meg a naplózás bővítéséhez. A Serilog például az esemény nevét használja.

A naplózó üzenet hatókörének definiálása a forrásgenerálással

Naplóhatóköröket definiálhat a naplóüzenetek sorozatának további környezettel való körbefuttatásához. A forrás által generált naplózással kombinálhatja a LoggerMessageAttribute metódusokat a standard ILogger.BeginScope metódussal.

Engedélyezés IncludeScopes a appsettings.jsonkonzolnaplózó szakaszában:

{
    "Logging": {
        "Console": {
            "IncludeScopes": true
        },
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    }
}

Hozzon létre forrás által létrehozott naplózási módszereket, és csomagolja őket egy hatókörbe a következővel BeginScope:

public static partial class Log
{
    [LoggerMessage(
        EventId = 1,
        Level = LogLevel.Information,
        Message = "Processing priority item: {Item}")]
    public static partial void PriorityItemProcessed(
        ILogger logger, WorkItem item);
}

Használja a naplózási módszert egy hatókörben az alkalmazás kódjában:

using (_logger.BeginScope("Processing scope, started at: {DateTime}", DateTime.Now))
{
    Log.PriorityItemProcessed(_logger, workItem);
}

Vizsgálja meg a naplóüzeneteket az alkalmazás konzoljának kimenetében. Az alábbi eredmény a naplóüzenetek prioritási sorrendbe helyezését mutatja be a napló hatókörére vonatkozó üzenettel együtt:

info: WorkerServiceOptions.Example.Worker[1]
      => Processing scope, started at: 04/11/2024 11:27:52
      Processing priority item: Priority-Extreme (7d153ef9-8894-4282-836a-8e5e38319fb3): 'Verify communications'

Örökölt megközelítés: LoggerMessage.Define (.NET-keretrendszerhez és .NET Core 3.1-hez)

Mielőtt a .NET 6-ban bevezették a forrás által generált naplózást, az ajánlott nagy teljesítményű naplózási módszer a LoggerMessage.Define gyorsítótárazható delegáltak létrehozása volt. Bár ez a megközelítés továbbra is támogatott a visszamenőleges kompatibilitás érdekében, az új kódnak a forrás által létrehozott naplózást kell használnia LoggerMessageAttribute helyette.

Az LoggerMessage osztály olyan funkciókat tesz elérhetővé, amelyek gyorsítótárazható delegáltakat hoznak létre, amelyek kevesebb objektumfoglalást igényelnek, és kevesebb számítási többletterhelést igényelnek a naplózó bővítménymetódusaihoz képest, például LogInformation és LogDebug. LoggerMessage a következő teljesítménybeli előnyöket nyújtja a naplózóbővítmények metódusaihoz képest:

  • A naplózási bővítmény metódusaihoz "boxing" (konvertálás) típusú értéktípusokra van szükség, például int: object. A LoggerMessage minta elkerüli a ökölvívást azáltal, hogy statikus Action mezőket és bővítménymetszeteket használ erősen gépelt paraméterekkel.
  • A naplózó bővítmény metódusainak minden alkalommal elemezniük kell az üzenetsablont (elnevezett formátumsztringet), amikor naplóüzenetet írnak. LoggerMessage csak egyszer kell elemezni egy sablont az üzenet definiálásakor.

Megjegyzés:

Ha olyan kódot tart fenn, amely használja LoggerMessage.Define, fontolja meg a forrás által létrehozott naplózásra való migrálást. .NET-keretrendszer vagy .NET Core 3.1-alkalmazások esetén folytassa a használatát LoggerMessage.Define.

Naplózó üzenetének definiálása

A Define (LogLevel, EventId, String) használatával hozzon létre egy Action delegáltat az üzenetek naplózásához. Define a túlterhelések legfeljebb hat típusparamétert adhatnak át egy nevesített formátumsztringnek (sablonnak).

A metódushoz Define megadott sztring egy sablon, és nem interpolált sztring. A helykitöltőket a típusok megadásának sorrendjében töltik ki. A sablon helyőrzőinek leírónak és konzisztensnek kell lenniük a sablonok között. Tulajdonságnévként szolgálnak a strukturált naplóadatokban. A helyőrzők neveihez a Pascal-burkolatot javasoljuk. Például: {Item}. {DateTime}

Minden naplóüzenet egy Action statikus mezőben található, amelyet a LoggerMessage.Define hozott létre. A mintaalkalmazás például létrehoz egy mezőt, amely leírja a munkaelemek feldolgozására vonatkozó naplóüzenetet:

private static readonly Action<ILogger, Exception> s_failedToProcessWorkItem;

Adja meg a Action következőt:

  • A naplózási szint.
  • Egyedi eseményazonosító (EventId) a statikus bővítménymetódus nevével.
  • Az üzenetsablon (névvel ellátott formátumkarakterlánc).

A munkaelemek feldolgozásra való lekérdezése során a feldolgozói szolgáltatásalkalmazás a következőt állítja be:

  • Naplószint – LogLevel.Critical.
  • Az 13 eseményazonosító a FailedToProcessWorkItem metódus nevével.
  • Egy üzenetsablon (elnevezett formázási karakterlánc) konvertálása egy karakterláncra.
s_failedToProcessWorkItem = LoggerMessage.Define(
    LogLevel.Critical,
    new EventId(13, nameof(FailedToProcessWorkItem)),
    "Epic failure processing item!");

A LoggerMessage.Define metódus egy naplóüzenetet képviselő Action delegáltat konfigurál és definiál.

A strukturált naplózási tárolók akkor használhatják az eseménynevet, ha az eseményazonosítóval együtt adják meg a naplózás bővítéséhez. A Serilog például az esemény nevét használja.

A Action meghívás egy erősen beírt bővítménymetóduson keresztül történik. A PriorityItemProcessed metódus minden munkaelem feldolgozásakor naplózza az üzenetet. FailedToProcessWorkItem a rendszer meghívja, ha és amikor kivétel történik:

protected override async Task ExecuteAsync(
    CancellationToken stoppingToken)
{
    using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();

                if (nextItem is not null)
                {
                    logger.PriorityItemProcessed(nextItem);
                }
            }
            catch (Exception ex)
            {
                logger.FailedToProcessWorkItem(ex);
            }

            await Task.Delay(1_000, stoppingToken);
        }
    }
}

Vizsgálja meg az alkalmazás konzolkimenetét:

crit: WorkerServiceOptions.Example.Worker[13]
      Epic failure processing item!
      System.Exception: Failed to verify communications.
         at WorkerServiceOptions.Example.Worker.ExecuteAsync(CancellationToken stoppingToken) in
         ..\Worker.cs:line 27

Ha paramétereket szeretne átadni egy naplóüzenetnek, legfeljebb hat típust definiálhat a statikus mező létrehozásakor. A mintaalkalmazás naplózza a munkaelem részleteit az elemek feldolgozásakor a mező típusának WorkItemAction meghatározásával:

private static readonly Action<ILogger, WorkItem, Exception> s_processingPriorityItem;

A meghatalmazott naplóüzenetsablonja a megadott típusoktól kapja meg a helyőrző értékeit. A mintaalkalmazás egy delegáltat határoz meg azon munkaelem hozzáadására, ahol az elem paraméter a WorkItem.

s_processingPriorityItem = LoggerMessage.Define<WorkItem>(
    LogLevel.Information,
    new EventId(1, nameof(PriorityItemProcessed)),
    "Processing priority item: {Item}");

A munkaelem feldolgozásának naplózására szolgáló statikus bővítménymetódus megkapja a munkaelem argumentumértékét, PriorityItemProcessedés átadja a Action meghatalmazottnak:

public static void PriorityItemProcessed(
    this ILogger logger, WorkItem workItem) =>
    s_processingPriorityItem(logger, workItem, default!);

A munkavégző szolgáltatás metódusában ExecuteAsyncPriorityItemProcessed az üzenet naplózására van meghívva:

protected override async Task ExecuteAsync(
    CancellationToken stoppingToken)
{
    using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();

                if (nextItem is not null)
                {
                    logger.PriorityItemProcessed(nextItem);
                }
            }
            catch (Exception ex)
            {
                logger.FailedToProcessWorkItem(ex);
            }

            await Task.Delay(1_000, stoppingToken);
        }
    }
}

Vizsgálja meg az alkalmazás konzolkimenetét:

info: WorkerServiceOptions.Example.Worker[1]
      Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'

Naplószintű védett optimalizálás

A teljesítmény optimalizálása érdekében ellenőrizheti a LogLevel a ILogger.IsEnabled(LogLevel) segítségével, mielőtt meghívná a megfelelő Log* metódust. Ha a naplózás nincs konfigurálva az adotthoz LogLevel, ILogger.Log a rendszer nem hívja meg. Emellett elkerülhető az érték típusú dobozolás és a object[] (paramétereknek megfelelő) kiosztás.

További információkért lásd:

Lásd még