Loggning i C# och .NET

.NET stöder hög prestanda, strukturerad loggning via API:et ILogger för att övervaka programmets beteende och diagnostisera problem. Loggar kan skrivas till olika mål genom att konfigurera olika loggningsproviders. Grundläggande loggningsproviders är inbyggda och det finns även många tredjepartsleverantörer tillgängliga.

Kom igång

Det här första exemplet visar grunderna, men det är bara lämpligt för en trivial konsolapp. Den här exempelkonsolappen förlitar sig på följande NuGet-paket:

I nästa avsnitt ser du hur du kan förbättra koden med tanke på skalning, prestanda, konfiguration och typiska programmeringsmönster.

using Microsoft.Extensions.Logging;

using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Föregående exempel:

  • Skapar en ILoggerFactory. Lagrar ILoggerFactory all konfiguration som avgör var loggmeddelanden skickas. I det här fallet konfigurerar du konsolens loggningsprovider så att loggmeddelanden skrivs till konsolen.
  • Skapar en ILogger med en kategori med namnet "Program". Kategorin är en string som är associerad med varje meddelande som loggas av ILogger objektet. Den används för att gruppera loggmeddelanden från samma klass (eller kategori) tillsammans vid sökning eller filtrering av loggar.
  • Anropar LogInformation för att logga ett meddelande på Information nivån. Loggnivån anger allvarlighetsgraden för den loggade händelsen och används för att filtrera bort mindre viktiga loggmeddelanden. Loggposten innehåller även en meddelandemall "Hello World! Logging is {Description}." och ett nyckel/värde-par Description = fun. Nyckelnamnet (eller platshållaren) kommer från ordet inuti klammerparenteserna i mallen och värdet kommer från det återstående metodargumentet.

Den här projektfilen för det här exemplet innehåller två NuGet-paket:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
  </ItemGroup>

</Project>

Dricks

All källkod för loggningsexemplet är tillgänglig i Exempelwebbläsaren för nedladdning. Mer information finns i Bläddra bland kodexempel: Loggning i .NET.

Logga in en icke-trivial app

Det finns flera ändringar som du bör överväga att göra i föregående exempel när du loggar in i ett mindre trivialt scenario:

  • Om ditt program använder beroendeinmatning (DI) eller en värd, till exempel ASP. NET:s webapplication eller generiska värd bör du använda ILoggerFactory och ILogger objekt från deras respektive DI-containrar i stället för att skapa dem direkt. Mer information finns i Integrering med DI och Värdar.

  • Loggning av kompileringstidskälla är vanligtvis ett bättre alternativ till ILogger tilläggsmetoder som LogInformation. Loggningskällans generering ger bättre prestanda, starkare typning och undviker att sprida string konstanter i dina metoder. Kompromissen är att användning av den här tekniken kräver lite mer kod.

using Microsoft.Extensions.Logging;

internal partial class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger("Program");
        LogStartupMessage(logger, "fun");
    }

    [LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
    static partial void LogStartupMessage(ILogger logger, string description);
}
  • Den rekommenderade metoden för loggkategorinamn är att använda det fullständigt kvalificerade namnet på klassen som skapar loggmeddelandet. Detta hjälper till att relatera loggmeddelanden tillbaka till koden som skapade dem och ger en bra kontrollnivå vid filtrering av loggar. CreateLogger accepterar en Type för att göra den här namngivningen enkel att göra.
using Microsoft.Extensions.Logging;

internal class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger<Program>();
        logger.LogInformation("Hello World! Logging is {Description}.", "fun");
    }
}
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;

using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
    builder.AddOpenTelemetry(logging =>
    {
        logging.AddOtlpExporter();
    });
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Integrering med värdar och beroendeinmatning

Om ditt program använder beroendeinmatning (DI) eller en värd, till exempel ASP. NET:s webapplication eller generiska värd bör du använda ILoggerFactory och ILogger objekt från DI-containern i stället för att skapa dem direkt.

Hämta en ILogger från DI

Det här exemplet hämtar ett ILogger-objekt i en värdbaserad app med ASP.NET minimala API:er:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<ExampleHandler>();

