Naplópufferelés a .NET-ben

A .NET naplópufferezési képességeket biztosít, amelyek lehetővé teszik a naplók kibocsátásának késleltetését bizonyos feltételek teljesítéséig. A naplópufferelés olyan helyzetekben hasznos, ahol a következőt szeretné használni:

  • Gyűjtse össze az összes naplót egy adott műveletből, mielőtt eldöntené, hogy kibocsátja-e őket.
  • A naplók ne legyenek kibocsátva normál működés során, viszont hiba esetén kibocsátásra kerüljenek.
  • A tárolóba írt naplók számának csökkentésével optimalizálhatja a teljesítményt.

A pufferelt naplók ideiglenes körkörös pufferekben vannak tárolva a folyamat memóriájában, és a következő feltételek érvényesek:

  • Ha a puffer megtelt, a rendszer elveti a legrégebbi naplókat, és azokat soha nem továbbítja.
  • Ha ki szeretné küldeni a pufferelt naplókat, hívja meg a Flush() vagy GlobalLogBuffer osztályt a PerRequestLogBuffer segítségével.
  • Ha soha nem üríti ki a puffereket, a pufferelt naplók végül el lesznek dobva az alkalmazás futtatásakor, így az ténylegesen úgy viselkedik, mintha ezek a naplók le lettek tiltva.

Két pufferelési stratégia érhető el:

  • Globális pufferelés: A pufferek a teljes alkalmazás naplóit naplózják.
  • Kérésenkénti pufferelés: Az egyes HTTP-kérések naplóit puffereli, ha elérhető; ellenkező esetben a globális pufferbe való pufferelés.

Megjegyzés:

A naplópufferelés a .NET 9-ben és újabb verzióiban érhető el.

A naplópufferelés minden naplózási szolgáltatóval működik. Ha egy használt naplózási szolgáltató nem valósítja meg a IBufferedLogger interfészt, a naplópufferelés a puffer kiürítésekor közvetlenül az egyes pufferelt naplórekordokon hívja meg a naplómódszereket.

A naplópufferelés kibővíti a szűrési képességeket , lehetővé téve a naplók ideiglenes rögzítését és tárolását. Ahelyett, hogy azonnali kibocsátó vagy elvető döntést hoz, a pufferelés lehetővé teszi a naplók memóriában való tárolását, és később eldöntheti, hogy kibocsátja-e őket.

Első lépések

Első lépésként telepítse a 📦 Microsoft.Extensions.Telemetria NuGet-csomagot a globális puffereléshez. Vagy telepítse a 📦 Microsoft.AspNetCore.Diagnostics.MiddlewareNuGet-csomagot kérésenkénti puffereléshez.

dotnet add package Microsoft.Extensions.Telemetry
dotnet add package Microsoft.AspNetCore.Diagnostics.Middleware

További információ a csomagok hozzáadásáról: dotnet add package or Manage package dependencies in .NET applications.

Globális pufferelés

A globális pufferelés lehetővé teszi a naplók pufferelését a teljes alkalmazásban. A szűrőszabályok használatával konfigurálhatja, hogy mely naplókat pufferelje, majd szükség szerint ürítse ki a puffert a naplók kibocsátásához.

Egyszerű konfiguráció

Ha engedélyezni szeretné a globális pufferelést egy adott naplószinten vagy az alatt, adja meg a következő szintet:

// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(LogLevel.Information);

Az előző konfiguráció lehetővé teszi a LogLevel.Information szintű és annál alacsonyabb naplók pufferelését.

Fájlalapú konfiguráció

Hozzon létre egy konfigurációs szakaszt a(z) appsettings.json-ban, például:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    },

    "GlobalLogBuffering": {
      "MaxBufferSizeInBytes": 104857600,
      "MaxLogRecordSizeInBytes": 51200,
      "AutoFlushDuration": "00:00:30",
      "Rules": [
        {
          "CategoryName": "BufferingDemo",
          "LogLevel": "Information"
        },
        {
          "EventId": 1001
        }
      ]
    }
  }
}

Az előző konfiguráció:

  • Puffertárolja a naplókat az olyan kategóriákból, amelyek `BufferingDemo`-vel kezdődnek, `LogLevel.Information` szint és az alattiak esetében.
  • Puffereli az összes naplót az 1001-s eseményazonosítóval.
  • A puffer maximális méretét körülbelül 100 MB-ra állítja.
  • A naplórekord maximális méretét 50 KB-ra állítja.
  • A manuális kiürítés után 30 másodperces automatikus kiürítési időtartamot állít be.

A naplópufferelés konfigurációval való regisztrálásához vegye figyelembe a következő kódot:

// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(hostBuilder.Configuration.GetSection("Logging"));

Vonalon belüli kódkonfiguráció

// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(options =>
{
    options.MaxBufferSizeInBytes = 104857600; // 100 MB
    options.MaxLogRecordSizeInBytes = 51200; // 50 KB
    options.AutoFlushDuration = TimeSpan.FromSeconds(30);
    options.Rules.Add(new LogBufferingFilterRule(
        categoryName: "BufferingDemo",
        logLevel: LogLevel.Information));
    options.Rules.Add(new LogBufferingFilterRule(eventId: 1001));
});

