Delen via


Logboekbuffering in .NET

.NET biedt mogelijkheden voor logboekbuffering waarmee u de emissie van logboeken kunt vertragen tot aan bepaalde voorwaarden wordt voldaan. Logboekbuffering is handig in scenario's waarin u het volgende wilt doen:

  • Verzamel alle logboeken van een specifieke bewerking voordat u beslist of ze moeten worden verzonden.
  • Voorkom dat logboeken worden verzonden tijdens de normale werking, maar deze verzenden wanneer er fouten optreden.
  • Optimaliseer de prestaties door het aantal logboeken dat naar de opslag is geschreven te verminderen.

Gebufferde logboeken worden opgeslagen in tijdelijke cirkelbuffers in procesgeheugen en de volgende voorwaarden zijn van toepassing:

  • Als de buffer vol is, worden de oudste logboeken verwijderd en nooit verzonden.
  • Als u de gebufferde logboeken wilt verzenden, kunt u de Flush() of GlobalLogBuffer klasse aanroepenPerRequestLogBuffer.
  • Als u de buffers nooit leeggemaakt hebt, worden de gebufferde logboeken uiteindelijk verwijderd wanneer de toepassing wordt uitgevoerd, zodat deze zich effectief gedraagt als deze logboeken worden uitgeschakeld.

Er zijn twee bufferstrategieën beschikbaar:

  • Globale buffering: bufferslogboeken in de hele toepassing.
  • Buffering per aanvraag: buffert logboeken voor elke afzonderlijke HTTP-aanvraag, indien beschikbaar; anders buffert u naar de globale buffer.

Opmerking

Logboekbuffering is beschikbaar in .NET 9 en nieuwere versies.

Logboekbuffering werkt met alle logboekproviders. Als een logboekprovider die u gebruikt de IBufferedLogger interface niet implementeert, roept logboekbuffering logboekmethoden rechtstreeks aan op elke gebufferde logboekrecord bij het leegmaken van de buffer.

Logboekbuffering breidt de filtermogelijkheden uit door u in staat te stellen logboeken tijdelijk vast te leggen en op te slaan. In plaats van direct een beslissing te nemen over verzenden of verwijderen, kunt u met bufferen logboeken in het geheugen opslaan en later beslissen of u ze wilt verzenden.

Aan de slag

Om aan de slag te gaan, installeer het NuGet-pakket 📦 voor global buffering. Of installeer het 📦 NuGet-pakket Microsoft.AspNetCore.Diagnostics.Middleware voor buffering per aanvraag.

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

Zie dotnet-pakket toevoegen of pakketafhankelijkheden beheren in .NET-toepassingen voor meer informatie over het toevoegen van pakketten.

Wereldwijde buffering

Met globale buffering kunt u logboeken in de hele toepassing bufferen. U kunt configureren welke logboeken moeten worden gebufferd met behulp van filterregels en vervolgens de buffer zo nodig leegmaken om deze logboeken te verzenden.

Eenvoudige configuratie

Als u globale buffering wilt inschakelen op of onder een specifiek logboekniveau, geeft u dat niveau op:

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

Met de voorgaande configuratie kunt u logs bufferen met niveau LogLevel.Information en lager.

Bestandsgebaseerde configuratie

Maak een configuratiesectie in uw appsettings.json, bijvoorbeeld:

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

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

De voorgaande configuratie:

  • Buffert logboeken van categorieën die beginnen met BufferingDemo met niveau LogLevel.Information en lager.
  • Buffert alle logboeken met gebeurtenis-id 1001.
  • Hiermee stelt u de maximale buffergrootte in op ongeveer 100 MB.
  • Hiermee stelt u de maximale logboekrecordgrootte in op 50 kB.
  • Hiermee stelt u een duur van automatisch leegmaken van 30 seconden na handmatig leegmaken in.

Als u de logboekbuffering wilt registreren bij de configuratie, moet u rekening houden met de volgende code:

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

Configuratie van inlinecode

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

De voorgaande configuratie:

  • Buffert logboeken van categorieën die beginnen met BufferingDemo met niveau LogLevel.Information en lager.
  • Buffert alle logboeken met gebeurtenis-id 1001.
  • Hiermee stelt u de maximale buffergrootte in op ongeveer 100 MB.
  • Hiermee stelt u de maximale logboekrecordgrootte in op 50 kB.
  • Hiermee stelt u een duur van automatisch leegmaken van 30 seconden na handmatig leegmaken in.

De buffer leegmaken

Als u de gebufferde logboeken wilt leegmaken, injecteert u de GlobalLogBuffer abstracte klasse en roept u de methode aan 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.
    }
}

Buffering per aanvraag

Buffering per aanvraag is specifiek voor ASP.NET Core-toepassingen en stelt u in staat om logboeken afzonderlijk te bufferen voor elke HTTP-aanvraag. De buffer voor elke respectieve aanvraag wordt gemaakt wanneer de aanvraag wordt gestart en verwijderd wanneer de aanvraag eindigt, dus als u de buffer niet leegmaken, gaan de logboeken verloren wanneer de aanvraag eindigt. Op deze manier is het handig om alleen buffers leeg te maken wanneer dat echt nodig is, zoals wanneer er een fout optreedt.

Buffering per aanvraag is nauw gekoppeld aan wereldwijde buffering. Als een logboekvermelding moet worden gebufferd naar een buffer per aanvraag, maar er op het moment van bufferpoging geen actieve HTTP-context is, wordt deze gebufferd naar de globale buffer. Als buffers leegmaken wordt geactiveerd, wordt de buffer per aanvraag eerst leeggemaakt, gevolgd door de globale buffer.