var app = builder.Build();

var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);

app.Run();

partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
    public string HandleRequest()
    {
        LogHandleRequest(logger);
        return "Hello World";
    }

    [LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
    public static partial void LogHandleRequest(ILogger logger);
}

Föregående exempel:

  • Skapade en singleton-tjänst med namnet ExampleHandler och mappade inkommande webbbegäranden för att köra ExampleHandler.HandleRequest funktionen.
  • Rad 8 definierar en primär konstruktor för ExampleHandler, en funktion som läggs till i C# 12. Att använda den äldre C#-konstruktorn skulle fungera lika bra men är lite mer utförligt.
  • Konstruktorn definierar en parameter av typen ILogger<ExampleHandler>. ILogger<TCategoryName> härleds från ILogger och anger vilken kategori objektet ILogger har. DI-containern letar upp en ILogger med rätt kategori och tillhandahåller den som konstruktorargument. Om det inte finns någon ILogger med den kategorin ännu skapar DI-containern den automatiskt från ILoggerFactory i tjänstleverantören.
  • Parametern logger som togs emot i konstruktorn användes för loggning i HandleRequest funktionen.

Värdbaserad ILoggerFactory

Värdskapare initierar standardkonfigurationen och lägger sedan till ett konfigurerat ILoggerFactory objekt i värdens DI-container när värden skapas. Innan värden skapas kan du justera loggningskonfigurationen via HostApplicationBuilder.Logging, WebApplicationBuilder.Loggingeller liknande API:er på andra värdar. Värdar tillämpar även loggningskonfiguration från standardkonfigurationskällor som appsettings.json och miljövariabler. Mer information finns i Konfiguration i .NET.

Det här exemplet expanderar på föregående för att anpassa den ILoggerFactory som tillhandahålls av WebApplicationBuilder. Den lägger till OpenTelemetry som en loggningsprovider som skickar loggarna via OTLP (OpenTelemetry protocol):

var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();

Skapa en ILoggerFactory med DI

Om du använder en DI-container utan värd använder du AddLogging för att konfigurera och lägga till ILoggerFactory i containern.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();

// Do some pretend work
service.DoSomeWork(10, 20);

class ExampleService(ILogger<ExampleService> logger)
{
    public void DoSomeWork(int x, int y)
    {
        logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
    }
}

Föregående exempel:

  • Skapade en DI-tjänstcontainer som innehåller en ILoggerFactory konfigurerad för att skriva till konsolen
  • En singleton ExampleService har lagts till i containern
  • Skapade en instans av ExampleService från DI-containern som också automatiskt skapade en ILogger<ExampleService> som ska användas som konstruktorargument.
  • Anropad ExampleService.DoSomeWork som använde ILogger<ExampleService> för att logga ett meddelande till konsolen.

Konfigurera loggning

Loggningskonfigurationen anges i kod eller via externa källor, till exempel konfigurationsfiler och miljövariabler. Det är fördelaktigt att använda extern konfiguration när det är möjligt eftersom det kan ändras utan att programmet återskapas. Vissa uppgifter, till exempel att ange loggningsproviders, kan dock bara konfigureras från kod.

Konfigurera loggning utan kod

För appar som använder en värd tillhandahålls loggningskonfiguration ofta i "Logging" avsnittet apparinställningar.{Environment}.json filer. För appar som inte använder en värd konfigureras externa konfigurationskällor explicit eller konfigureras i kod i stället.

Följande apparinställningar. Development.json filen genereras av .NET Worker-tjänstmallarna:

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

I JSON-koden ovan:

  • Kategorierna "Default", "Microsoft"och "Microsoft.Hosting.Lifetime" loggnivå anges.
  • Värdet "Default" tillämpas på alla kategorier som inte anges på annat sätt, vilket i praktiken gör alla standardvärden för alla kategorier "Information". Du kan åsidosätta det här beteendet genom att ange ett värde för en kategori.
  • Kategorin "Microsoft" gäller för alla kategorier som börjar med "Microsoft".
  • Kategoriloggarna "Microsoft" på loggnivå Warning och högre.
  • Kategorin "Microsoft.Hosting.Lifetime" är mer specifik än "Microsoft" kategorin, så "Microsoft.Hosting.Lifetime" kategoriloggarna på loggnivå "Information" och högre.
  • En specifik loggprovider har inte angetts, så LogLevel den gäller för alla aktiverade loggningsproviders förutom Windows EventLog.