Az előző konfiguráció:

  • Puffertárolja a naplókat az olyan kategóriákból, amelyek `BufferingDemo`-vel kezdődnek, `LogLevel.Information` szint és az alattiak esetében.
  • Puffereli az összes naplót az 1001-s eseményazonosítóval.
  • A puffer maximális méretét körülbelül 100 MB-ra állítja.
  • A naplórekord maximális méretét 50 KB-ra állítja.
  • A manuális kiürítés után 30 másodperces automatikus kiürítési időtartamot állít be.

A puffer kiürítése

A pufferelt naplók kiürítéséhez injektálja az GlobalLogBuffer absztrakt osztályt, és hívja meg a metódust Flush() :

public class MyService
{
    private readonly GlobalLogBuffer _buffer;

    public MyService(GlobalLogBuffer buffer)
    {
        _buffer = buffer;
    }

    public void HandleException(Exception ex)
    {
        _buffer.Flush();

        // After flushing, log buffering will be temporarily suspended (= all logs will be emitted immediately)
        // for the duration specified by AutoFlushDuration.
    }
}

Kérésenkénti pufferelés

A kérésenkénti pufferelés ASP.NET Core-alkalmazásokra vonatkozik, és lehetővé teszi a naplók egymástól függetlenül történő pufferelését minden HTTP-kéréshez. Az egyes kérések puffere akkor jön létre, amikor a kérés elindul, és a kérés befejeződésekor törlődik, így ha nem üríti ki a puffert, a naplók elvesznek a kérés befejeződésekor. Így hasznos lehet csak akkor kiüríteni a puffereket, amikor valóban szükség van rá, például ha hiba történik.

A kérelemenkénti pufferelés szorosan kapcsolódik a globális puffereléshez. Ha egy naplóbejegyzést kérésenkénti pufferbe kell pufferelni, de a pufferelési kísérlet pillanatában nincs aktív HTTP-környezet, a rendszer ehelyett a globális pufferbe puffereli. Ha a puffer kiürítése aktiválódik, a rendszer először a kérelemenkénti puffert üríti ki, majd a globális puffert.

Egyszerű konfiguráció

Csak egy adott naplószinten vagy alatt lévő naplók pufferelése:

builder.Logging.AddPerIncomingRequestBuffer(LogLevel.Information);

Fájlalapú konfiguráció

Hozzon létre egy konfigurációs részt a appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.*": "None"
    },

    "PerIncomingRequestLogBuffering": {
      "AutoFlushDuration": "00:00:05",
      "Rules": [
        {
          "CategoryName": "PerRequestLogBufferingFileBased.*",
          "LogLevel": "Information"
        }
      ]
    }
  }
}

Az előző konfiguráció:

  • Puffertárolja a naplókat az olyan kategóriákból, amelyek `PerRequestLogBufferingFileBased.`-vel kezdődnek, `LogLevel.Information` szint és az alattiak esetében.
  • A manuális kiürítés után 5 másodperces automatikus kiürítési időtartamot állít be.

A naplópufferelés konfigurációval való regisztrálásához vegye figyelembe a következő kódot:

builder.Logging.AddPerIncomingRequestBuffer(builder.Configuration.GetSection("Logging"));

Vonalon belüli kódkonfiguráció

builder.Logging.AddPerIncomingRequestBuffer(options =>
{
    options.AutoFlushDuration = TimeSpan.FromSeconds(5);
    options.Rules.Add(new Microsoft.Extensions.Diagnostics.Buffering.LogBufferingFilterRule("PerRequestLogBufferingCodeBased.*", LogLevel.Information));
});

Az előző konfiguráció:

  • Puffertárolja a naplókat az olyan kategóriákból, amelyek `PerRequestLogBufferingFileBased.`-vel kezdődnek, `LogLevel.Information` szint és az alattiak esetében.
  • A manuális kiürítés után 5 másodperces automatikus kiürítési időtartamot állít be.

A kérelemenkénti puffer kiürítése

Az aktuális kérés pufferelt naplóinak kiürítéséhez fecskendezze be az PerRequestLogBuffer absztrakt osztályt, és hívja meg a metódust Flush() :

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly ILogger<HomeController> _logger;
    private readonly PerRequestLogBuffer _buffer;

    public HomeController(ILogger<HomeController> logger, PerRequestLogBuffer buffer)
    {
        _logger = logger;
        _buffer = buffer;
    }

    [HttpGet("index/{id}")]
    public IActionResult Index(int id)
    {
        try
        {
            _logger.RequestStarted(id);

            // Simulate exception every 10th request
            if (id % 10 == 0)
            {
                throw new Exception("Simulated exception in controller");
            }

            _logger.RequestEnded(id);

            return Ok();
        }
        catch
        {
            _logger.ErrorMessage(id);
            _buffer.Flush();

            _logger.ExceptionHandlingFinished(id);

            return StatusCode(500, "An error occurred.");
        }
    }
}

Megjegyzés:

