Loggbuffertning i .NET

.NET tillhandahåller funktioner för loggbuffertning som gör att du kan fördröja utsläppen av loggar tills vissa villkor är uppfyllda. Loggbuffertning är användbart i scenarier där du vill:

  • Samla in alla loggar från en specifik åtgärd innan du bestämmer dig för om de ska skickas.
  • Förhindra att loggar genereras under normal drift, men generera dem när fel inträffar.
  • Optimera prestanda genom att minska antalet loggar som skrivs till lagring.

Buffrade loggar lagras i tillfälliga cirkulära buffertar i processminnet och följande villkor gäller:

  • Om bufferten är full tas de äldsta loggarna bort och genereras aldrig.
  • Om du vill generera de buffrade loggarna kan du anropa Flush()GlobalLogBuffer klassen eller PerRequestLogBuffer .
  • Om du aldrig tömer buffertarna kommer de buffrade loggarna så småningom att tas bort när programmet körs, så det fungerar effektivt som om loggarna är inaktiverade.

Det finns två tillgängliga buffringsstrategier:

  • Global buffring: Buffrar loggar i hela applikationen.
  • Buffring per begäran: Buffrar loggar för varje enskild HTTP-begäran om tillgänglig; annars buffrar till den globala bufferten.

Anmärkning

Loggbuffertning är tillgängligt i .NET 9 och senare versioner.

Loggbuffring fungerar med alla loggningsleverantörer. Om en loggningsprovider som du använder inte implementerar IBufferedLogger gränssnittet anropar buffring av loggar loggmetoder direkt på varje buffrad loggpost när bufferten töms.

Loggbuffertning utökar filtreringsfunktionerna genom att du tillfälligt kan samla in och lagra loggar. I stället för att fatta ett omedelbart beslut om att generera eller ignorera kan du med buffring lagra loggar i minnet och bestämma senare om du vill generera dem.

Get started

Kom igång genom att installera 📦 NuGet-paketet Microsoft.Extensions.Telemetry för global buffring. Du kan också installera 📦 NuGet-paketet Microsoft.AspNetCore.Diagnostics.Middleware för buffring per begäran.

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

Mer information om hur du lägger till paket finns i dotnet add package or Manage package dependencies in .NET applications (Lägga till paket eller Hantera paketberoenden i .NET-program).

Global buffring

Med global buffring kan du buffra loggar i hela din applikation. Du kan konfigurera vilka loggar som ska buffras med hjälp av filterregler och sedan rensa bufferten efter behov för att skicka ut loggarna.

Enkel konfiguration

Om du vill aktivera global buffring på eller under en specifik loggnivå anger du den nivån:

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

Den föregående konfigurationen aktiverar buffringsloggar med nivå LogLevel.Information och under.

Filbaserad konfiguration

Skapa ett konfigurationsavsnitt i din appsettings.json, till exempel:

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

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

Föregående konfiguration:

  • Buffrar loggar från kategorier vars namn börjar med BufferingDemo och har nivå LogLevel.Information eller lägre.
  • Buffrar alla loggar med händelse-ID 1001.
  • Anger den maximala buffertstorleken till cirka 100 MB.
  • Anger den maximala loggpoststorleken till 50 KB.
  • Anger en varaktighet för automatisk tömning på 30 sekunder efter manuell tömning.

Om du vill registrera loggbuffertningen med konfigurationen bör du överväga följande kod:

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

Konfiguration av infogad kod

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

Föregående konfiguration:

  • Buffrar loggar från kategorier vars namn börjar med BufferingDemo och har nivå LogLevel.Information eller lägre.
  • Buffrar alla loggar med händelse-ID 1001.
  • Anger den maximala buffertstorleken till cirka 100 MB.
  • Anger den maximala loggpoststorleken till 50 KB.
  • Anger en varaktighet för automatisk tömning på 30 sekunder efter manuell tömning.

Rensa bufferten

Om du vill rensa de buffrade loggarna matar du in den GlobalLogBuffer abstrakta klassen och anropar Flush() metoden:

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.
    }
}

Buffert vid förfrågan

Buffring per förfrågan är specifik för ASP.NET Core-program och gör att du kan buffra loggar separat för varje HTTP-förfrågan. Bufferten för varje respektive begäran skapas när begäran startas och tas bort när begäran avslutas, så om du inte tömer bufferten går loggarna förlorade när begäran avslutas. På så sätt är det användbart att bara rensa buffertar när du verkligen behöver, till exempel när ett fel inträffar.

Buffring per begäran är nära kopplad till global buffring. Om en loggpost ska buffras till en buffert för varje begäran, men det inte finns någon aktiv HTTP-kontext vid tidpunkten för buffringsförsöket, buffras den istället till global buffert. Om buffertspolning utlöses rensas bufferten per begäran först följt av den globala bufferten.