Egenskapen Logging kan ha LogLevel egenskaper för loggprovidern. LogLevel Anger den lägsta nivå som ska loggas för valda kategorier. I föregående JSON Information Warning och loggnivåer anges. LogLevel anger loggens allvarlighetsgrad och sträcker sig från 0 till 6:

Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5 och None = 6.

När en LogLevel har angetts aktiveras loggning för meddelanden på den angivna nivån och högre. I föregående JSON Default loggas kategorin för Information och högre. Till exempel Informationloggas , Warning, Erroroch Critical meddelanden. Om nej LogLevel har angetts är loggning standardvärdet för Information nivån. Mer information finns i Loggnivåer.

En provideregenskap kan ange en LogLevel egenskap. LogLevel under en provider anger nivåer för att logga för den providern och åsidosätter logginställningarna som inte är provider. Överväg följande appsettings.json fil:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.Hosting": "Trace"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    }
}

Inställningar i Logging.{ProviderName}.LogLevel åsidosättningsinställningar i Logging.LogLevel. I föregående JSON Debug är providerns standardloggnivå inställd Informationpå :

Logging:Debug:LogLevel:Default:Information

Föregående inställning anger Information loggnivån för varje Logging:Debug: kategori utom Microsoft.Hosting. När en specifik kategori visas åsidosätter den specifika kategorin standardkategorin. I föregående JSON åsidosätter Logging:Debug:LogLevel kategorierna "Microsoft.Hosting" och "Default" inställningarna i Logging:LogLevel

Minsta loggnivå kan anges för något av följande:

  • Specifika leverantörer: Till exempel Logging:EventSource:LogLevel:Default:Information
  • Specifika kategorier: Till exempel Logging:LogLevel:Microsoft:Warning
  • Alla leverantörer och alla kategorier: Logging:LogLevel:Default:Warning

Loggar under miniminivån är inte:

  • Skickas till providern.
  • Loggas eller visas.

Om du vill ignorera alla loggar anger du LogLevel.None. LogLevel.None har värdet 6, vilket är högre än LogLevel.Critical (5).

Om en provider stöder loggomfattningar anger IncludeScopes du om de är aktiverade. Mer information finns i loggomfattningar

Följande appsettings.json fil innehåller inställningar för alla inbyggda leverantörer:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft.Extensions.Hosting": "Warning",
                "Default": "Information"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "EventLog": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "AzureAppServicesFile": {
            "IncludeScopes": true,
            "LogLevel": {
                "Default": "Warning"
            }
        },
        "AzureAppServicesBlob": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "ApplicationInsights": {
            "LogLevel": {
                "Default": "Information"
            }
        }
    }
}

I föregående exempel:

  • Kategorierna och nivåerna är inte föreslagna värden. Exemplet tillhandahålls för att visa alla standardprovidrar.
  • Inställningar i Logging.{ProviderName}.LogLevel åsidosättningsinställningar i Logging.LogLevel. Nivån i Debug.LogLevel.Default åsidosätter till exempel nivån i LogLevel.Default.
  • Varje providers alias används. Varje provider definierar ett alias som kan användas i konfigurationen i stället för det fullständigt kvalificerade typnamnet. De inbyggda provideraliasen är:
    • Console
    • Debug
    • EventSource
    • EventLog
    • AzureAppServicesFile
    • AzureAppServicesBlob
    • ApplicationInsights

Ange loggnivå efter kommandorad, miljövariabler och annan konfiguration

Loggnivån kan anges av någon av konfigurationsprovidrar. Du kan till exempel skapa en bevarad miljövariabel med namnet Logging:LogLevel:Microsoft med värdet Information.

Skapa och tilldela en bevarad miljövariabel med tanke på loggnivåvärdet.

:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M

I en ny instans av kommandotolken läser du miljövariabeln.