A kérelemenkénti puffer kiürítése a globális puffert is kiüríti.

A pufferelési szabályok alkalmazása

A naplópufferelési szabályok kiértékelése minden naplórekordon történik. Minden naplórekordhoz a következő algoritmust használja a rendszer:

  1. Ha egy naplóbejegyzés megfelel egy szabálynak, a rendszer puffereli ahelyett, hogy azonnal kibocsátanák.
  2. Ha egy naplóbejegyzés nem egyezik meg egy szabálysal, a rendszer a szokásos módon bocsátja ki.
  3. Ha eléri a pufferméretkorlátot, a rendszer elveti a legrégebbi pufferelt naplóbejegyzéseket (nem bocsát ki!), hogy helyet biztosítsunk az újaknak.
  4. Ha egy naplóbejegyzés mérete nagyobb, mint a naplórekord maximális mérete, akkor a rendszer nem puffereli, és a rendszer normál módon bocsátja ki.

Minden naplórekord esetében az algoritmus a következőt ellenőrzi:

  • Ha a naplószint megegyezik a szabály naplószintjével (vagyis egyenlő azzal vagy alacsonyabb annál).
  • Ha a kategória neve a szabály előtagjával CategoryName kezdődik.
  • Ha az eseményazonosító megegyezik a szabályéval EventId.
  • Ha az esemény neve megegyezik a szabály nevével EventName.
  • Ha bármely attribútum megegyezik a szabályéval Attributes.

Pufferszűrési szabályok módosítása futó alkalmazásokban

A globális pufferelés és a kérésenkénti pufferelés egyaránt támogatja a futtatókörnyezet konfigurációs frissítéseit a IOptionsMonitor<TOptions> felületen keresztül. Ha olyan konfigurációszolgáltatót használ, amely támogatja az újratöltéseket (például a fájlkonfigurációs szolgáltatót), futásidőben frissítheti a szűrési szabályokat az alkalmazás újraindítása nélkül.

Az alkalmazást például a következő appsettings.json indíthatja el, amely lehetővé teszi a naplók pufferelését azokhoz a naplókhoz, amelyek LogLevel.Information szinttel és PerRequestLogBufferingFileBased. kezdődő kategóriával rendelkeznek.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.*": "None"
    },

    "PerIncomingRequestLogBuffering": {
      "AutoFlushDuration": "00:00:05",
      "Rules": [
        {
          "CategoryName": "PerRequestLogBufferingFileBased.*",
          "LogLevel": "Information"
        }
      ]
    }
  }
}

Amíg az alkalmazás fut, a appsettings.json a következő konfigurációval frissítheti:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.*": "None"
    },

    "PerIncomingRequestLogBuffering": {
      "Rules": [
        {
          "LogLevel": "Information"
        }
      ]
    }
  }
}

A rendszer automatikusan alkalmazza az új szabályokat. Az előző konfiguráció esetében például a rendszer a szinttel rendelkező LogLevel.Information összes naplót puffereli.

Teljesítménnyel kapcsolatos szempontok

A naplópufferelés kompromisszumot kínál a memóriahasználat és a naplótárolás költségei között. A naplók memóriabeli pufferelése lehetővé teszi a következőt:

  1. A futásidejű feltételek alapján szelektíven bocsát ki naplókat.
  2. A felesleges naplók elvetése anélkül, hogy a tárolóba íratja őket.

Ügyeljen azonban a memóriahasználatra, különösen a nagy átviteli sebességű alkalmazásokban. Konfigurálja a megfelelő pufferméretkorlátokat a túlzott memóriahasználat elkerülése érdekében.

Ajánlott eljárások

  • Állítsa be a megfelelő pufferméretkorlátokat az alkalmazás memóriakorlátozásai alapján.
  • Használjon kérésenkénti pufferelést a webalkalmazásokhoz a naplók kérés szerinti elkülönítéséhez.
  • A memóriahasználat és a naplók rendelkezésre állásának egyensúlyba hozásához konfigurálja az automatikus kiürítési időtartamot.
  • Explicit kiürítési eseményindítók implementálása fontos eseményekhez (például hibákhoz és figyelmeztetésekhez).
  • Ellenőrizze a puffer memóriahasználatát az éles környezetben történő működés során, hogy az elfogadható korlátokon belül maradjon.

Korlátozások

  • A naplópufferelés nem támogatott a .NET 8-ban és a korábbi verziókban.
  • A naplók sorrendjének megőrzése nem garantált. Az eredeti időbélyegek azonban megmaradnak.
  • Az egyes naplózási szolgáltatók egyéni konfigurációja nem támogatott. Ugyanez a konfiguráció minden szolgáltatónál használható.
  • A naplózás hatókörei nem támogatottak. Ez azt jelenti, hogy a BeginScope metódus használata esetén a pufferelt naplórekordok nem lesznek társítva a hatókörrel.
  • Az eredeti naplórekord nem minden információja marad meg. A naplópufferelés belsőleg a(z) BufferedLogRecord osztályt használja az ürítés közben, és az alábbi tulajdonságai mindig üresek:

Lásd még