Eenvoudige configuratie

Als u alleen logboeken wilt bufferen op of onder een specifiek logboekniveau:

builder.Logging.AddPerIncomingRequestBuffer(LogLevel.Information);

Bestandsgebaseerde configuratie

Maak een configuratiesectie in uw appsettings.json:

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

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

De voorgaande configuratie:

  • Buffert logboeken van categorieën die beginnen met PerRequestLogBufferingFileBased. met niveau LogLevel.Information en lager.
  • Hiermee stelt u een automatisch leegmaken duur van 5 seconden na handmatig leegmaken in.

Als u de logboekbuffering wilt registreren bij de configuratie, moet u rekening houden met de volgende code:

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

Configuratie van inlinecode

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

De voorgaande configuratie:

  • Buffert logboeken van categorieën die beginnen met PerRequestLogBufferingFileBased. met niveau LogLevel.Information en lager.
  • Hiermee stelt u een automatisch leegmaken duur van 5 seconden na handmatig leegmaken in.

De buffer per aanvraag leegmaken

Om de gebufferde logboeken voor de huidige aanvraag te leegmaken, injecteer de PerRequestLogBuffer abstracte klasse en roep de Flush() methode aan.

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

Opmerking

Het legen van de buffer per aanvraag leegt ook de globale buffer.

Hoe bufferregels worden toegepast

Evaluatie van regels voor logboekbuffering wordt uitgevoerd op elke logboekrecord. Het volgende algoritme wordt gebruikt voor elke logboekrecord:

  1. Als een logboekvermelding overeenkomt met een regel, wordt deze gebufferd in plaats van onmiddellijk te worden verzonden.
  2. Als een logboekvermelding niet overeenkomt met een regel, wordt deze normaal verzonden.
  3. Als de limiet voor de buffergrootte is bereikt, worden de oudste gebufferde logboekvermeldingen verwijderd (niet verzonden!) om ruimte te maken voor nieuwe items.
  4. Als een logboekvermelding groter is dan de maximale logboekrecordgrootte, wordt deze niet gebufferd en normaal verzonden.

Voor elke logboekrecord controleert het algoritme het volgende:

  • Als het logboekniveau overeenkomt (gelijk aan of lager is dan) het logboekniveau van de regel.
  • Als de naam van de categorie begint met het voorvoegsel van de regel CategoryName.
  • Als de gebeurtenis-id overeenkomt met de EventIdregel.
  • Als de gebeurtenisnaam overeenkomt met de EventName van de regel.
  • Als er eigenschappen overeenkomen met de regel Attributes.

Bufferfilterregels in een actieve app wijzigen

Zowel globale buffering als buffering per aanvraag bieden ondersteuning voor runtime-configuratie-updates via de IOptionsMonitor<TOptions> interface. Als u een configuratieprovider gebruikt die ondersteuning biedt voor opnieuw laden, zoals de bestandsconfiguratieprovider, kunt u filterregels tijdens runtime bijwerken zonder de toepassing opnieuw op te starten.

U kunt uw toepassing bijvoorbeeld starten met de volgende appsettings.json, die logboekbuffering inschakelt voor logboeken met zowel het niveau als de categorie die beginnen met LogLevel.Information:PerRequestLogBufferingFileBased.

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

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

Terwijl de app wordt uitgevoerd, kunt u de appsettings.json bijwerken met de volgende configuratie:

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

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

De nieuwe regels worden automatisch toegepast. Met de voorgaande configuratie worden bijvoorbeeld alle logboeken met het LogLevel.Information niveau gebufferd.

Prestatie-overwegingen

Logboekbuffering biedt een afweging tussen geheugengebruik en kosten voor logboekopslag. Door logboeken in het geheugen te bufferen, kunt u het volgende doen:

  1. Selectief logboeken verzenden op basis van runtimevoorwaarden.
  2. Verwijder onnodige logboeken zonder ze naar de opslag te schrijven.

Houd echter rekening met het geheugenverbruik, met name in toepassingen met hoge doorvoer. Configureer de juiste buffergroottelimieten om overmatig geheugengebruik te voorkomen.

Beste praktijken

  • Stel de juiste buffergroottelimieten in op basis van de geheugenbeperkingen van uw toepassing.
  • Gebruik buffering per aanvraag voor webtoepassingen om logboeken op aanvraag te isoleren.
  • Configureer de duur van automatisch leegmaken zorgvuldig om het geheugengebruik en de beschikbaarheid van logboeken te verdelen.
  • Implementeer expliciete flush-triggers voor belangrijke gebeurtenissen (zoals fouten en waarschuwingen).
  • Bewaak het geheugengebruik van buffers in productie om ervoor te zorgen dat deze binnen acceptabele limieten blijft.

Beperkingen

  • Logboekbuffering wordt niet ondersteund in .NET 8 en eerdere versies.
  • De volgorde van logboeken blijft niet gegarandeerd behouden. Oorspronkelijke tijdstempels blijven echter behouden.
  • Aangepaste configuratie voor elke logboekprovider wordt niet ondersteund. Dezelfde configuratie wordt gebruikt voor alle providers.
  • Logboekbereiken worden niet ondersteund. Dit betekent dat als u de BeginScope methode gebruikt, de gebufferde logboekrecords niet worden gekoppeld aan het bereik.
  • Niet alle gegevens van de oorspronkelijke logboekrecord blijven behouden. Logboekbuffering maakt intern gebruik van de BufferedLogRecord klasse bij het legen, en de volgende eigenschappen zijn altijd leeg:

Zie ook