:: Prints the env var value
echo %Logging__LogLevel__Microsoft%

Den föregående miljöinställningen sparas i miljön. Om du vill testa inställningarna när du använder en app som skapats med .NET Worker-tjänstmallarna använder du dotnet run kommandot i projektkatalogen när miljövariabeln har tilldelats.

dotnet run

Dricks

När du har angett en miljövariabel startar du om din integrerade utvecklingsmiljö (IDE) för att säkerställa att nyligen tillagda miljövariabler är tillgängliga.

I Azure App Service väljer du Ny programinställning på sidan Konfiguration av inställningar>. Azure App Service-programinställningar är:

  • Krypterad i vila och överförs via en krypterad kanal.
  • Exponeras som miljövariabler.

Mer information om hur du anger .NET-konfigurationsvärden med hjälp av miljövariabler finns i miljövariabler.

Konfigurera loggning med kod

Om du vill konfigurera loggning i kod använder du API:et ILoggingBuilder . Detta kan nås från olika platser:

Det här exemplet visar hur du ställer in konsolens loggningsprovider och flera filter.

using Microsoft.Extensions.Logging;

using var loggerFactory = LoggerFactory.Create(static builder =>
{
    builder
        .AddFilter("Microsoft", LogLevel.Warning)
        .AddFilter("System", LogLevel.Warning)
        .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
        .AddConsole();
});

ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");

I föregående exempel AddFilter används för att justera loggnivån som är aktiverad för olika kategorier. AddConsole används för att lägga till konsolens loggningsprovider. Som standard är loggar med Debug allvarlighetsgrad inte aktiverade, men eftersom konfigurationen justerade filtren visas felsökningsmeddelandet "Hello Everyone" i konsolen.

Så här tillämpas filtreringsregler

När ett ILogger<TCategoryName> objekt skapas ILoggerFactory väljer objektet en enda regel per provider som ska tillämpas på den loggaren. Alla meddelanden som skrivs av en ILogger instans filtreras baserat på de valda reglerna. Den mest specifika regeln för varje provider och kategoripar väljs från de tillgängliga reglerna.

Följande algoritm används för varje provider när en ILogger skapas för en viss kategori:

  • Välj alla regler som matchar providern eller dess alias. Om ingen matchning hittas väljer du alla regler med en tom provider.
  • Från resultatet av föregående steg väljer du regler med det längsta matchande kategoriprefixet. Om ingen matchning hittas väljer du alla regler som inte anger en kategori.
  • Om flera regler har valts tar du den sista .
  • Om inga regler har valts använder du LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) för att ange den minsta loggningsnivån.

Loggkategori

När ett ILogger objekt skapas anges en kategori . Den kategorin ingår i varje loggmeddelande som skapas av den instansen av ILogger. Kategoristrängen är godtycklig, men konventionen är att använda det fullständigt kvalificerade klassnamnet. I ett program med en tjänst som definierats som följande objekt kan kategorin till exempel vara "Example.DefaultService":

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger<DefaultService> _logger;

        public DefaultService(ILogger<DefaultService> logger) =>
            _logger = logger;

        // ...
    }
}

Om ytterligare kategorisering önskas är konventionen att använda ett hierarkiskt namn genom att lägga till en underkategori i det fullständigt kvalificerade klassnamnet och uttryckligen ange kategorin med hjälp av LoggerFactory.CreateLogger:

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger _logger;

        public DefaultService(ILoggerFactory loggerFactory) =>
            _logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");

        // ...
    }
}

Att anropa CreateLogger med ett fast namn kan vara användbart när det används i flera klasser/typer så att händelserna kan ordnas efter kategori.

ILogger<T> motsvarar anrop CreateLogger med det fullständigt kvalificerade typnamnet T.

Loggnivå

I följande tabell visas LogLevel värdena, metoden för bekvämlighetstillägg Log{LogLevel} och den föreslagna användningen:

Loggnivå Värde Metod beskrivning
Spårning 0 LogTrace Innehåller de mest detaljerade meddelandena. Dessa meddelanden kan innehålla känsliga appdata. Dessa meddelanden är inaktiverade som standard och bör inte aktiveras i produktion.
Debug 1 LogDebug För felsökning och utveckling. Använd med försiktighet i produktionen på grund av den höga volymen.
Information 2 LogInformation Spårar appens allmänna flöde. Kan ha ett långsiktigt värde.
Varning! 3 LogWarning För onormala eller oväntade händelser. Innehåller vanligtvis fel eller villkor som inte gör att appen misslyckas.
Fel 4 LogError För fel och undantag som inte kan hanteras. Dessa meddelanden anger ett fel i den aktuella åtgärden eller begäran, inte ett appomfattande fel.
Kritisk 5 LogCritical För fel som kräver omedelbar uppmärksamhet. Exempel: scenarier för dataförlust, slut på diskutrymme.
None 6 Anger att inga meddelanden ska skrivas.

I föregående tabell LogLevel visas från lägsta till högsta allvarlighetsgrad.

Log-metodens första parameter, LogLevel, anger loggens allvarlighetsgrad. I stället för att anropa Log(LogLevel, ...)anropa anropar de flesta utvecklare log{LogLevel} -tilläggsmetoderna. Tilläggsmetoderna Log{LogLevel} Log anropar metoden och anger LogLevel. Följande två loggningsanrop är till exempel funktionellt likvärdiga och skapar samma logg:

public void LogDetails()
{
    var logMessage = "Details for log.";

    _logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
    _logger.LogInformation(AppLogEvents.Details, logMessage);
}

AppLogEvents.Details är händelse-ID:t och representeras implicit av ett konstant Int32 värde. AppLogEvents är en klass som exponerar olika namngivna identifierarkonstanter och som visas i avsnittet Logghändelse-ID .

Följande kod skapar Information och Warning loggar:

public async Task<T> GetAsync<T>(string id)
{
    _logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);

    var result = await _repository.GetAsync(id);
    if (result is null)
    {
        _logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
    }

    return result;
}

I föregående kod är den första Log{LogLevel} parametern, AppLogEvents.Read, logghändelse-ID. Den andra parametern är en meddelandemall med platshållare för argumentvärden som tillhandahålls av de återstående metodparametrarna. Metodparametrarna beskrivs i avsnittet med meddelandemallar senare i den här artikeln.

Konfigurera lämplig loggnivå och anropa rätt Log{LogLevel} metoder för att styra hur mycket loggutdata som skrivs till ett visst lagringsmedium. Till exempel:

  • I produktion:
    • Loggning på Trace nivåerna eller Debug ger en stor mängd detaljerade loggmeddelanden. För att kontrollera kostnader och inte överskrida datalagringsgränser loggar Trace och Debug nivåmeddelanden till ett datalager med hög volym och låg kostnad. Överväg att Trace begränsa och Debug till specifika kategorier.
    • Loggning på Warning via Critical nivåer bör ge få loggmeddelanden.
      • Kostnader och lagringsgränser är vanligtvis inte ett problem.
      • Få loggar ger mer flexibilitet i val av datalager.
  • Under utveckling:
    • Ange till Warning.
    • Lägg till Trace eller Debug meddelanden vid felsökning. Om du vill begränsa utdata anger Trace du eller Debug endast för de kategorier som undersöks.

Följande JSON-uppsättningar Logging:Console:LogLevel:Microsoft:Information:

{
    "Logging": {
        "LogLevel": {
            "Microsoft": "Warning"
        },
        "Console": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        }
    }
}

Logghändelse-ID

Varje logg kan ange en händelseidentifierare, EventId är en struktur med en Id och valfri skrivskyddad Name egenskap. Exempelkällan använder AppLogEvents klassen för att definiera händelse-ID:t:

using Microsoft.Extensions.Logging;

internal static class AppLogEvents
{
    internal static EventId Create = new(1000, "Created");
    internal static EventId Read = new(1001, "Read");
    internal static EventId Update = new(1002, "Updated");
    internal static EventId Delete = new(1003, "Deleted");

    // These are also valid EventId instances, as there's
    // an implicit conversion from int to an EventId
    internal const int Details = 3000;
    internal const int Error = 3001;