Enkel konfiguration

Så här buffrar du endast loggar på eller under en specifik loggnivå:

builder.Logging.AddPerIncomingRequestBuffer(LogLevel.Information);

Filbaserad konfiguration

Skapa ett konfigurationsavsnitt i din appsettings.json:

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

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

Föregående konfiguration:

  • Buffrar loggar från kategorier vars namn börjar med PerRequestLogBufferingFileBased. och har nivå LogLevel.Information eller lägre.
  • Anger en varaktighet för automatisk tömning på 5 sekunder efter manuell tömning.

Om du vill registrera loggbuffertningen med konfigurationen bör du överväga följande kod:

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

Konfiguration av infogad kod

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

Föregående konfiguration:

  • Buffrar loggar från kategorier vars namn börjar med PerRequestLogBufferingFileBased. och har nivå LogLevel.Information eller lägre.
  • Anger en varaktighet för automatisk tömning på 5 sekunder efter manuell tömning.

Rensa bufferten för varje förfrågan

Om du vill rensa de buffrade loggarna för den aktuella begäran matar du in den PerRequestLogBuffer abstrakta klassen och anropar dess Flush() metod:

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

Anmärkning

Genom att rensa bufferten per begäran töms även den globala bufferten.

Så här tillämpas buffringsregler

Utvärdering av loggbuffertningsregler utförs på varje loggpost. Följande algoritm används för varje loggpost:

  1. Om en loggpost matchar någon regel buffrades den i stället för att genereras omedelbart.
  2. Om en loggpost inte matchar någon regel genereras den normalt.
  3. Om buffertstorleksgränsen nås tas de äldsta buffrade loggposterna bort (inte genereras!) för att göra plats för nya.
  4. Om storleken på en loggpost är större än den maximala storleken för logginlägg, kommer den inte att buffras och emitteras normalt.

För varje loggpost kontrollerar algoritmen:

  • Om loggnivån matchar (är lika med eller lägre än) regelns loggnivå.
  • Om kategorinamnet börjar med regelns CategoryName prefix.
  • Om händelse-ID:t matchar regelns EventId.
  • Om händelsenamnet matchar regelns EventName.
  • Om några attribut matchar regelns Attributes.

Ändra regler för buffertfiltrering i en app som körs

Både global buffring och buffring per begäran stöder körningskonfigurationsuppdateringar via IOptionsMonitor<TOptions> gränssnittet. Om du använder en konfigurationsprovider som stöder omladdningar, till exempel filkonfigurationsprovidern, kan du uppdatera filtreringsreglerna vid körning utan att starta om programmet.

Du kan till exempel starta ditt program med följande appsettings.json, som möjliggör loggbuffertning för loggar med nivån LogLevel.Information och kategorin som börjar med PerRequestLogBufferingFileBased.:

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

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

När appen körs kan du uppdatera appsettings.json med följande konfiguration:

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

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

De nya reglerna tillämpas automatiskt. Med den föregående konfigurationen buffrades till exempel alla loggar med LogLevel.Information nivån.

Prestandaöverväganden

Loggbuffertning erbjuder en kompromiss mellan minnesanvändning och logglagringskostnader. Om du buffrar loggar i minnet kan du:

  1. Generera loggar selektivt baserat på körningsvillkor.
  2. Släpp onödiga loggar utan att skriva dem till lagringsutrymme.

Tänk dock på minnesförbrukningen, särskilt i program med högt dataflöde. Konfigurera lämpliga buffertstorleksgränser för att förhindra överdriven minnesanvändning.

Metodtips

  • Ange lämpliga buffertstorleksgränser baserat på programmets minnesbegränsningar.
  • Använd buffring per begäran för webbprogram för att isolera loggar efter begäran.
  • Konfigurera varaktigheten för automatisk tömning noggrant för att balansera minnesanvändning och loggtillgänglighet.
  • Implementera explicita tömningsutlösare för viktiga händelser (till exempel fel och varningar).
  • Övervaka buffertminnesanvändningen i produktion för att säkerställa att den förblir inom godkända gränser.

Begränsningar

  • Loggbuffertning stöds inte i .NET 8 och tidigare versioner.
  • Det garanteras inte att loggordningen bevaras. Ursprungliga tidsstämplar bevaras dock.
  • Anpassad konfiguration per varje loggningsprovider stöds inte. Samma konfiguration används för alla leverantörer.
  • Loggomfattningar stöds inte. Det innebär att om du använder BeginScope metoden associeras inte de buffrade loggposterna med omfånget.
  • All information om den ursprungliga loggposten bevaras inte. Loggbuffring använder klassen BufferedLogRecord internt vid tömning, och följande egenskaper är alltid tomma:

Se även