    internal static EventId ReadNotFound = 4000;
    internal static EventId UpdateNotFound = 4001;

    // ...
}

Dricks

Mer information om hur du konverterar en int till en EventIdfinns i EventId.Implicit(Int32 till EventId)-operator.

Ett händelse-ID associerar en uppsättning händelser. Till exempel kan alla loggar som rör läsningsvärden från en lagringsplats vara 1001.

Loggningsprovidern kan logga händelse-ID:t i ett ID-fält, i loggningsmeddelandet eller inte alls. Felsökningsprovidern visar inte händelse-ID:t. Konsolprovidern visar händelse-ID:t inom hakparenteser efter kategorin:

info: Example.DefaultService.GetAsync[1001]
      Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
      GetAsync(a1b2c3) not found

Vissa loggningsleverantörer lagrar händelse-ID:t i ett fält, vilket möjliggör filtrering på ID:t.

Loggmeddelandemall

Varje logg-API använder en meddelandemall. Meddelandemallen kan innehålla platshållare för vilka argument anges. Använd namn för platshållarna, inte siffror. Platshållarnas ordning, inte deras namn, avgör vilka parametrar som används för att ange deras värden. I följande kod är parameternamnen ur sekvens i meddelandemallen:

string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

Föregående kod skapar ett loggmeddelande med parametervärdena i följd:

Parameter values: param1, param2

Kommentar

Tänk på när du använder flera platshållare i en enda meddelandemall, eftersom de är ordningsbaserade. Namnen används inte för att justera argumenten mot platshållarna.

Med den här metoden kan loggningsproviders implementera semantisk eller strukturerad loggning. Själva argumenten skickas till loggningssystemet, inte bara den formaterade meddelandemallen. Detta gör det möjligt för loggningsprovidrar att lagra parametervärdena som fält. Överväg följande loggningsmetod:

_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);

När du till exempel loggar till Azure Table Storage:

  • Varje Azure Table-entitet kan ha ID och RunTime egenskaper.
  • Tabeller med egenskaper förenklar frågor om loggade data. En fråga kan till exempel hitta alla loggar inom ett visst RunTime intervall utan att behöva parsa tidsgränsen för textmeddelandet.

Formatering av loggmeddelandemall

Loggmeddelandemallar stöder platshållarformatering. Mallar kan ange valfritt giltigt format för det angivna typargumentet. Tänk till exempel på följande Information mall för loggningsmeddelande:

_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022

I föregående exempel är instansen DateTimeOffset den typ som motsvarar mallen PlaceHolderName i loggningsmeddelandet. Det här namnet kan vara vad som helst eftersom värdena är ordningsbaserade. Formatet MMMM dd, yyyy är giltigt för DateTimeOffset typen.

Mer information om DateTime och DateTimeOffset formatering finns i Anpassade datum- och tidsformatsträngar.

Exempel

I följande exempel visas hur du formaterar en meddelandemall med platshållarsyntaxen {} . Dessutom visas ett exempel på hur platshållarsyntaxen {} kan undvikas med dess utdata. Slutligen visas även stränginterpolation med platshållare för mallar:

logger.LogInformation("Number: {Number}", 1);               // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3);           // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5);    // {Number}: 5

Dricks

  • I de flesta fall bör du använda formatering av loggmeddelandemallar vid loggning. Användning av stränginterpolation kan orsaka prestandaproblem.
  • Kodanalysregel CA2254: Mallen ska vara ett statiskt uttryck som hjälper dig att varna dig om platser där dina loggmeddelanden inte använder korrekt formatering.

Loggfel

Loggningsmetoderna har överlagringar som tar en undantagsparameter:

public void Test(string id)
{
    try
    {
        if (id is "none")
        {
            throw new Exception("Default Id detected.");
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(
            AppLogEvents.Error, ex,
            "Failed to process iteration: {Id}", id);
    }
}

Undantagsloggning är providerspecifik.

Standardloggnivå

Om standardloggnivån inte har angetts är Informationstandardvärdet för loggnivå .

Tänk till exempel på följande arbetstjänstapp:

  • Skapades med .NET Worker-mallarna.
  • appsettings.json och apparinställningar. Development.json har tagits bort eller bytt namn.

Med den föregående konfigurationen genererar navigering till sekretess- eller startsidan många Trace, Debugoch Information meddelanden med Microsoft i kategorinamnet.

Följande kod anger standardloggnivån när standardloggnivån inte har angetts i konfigurationen:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

await host.RunAsync();

Filterfunktion

En filterfunktion anropas för alla leverantörer och kategorier som inte har regler tilldelade till sig genom konfiguration eller kod:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddFilter((provider, category, logLevel) =>
{
    return provider.Contains("ConsoleLoggerProvider")
        && (category.Contains("Example") || category.Contains("Microsoft"))
        && logLevel >= LogLevel.Information;
});

using IHost host = builder.Build();

await host.RunAsync();

Föregående kod visar konsolloggar när kategorin innehåller Example eller Microsoft och loggnivån är Information eller högre.

Loggomfattningar

Ett omfång grupperar en uppsättning logiska åtgärder. Den här grupperingen kan användas för att koppla samma data till varje logg som skapas som en del av en uppsättning. Varje logg som skapas som en del av bearbetningen av en transaktion kan till exempel innehålla transaktions-ID:t.

Ett omfång:

Följande leverantörer stöder omfång:

Använd ett omfång genom att omsluta loggningsanrop i ett using block:

public async Task<T> GetAsync<T>(string id)
{
    T result;
    var transactionId = Guid.NewGuid().ToString();

    using (_logger.BeginScope(new List<KeyValuePair<string, object>>
        {
            new KeyValuePair<string, object>("TransactionId", transactionId),
        }))
    {
        _logger.LogInformation(
            AppLogEvents.Read, "Reading value for {Id}", id);

        var result = await _repository.GetAsync(id);
        if (result is null)
        {
            _logger.LogWarning(
                AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
        }
    }

    return result;
}

Följande JSON aktiverar omfång för konsolprovidern:

{
    "Logging": {
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Warning",
                "Default": "Information"
            }
        },
        "LogLevel": {
            "Default": "Debug"
        }
    }
}

Följande kod aktiverar omfång för konsolprovidern:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);

using IHost host = builder.Build();

await host.RunAsync();

Skapa loggar i Main

Följande kod loggar in Main genom att hämta en ILogger instans från DI när värden har skapats:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using IHost host = Host.CreateApplicationBuilder(args).Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");

await host.RunAsync();

Föregående kod förlitar sig på två NuGet-paket:

Projektfilen skulle se ut ungefär så här:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
  </ItemGroup>

</Project>

Inga asynkrona loggningsmetoder

Loggning bör vara så snabb att det inte är värt prestandakostnaden för asynkron kod. Om ett loggningsdatalager är långsamt ska du inte skriva till det direkt. Överväg att skriva loggmeddelandena till en snabb butik först och sedan flytta dem till det långsamma arkivet senare. När du till exempel loggar till SQL Server ska du inte göra det direkt i en Log metod, eftersom Log metoderna är synkrona. Lägg i stället synkront till loggmeddelanden i en minnesintern kö och låt en bakgrundsarbetare hämta meddelandena från kön för att utföra det asynkrona arbetet med att skicka data till SQL Server.

Ändra loggnivåer i en app som körs

Loggnings-API:et innehåller inget scenario för att ändra loggnivåer när en app körs. Vissa konfigurationsprovidrar kan dock läsa in konfigurationen igen, vilket omedelbart påverkar loggningskonfigurationen. Till exempel läser filkonfigurationsprovidern in loggningskonfigurationen igen som standard. Om konfigurationen ändras i kod medan en app körs kan appen anropa IConfigurationRoot.Reload för att uppdatera appens loggningskonfiguration.

NuGet-paket

Gränssnitten ILogger<TCategoryName> och ILoggerFactory implementeringarna och ingår i de flesta .NET SDK:er som implicit paketreferens. De är också uttryckligen tillgängliga i följande NuGet-paket när de inte refereras implicit:

Mer information om vilka .NET SDK som innehåller implicita paketreferenser finns i .NET SDK: table to implicit namespace (.NET SDK: table to implicit namespace).

Se även