Protokolování v C# a .NET
.NET podporuje vysoké výkonné strukturované protokolování prostřednictvím ILogger rozhraní API, které pomáhá monitorovat chování aplikací a diagnostikovat problémy. Protokoly je možné zapisovat do různých cílů konfigurací různých poskytovatelů protokolování. Základní zprostředkovatelé protokolování jsou integrované a k dispozici je také mnoho poskytovatelů třetích stran.
Začínáme
Tento první příklad ukazuje základy, ale je vhodný pouze pro triviální konzolovou aplikaci. Tato ukázková konzolová aplikace spoléhá na následující balíčky NuGet:
V další části se dozvíte, jak zlepšit kód s ohledem na škálování, výkon, konfiguraci a typické programovací vzory.
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");
Předchozí příklad:
- Vytvoří ILoggerFactory. Konfigurace
ILoggerFactory
uloží veškeré nastavení, které určuje, kam se odesílají logovací zprávy. V takovém případě nakonfigurujete zprostředkovatele protokolování konzoly tak, aby zprávy protokolu byly zapsány do konzoly. - Vytvoří kategorii ILogger s názvem Program. Kategorie
je , která je spojena s každou zprávou zaznamenanou objektem . Používá se k seskupení zpráv protokolu ze stejné třídy (nebo kategorie) společně při vyhledávání nebo filtrování protokolů. - Volání LogInformation pro protokolování zprávy na
Information
úrovni Úroveň protokolu označuje závažnost protokolované události a používá se k odfiltrování méně důležitých zpráv protokolu. Položka protokolu také obsahuje šablonu zprávy"Hello World! Logging is {Description}."
a párDescription = fun
klíč-hodnota. Název klíče (nebo zástupný symbol) pochází z slova uvnitř složených závorek v šabloně a hodnota pochází ze zbývajícího argumentu metody.
Tento soubor projektu v tomto příkladu obsahuje dva balíčky NuGet:
<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="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.3" />
</ItemGroup>
</Project>
Tip
Veškerý zdrojový kód příkladu protokolování je k dispozici v prohlížeči ukázek ke stažení. Další informace najdete v tématu Procházení ukázek kódu: Protokolování v .NET.
Protokolování v netriviální aplikaci
Při protokolování v méně triviálním scénáři byste měli zvážit provedení několika změn v předchozím příkladu:
Pokud vaše aplikace používá injektáž závislostí (DI) nebo hostitele, jako je WebApplication nebo Generic Host technologie ASP.NET, pak byste měli použít
ILoggerFactory
aILogger
objekty z příslušných DI kontejnerů místo jejich přímého vytváření. Další informace najdete v tématu Integrace s DI a hostiteli.Protokolování generování zdrojového kódu během kompilace bývá běžně lepší alternativou k
ILogger
rozšiřujícím metodám, jako jeLogInformation
. Generování zdroje protokolování nabízí lepší výkon, silnější psaní a zabraňuje šířenístring
konstant v rámci vašich metod. Nevýhodou je, že použití této techniky vyžaduje trochu více kódu.
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);
}
- Doporučeným postupem pro názvy kategorií protokolu je použít plně kvalifikovaný název třídy, která vytváří zprávu protokolu. To pomáhá spojit zprávy protokolu zpět s kódem, který je vytvořil, a nabízí dobrou úroveň řízení při filtrování protokolů.
CreateLogger přijímá
Type
ke snadnému pojmenování.
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");
}
}
- Pokud nepoužíváte protokoly konzoly jako jediné řešení pro monitorování produkčního prostředí, přidejte zprostředkovatele protokolování, které plánujete použít. K odesílání protokolů přes protokol OTLP (protokol OpenTelemetry) můžete například použít OpenTelemetry:
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");
Integrace s hosty a injektováním závislostí
Pokud vaše aplikace používá injektáž závislostí (DI) nebo hostitele, jako je technologie ASP.NET WebApplication nebo Generic Host, měli byste použít ILoggerFactory
a ILogger
objekty z kontejneru DI místo jejich přímého vytváření.
Získání ILoggeru z DI
Tento příklad získá objekt ILogger v hostované aplikaci pomocí ASP.NET Minimal APIs:
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);
}
Předchozí příklad:
- Vytvořili jsme jednoúčelovou službu, která volala
ExampleHandler
a mapovala příchozí webové požadavky pro spuštěníExampleHandler.HandleRequest
funkce. - Řádek 12 definuje primární konstruktor pro ExampleHandler, funkci přidanou v jazyce C# 12. Použití staršího stylu konstruktoru jazyka C# by fungovalo stejně dobře, ale je trochu více podrobné.
- Konstruktor definuje parametr typu
ILogger<ExampleHandler>
. ILogger<TCategoryName> je odvozen od ILogger a označuje, která kategorieILogger
objekt má. Kontejner DI najdeILogger
se správnou kategorií a poskytne ji jako argument konstruktoru. Pokud zatím neexistujeILogger
v této kategorii, kontejner DI ji automaticky vytvoří na základěILoggerFactory
z poskytovatele služeb. - Parametr
logger
přijatý v konstruktoru byl použit pro protokolování funkceHandleRequest
.
ILoggerFactory poskytovaný hostitelem
Tvůrci hostitelů inicializují výchozí konfiguraci a po sestavení hostitele pak do kontejneru DI hostitele přidají nakonfigurovaný ILoggerFactory
objekt. Před sestavením serveru můžete upravit konfiguraci protokolování prostřednictvím HostApplicationBuilder.Logging, WebApplicationBuilder.Logging, nebo podobná API u jiných serverů. Hosté také používají konfiguraci protokolování z výchozích konfiguračních zdrojů, např. appsettings.json a proměnných prostředí. Další informace naleznete v tématu Konfigurace v .NET.
Tento příklad se rozšiřuje na předchozí, aby přizpůsobil ILoggerFactory
poskytnutý WebApplicationBuilder
. Přidá OpenTelemetry jako zprostředkovatele protokolování, který přenáší protokoly přes protokol OTLP (protokol OpenTelemetry):
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
Vytvoření ILoggerFactory pomocí DI
Pokud používáte kontejner DI bez hostitele, použijte AddLogging ke konfiguraci a přidání ILoggerFactory
do kontejneru.
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);
}
}
Předchozí příklad:
- Vytvořili jste kontejner služby DI, který obsahuje nakonfigurovaný
ILoggerFactory
pro zápis do konzoly. - Přidán singleton
ExampleService
do kontejneru - Byla vytvořena instance
ExampleService
z kontejneru DI, která také automaticky vytvořilaILogger<ExampleService>
k použití jako argument konstruktoru. - Vyvoláno
ExampleService.DoSomeWork
, které použiloILogger<ExampleService>
pro záznam zprávy do konzole.
Konfigurujte protokolování
Konfigurace protokolování se nastavuje v kódu nebo prostřednictvím externích zdrojů, jako jsou konfigurační soubory a proměnné prostředí. Použití externí konfigurace je výhodné, pokud je to možné, protože se dá změnit bez opětovného sestavení aplikace. Některé úlohy, například nastavení zprostředkovatelů protokolování, se ale dají nakonfigurovat jenom z kódu.
Konfigurace protokolování bez kódu
Pro aplikace, které používají hostitele, se konfigurace protokolování běžně poskytuje v "Logging"
části souborů appsettings.{Environment}
.json. U aplikací, které nepoužívají hostitele, jsou externí zdroje konfigurace nastavené explicitně nebo nakonfigurované v kódu .
Soubor appsettings.Development.json je generován šablonami služby .NET Worker.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
V předchozím fragmentu kódu JSON:
- Kategorie protokolových úrovní
"Default"
,"Microsoft"
a"Microsoft.Hosting.Lifetime"
jsou specifikovány. - Hodnota
"Default"
se použije pro všechny kategorie, které nejsou jinak zadány, a tím se efektivně nastaví všechny výchozí hodnoty pro všechny kategorie"Information"
. Toto chování můžete přepsat zadáním hodnoty pro kategorii. - Kategorie
"Microsoft"
se vztahuje na všechny kategorie, které začínají na"Microsoft"
. - Kategorie
"Microsoft"
se protokoluje na úrovniWarning
protokolu a vyšší. - Kategorie
"Microsoft.Hosting.Lifetime"
je konkrétnější než kategorie"Microsoft"
, takže kategorie"Microsoft.Hosting.Lifetime"
zaznamenává na úrovni protokolu"Information"
a vyšší. - Není zadaný žádný konkrétní zprostředkovatel protokolování, takže se vlastnost
LogLevel
vztahuje na všechny povolené zprostředkovatele protokolování s výjimkou zprostředkovatele Windows EventLog.
Vlastnost Logging
může obsahovat vlastnost LogLevel a vlastnosti zprostředkovatele protokolování. Vlastnost LogLevel
určuje minimální úroveň protokolování pro vybrané kategorie. V předchozím JSONu jsou zadané úrovně záznamů Information
a Warning
. Vlastnost LogLevel
označuje závažnost protokolu v rozsahu od 0 do 6:
Trace
= 0, Debug
= 1, Information
= 2, Warning
= 3, Error
= 4, Critical
= 5 a None
= 6.
Když je zadaná vlastnost LogLevel
, protokolování se povolí pro zprávy na zadané úrovni a vyšší. V předchozím JSON se kategorie Default
zaznamenává pro Information
a vyšší. Protokolují se například zprávy Information
, Warning
, Error
a Critical
. Pokud není zadaná žádná vlastnost LogLevel
, nastaví se výchozí úroveň protokolování Information
. Další informace najdete v části Úrovně protokolování.
Vlastnost LogLevel
může být zadaná ve vlastnosti zprostředkovatele. Vlastnost LogLevel
pod zprostředkovatelem určuje úrovně protokolování pro daného zprostředkovatele a přepisuje obecná nastavení protokolování bez zprostředkovatele. Zvažte následující appsettings.json soubor:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
Nastavení v Logging.{ProviderName}.LogLevel
přepíše nastavení v Logging.LogLevel
. V předchozím kódu JSON Debug
je výchozí úroveň protokolu poskytovatele nastavená na Information
:
Logging:Debug:LogLevel:Default:Information
Výše uvedené nastavení určuje úroveň protokolování Information
pro všechny kategorie Logging:Debug:
s výjimkou kategorie Microsoft.Hosting
. Pokud je uvedená konkrétní kategorie, tato konkrétní kategorie přepíše výchozí kategorii. V předchozím JSONu kategorie "Microsoft.Hosting"
a "Default"
přepisují nastavení v Logging:LogLevel
Minimální úroveň protokolování je možné zadat pro:
- Konkrétní zprostředkovatele: například
Logging:EventSource:LogLevel:Default:Information
- Konkrétní kategorie: například
Logging:LogLevel:Microsoft:Warning
- Všechny zprostředkovatele a všechny kategorie:
Logging:LogLevel:Default:Warning
Žádné protokoly pod minimální úrovní se:
- Předáno poskytovateli.
- Protokolováno nebo zobrazeno.
Pokud chcete potlačit všechny logy, zadejte LogLevel.None. Vlastnost LogLevel.None
má hodnotu 6, která je vyšší než hodnota vlastnosti LogLevel.Critical
(5).
Pokud zprostředkovatel podporuje obory protokolování, vlastnost IncludeScopes
označuje, jestli jsou povolené. Další informace viz rozsahy protokolu .
Následující soubor appsettings.json obsahuje nastavení pro všechny předdefinované zprostředkovatele:
{
"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"
}
}
}
}
Ve výše uvedené ukázce:
- Kategorie a úrovně nejsou navrhované hodnoty. Ukázka obsahuje všechny výchozí zprostředkovatele.
- Nastavení v
Logging.{ProviderName}.LogLevel
přepíše nastavení vLogging.LogLevel
. Například úroveň vDebug.LogLevel.Default
přepíše úroveň vLogLevel.Default
. - Používá se alias každého poskytovatele. Každý zprostředkovatel definuje alias, který je možné použít v konfiguraci místo plně kvalifikovaného názvu typu. Předdefinované aliasy poskytovatelů jsou:
Console
Debug
EventSource
EventLog
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
Nastavení úrovně protokolování pomocí příkazového řádku, proměnných prostředí a další konfigurace
Úroveň protokolování může nastavit jakýkoli zprostředkovatel konfigurace. Můžete například vytvořit trvalou proměnnou prostředí s názvem Logging:LogLevel:Microsoft
hodnota Information
.
Vytvořte a přiřaďte trvalou proměnnou prostředí na základě úrovně protokolování.
:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M
V nové instanci příkazového řádku načtěte proměnnou prostředí.
:: Prints the env var value
echo %Logging__LogLevel__Microsoft%
Předchozí nastavení prostředí je uchováno v systému. Pokud chcete otestovat nastavení při použití aplikace vytvořené pomocí šablon služby .NET Worker, použijte dotnet run
příkaz v adresáři projektu po přiřazení proměnné prostředí.
dotnet run
Tip
Po nastavení proměnné prostředí restartujte integrované vývojové prostředí (IDE), aby byly k dispozici nově přidané proměnné prostředí.
V Azure App Service vyberte Nové nastavení aplikace na stránce Nastavení > Konfigurace. Nastavení aplikace Azure App Service jsou následující:
- Šifrováno při uložení a přenášeno přes šifrovaný kanál.
- Jsou vystaveny jako proměnné prostředí.
Další informace o nastavení hodnot konfigurace .NET pomocí proměnných prostředí najdete v tématu proměnné prostředí.
Konfigurace protokolování pomocí kódu
Ke konfiguraci protokolování v kódu použijte ILoggingBuilder API. K tomuto přístupu je možné přistupovat z různých míst:
- Při vytváření
ILoggerFactory
přímo nakonfigurujte v rámci LoggerFactory.Create. - Při použití DI bez hostitele nakonfigurujte v souboru LoggingServiceCollectionExtensions.AddLogging.
- Pokud používáte hostitele, nakonfigurujte jej pomocí HostApplicationBuilder.Logging, WebApplicationBuilder.Logging nebo jiných API specifických pro hostitele.
Tento příklad ukazuje nastavení poskytovatele protokolování konzoly a několika filtrů.
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");
V předchozím příkladu AddFilter se používá k úpravě úrovně protokolu, která je povolená pro různé kategorie.
AddConsole slouží k přidání poskytovatele konzolového protokolování. Ve výchozím nastavení nejsou protokoly se závažností Debug
povolené, ale protože konfigurace upravila filtry, zobrazí se na konzole ladicí zpráva „Hello Everyone“.
Uplatňování pravidel filtrování
Když se vytvoří objekt ILogger<TCategoryName>, objekt ILoggerFactory pro každého zprostředkovatele vybere jedno pravidlo, které se aplikuje na tento logger. Všechny zprávy zapsané instancí ILogger
se filtrují na základě vybraných pravidel. Pro každou dvojici zprostředkovatele a kategorie se z dostupných pravidel vybere nejkonkrétnější pravidlo.
Při vytvoření objektu ILogger
pro danou kategorii se u každého zprostředkovatele použije následující algoritmus:
- Vyberte všechna pravidla, která odpovídají zprostředkovateli nebo jeho aliasu. Pokud se nenajde žádná shoda, vyberou se všechna pravidla s neuvedeným zprostředkovatelem.
- Z výsledků předchozího kroku vyberte pravidla s nejdelší shodující se předponou kategorie. Pokud se nenajde žádná shoda, vyberte všechna pravidla, která neuvádějí kategorii.
- Pokud je vybraných více pravidel, vybere se poslední pravidlo.
- Pokud nejsou vybrána žádná pravidla, použijte LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) k určení minimální úrovně protokolování.
Kategorie protokolu
Při vytvoření objektu ILogger
se určí kategorie. Tato kategorie je součástí každé zprávy protokolu vytvořené danou instancí ILogger
. Řetězec kategorie je libovolný, ale konvence je použít plně kvalifikovaný název třídy. Například v aplikaci, která má službu definovanou jako následující objekt, může být kategorie "Example.DefaultService"
:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;
public DefaultService(ILogger<DefaultService> logger) =>
_logger = logger;
// ...
}
}
Pokud je požadovaná další kategorizace, je konvence použít hierarchický název připojením podkategorie k plně kvalifikovanému názvu třídy a explicitně zadat kategorii pomocí LoggerFactory.CreateLogger:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;
public DefaultService(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");
// ...
}
}
Volání CreateLogger
s pevným názvem může být užitečné při použití ve více třídách nebo typech, aby události mohly být uspořádány podle kategorie.
Výraz ILogger<T>
je ekvivalentní volání CreateLogger
s plně kvalifikovaným názvem typu T
.
Úroveň protokolu
Následující tabulka uvádí hodnoty vlastnosti LogLevel, vhodnou rozšiřující metodu Log{LogLevel}
a navrhované použití:
ÚroveňProtokolu | Hodnota | metoda | Popis |
---|---|---|---|
Trasování | 0 | LogTrace | Obsahuje nejpodrobnější zprávy. Tyto zprávy můžou obsahovat citlivá data aplikace. Tyto zprávy jsou ve výchozím nastavení zakázané a neměly by se povolovat v produkčním prostředí. |
Debug | 1 | LogDebug | Slouží pro účely ladění a vývoje. Vzhledem k velkému objemu používejte opatrně v produkci. |
Informace | 2 | LogInformation | Sleduje obecný tok aplikace. Může mít dlouhodobou hodnotu. |
Upozornění | 3 | LogWarning | Slouží k protokolování neobvyklých nebo neočekávaných událostí. Obvykle zahrnuje chyby nebo podmínky, které nezpůsobují selhání aplikace. |
Chyba | 4 | LogError | Slouží k protokolování chyb a výjimek, které není možné zpracovat. Tyto zprávy značí selhání v aktuální operaci nebo požadavku, nikoli selhání na úrovni aplikace. |
Kritická | 5 | LogCritical | Pro selhání, která vyžadují okamžitou pozornost. Příklady: scénáře ztráty dat, nedostatek místa na disku. |
Nic | 6 | Určuje, že by se neměly zapisovat žádné zprávy. |
Ve výše uvedené tabulce jsou hodnoty vlastnosti LogLevel
uvedené v pořadí od nejnižší po nejvyšší závažnost.
První parametr metody Log, LogLevel, označuje závažnost protokolu. Většina vývojářů místo volání metody Log(LogLevel, ...)
volá rozšiřující metody Log{LogLevel}. Rozšiřující Log{LogLevel}
metody volají metodu Log
a určují LogLevel
. Například následující dvě volání logování jsou funkčně ekvivalentní a produkují stejný výsledný protokol.
public void LogDetails()
{
var logMessage = "Details for log.";
_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
_logger.LogInformation(AppLogEvents.Details, logMessage);
}
AppLogEvents.Details
je ID události a implicitně je reprezentována konstantní Int32 hodnotou.
AppLogEvents
je třída, která obsahuje různé pojmenované konstanty identifikátoru a je zobrazena v oddílu ID události logu.
Následující kód vytvoří logy Information
a Warning
:
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;
}
V předchozím kódu je prvním Log{LogLevel}
parametrem AppLogEvents.Read
ID události protokolu. Druhý parametr je šablona zprávy se zástupnými symboly pro hodnoty argumentů, které poskytují zbývající parametry metody. Parametry metody jsou vysvětleny v části šablony zprávy message template dále v tomto článku.
Nakonfigurujte odpovídající úroveň protokolu a volejte správné Log{LogLevel}
metody, abyste mohli řídit, kolik výstupu protokolu se zapisuje do konkrétního média úložiště. Příklad:
- V produkčním prostředí:
- Protokolování na úrovni
Trace
neboDebug
generuje velké množství podrobných zpráv protokolu. Pokud chcete mít náklady pod kontrolou a nepřekročit limity úložiště dat, protokolujte zprávy úrovněTrace
aDebug
do velkoobjemového a nízkonákladového úložiště dat. Zvažte omezení úrovníTrace
aDebug
na konkrétní kategorie. - Protokolování na úrovni
Warning
ažCritical
by mělo generovat malé množství zpráv protokolu.- Náklady a limity úložiště obvykle nepředstavují problém.
- Malé množství protokolů znamená větší flexibilitu při výběru úložiště dat.
- Protokolování na úrovni
- Ve vývoji:
- Nastavte na
Warning
. - Při řešení potíží přidejte zprávy
Trace
neboDebug
. Pokud chcete omezit výstup, nastavte úroveňTrace
neboDebug
pouze pro kategorie, které zkoumáte.
- Nastavte na
Následující JSON sady Logging:Console:LogLevel:Microsoft:Information
:
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
ID události záznamu
Každý protokol může zadat identifikátor události, jedná se EventId o strukturu s volitelnými Id
vlastnostmi Name
jen pro čtení. Ukázkový zdrojový kód používá AppLogEvents
třídu k definování ID událostí:
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;
// ...
}
Tip
Další informace o převodu int
na EventId
naleznete v části EventId.Implicit (Int32 na EventId) operátor.
ID události spojuje sadu událostí. Například všechny protokoly související se čtením hodnot z úložiště můžou být 1001
.
Zprostředkovatel protokolování může protokolovat ID události v poli ID, ve zprávě protokolování nebo vůbec. Zprostředkovatel Debug nezobrazuje ID událostí. Zprostředkovatel Console zobrazuje ID událostí v závorkách za kategorií:
info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found
Někteří zprostředkovatelé protokolování uchovávají ID událostí v poli, což umožňuje filtrování podle ID.
Šablona zprávy protokolu
Každé rozhraní API pro protokoly používá šablony zprávy. Šablona zprávy může obsahovat zástupné symboly, pro které se poskytnou argumenty. Jako zástupné symboly používejte názvy, a ne čísla. Pořadí zástupných symbolů, nikoli jejich názvů, určuje, které parametry se použijí k zadání jejich hodnot. V následujícím kódu jsou názvy parametrů mimo pořadí v šabloně zprávy:
string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);
Předchozí kód vytvoří zprávu protokolu s hodnotami parametrů v posloupnosti:
Parameter values: param1, param2
Poznámka:
Při používání více zástupných symbolů v rámci jedné šablony zprávy mějte na paměti, že jsou založené na pořadí. Názvy se nepoužívají k uspořádání argumentů na zástupné symboly.
Tento přístup umožňuje zprostředkovatelům protokolování implementovat sémantické neboli strukturované protokolování. Do systému protokolování se předávají samotné argumenty, nikoli pouze formátovaná šablona zprávy. To umožňuje zprostředkovatelům protokolování uchovávat hodnoty parametrů jako pole. Zvažte následující metodu loggeru:
_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);
Například při protokolování do služby Azure Table Storage:
- Každá entita tabulky Azure může mít vlastnosti
ID
aRunTime
. - Tabulky s vlastnostmi zjednodušují dotazy na protokolovaná data. Dotaz může například vyhledat všechny protokoly v určitém rozsahu hodnot
RunTime
, aniž by musel parsovat čas z textové zprávy.
Formátování šablony zprávy protokolu
Šablony zpráv protokolu podporují formátování zástupných symbolů. Šablony mohou zadat libovolný platný formát pro daný argument typu. Představte si například následující Information
šablonu zprávy protokolovacího nástroje:
_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022
V předchozím příkladu je DateTimeOffset
instance typem, který odpovídá PlaceHolderName
v šabloně zprávy loggeru. Tento název může být libovolný, protože hodnoty jsou řaděné. Formát MMMM dd, yyyy
je platný pro DateTimeOffset
typ.
Další informace o formátování DateTime
a DateTimeOffset
naleznete v tématu Vlastní řetězce formátu data a času.
Příklady
Následující příklady ukazují, jak formátovat šablonu zprávy pomocí {}
zástupné syntaxe. Navíc je ukázán příklad escapingu syntaxe zástupného symbolu {}
spolu s jeho výstupem. Nakonec se zobrazí interpolace řetězců se zástupnými symboly šablon:
logger.LogInformation("Number: {Number}", 1); // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5
Tip
- Ve většině případů byste měli při protokolování použít formátování šablony logovacích zpráv. Použití interpolace řetězců může způsobit problémy s výkonem.
- Pravidlo analýzy kódu CA2254: Šablona by měla být statický výraz , který vás upozorní na místa, kde zprávy protokolu nepoužívají správné formátování.
Výjimky protokolu
Metody logování mají přetížení, které přijímají parametr výjimky:
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);
}
}
Protokolování výjimek se u jednotlivých zprostředkovatelů liší.
Výchozí úroveň protokolování
Pokud není nastavená výchozí úroveň protokolování, má výchozí úroveň protokolování hodnotu Information
.
Představte si například následující aplikaci pracovní služby:
- Vytvořeno pomocí šablon pracovních procesů .NET.
- appsettings.json a appsettings.Development.json byly odstraněny nebo přejmenovány.
V případě výše uvedeného nastavení se při přechodu na stránku ochrany osobních údajů nebo domovskou stránku vygeneruje velké množství zpráv úrovně Trace
, Debug
a Information
s textem Microsoft
v názvu kategorie.
Následující kód nastaví výchozí úroveň protokolování, pokud není nastavená v konfiguraci:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
await host.RunAsync();
Funkce filtru
Funkce filtru se volá pro všechny zprostředkovatele a kategorie, ke kterým nejsou v konfiguraci nebo kódu přiřazená pravidla:
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();
Výše uvedený kód zobrazí protokoly konzoly v případě, že kategorie obsahuje Example
nebo Microsoft
a úroveň protokolování je Information
nebo vyšší.
Rozsahy protokolování
Obor seskupí sadu logických operací. Toto seskupení lze použít k připojení stejných dat ke každému protokolu vytvořenému v rámci sady. Například všechny protokoly vytvořené v rámci zpracování transakce můžou obsahovat ID transakce.
Obor:
- Je typem objektu IDisposable, který vrací metoda BeginScope.
- Platí, dokud se neodstraní.
Obory podporují následující zprostředkovatelé:
Obor můžete použít tak, že zabalíte volání protokolovacího nástroje do bloku using
:
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;
}
Následující kód JSON umožňuje rozsahy pro poskytovatele konzoly:
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
Následující kód povoluje obory pro poskytovatele konzoly:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);
using IHost host = builder.Build();
await host.RunAsync();
Vytvořte protokoly v Main
Následující kód se přihlásí do Main
získáním instance ILogger
z injektáže závislostí po sestavení hostitele:
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();
Předchozí kód spoléhá na dva balíčky NuGet:
Soubor projektu by vypadal nějak takto:
<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>
Žádné metody asynchronního loggeru
Protokolování by mělo být tak rychlé, že asynchronní kód nedává smysl z hlediska dopadu na výkon. Pokud je datové úložiště pro protokolování pomalé, nezapisujte do něj přímo. Zvažte možnost nejprve zapisovat zprávy protokolu do rychlého úložiště a přesouvat je do pomalého úložiště později. Pokud například využíváte protokolování na SQL Server, neprovádějte to přímo v metodě Log
, protože metody Log
jsou synchronní. Místo toho synchronně přidávejte zprávy protokolu do fronty v paměti a nastavte pracovní proces na pozadí, který bude zprávy přetahovat z fronty a asynchronně publikovat data na SQL Server.
Změna úrovní protokolování ve spuštěné aplikaci
Rozhraní API pro protokolování neobsahuje scénář pro změnu úrovní protokolování, když je aplikace spuštěná. Někteří zprostředkovatelé konfigurace však podporují opětovné načtení konfigurace, která se okamžitě projeví na konfiguraci protokolování. Poskytovatel konfigurace souborů například ve výchozím nastavení znovu načte konfiguraci protokolování. Pokud se konfigurace změní v kódu, když je aplikace spuštěná, může aplikace volat IConfigurationRoot.Reload , aby aktualizovala konfiguraci protokolování aplikace.
Balíčky NuGet
Rozhraní ILogger<TCategoryName> a implementace ILoggerFactory jsou součástí většiny sad .NET SDK jako implicitní odkaz na balíček. Jsou také explicitně k dispozici v následujících balíčcích NuGet, pokud na tyto balíčky nejsou implicitně odkazovány:
- Rozhraní se nacházejí v balíčku Microsoft.Extensions.Logging.Abstractions.
- Výchozí implementace se nacházejí v balíčku Microsoft.Extensions.Logging.
Další informace o tom, která sada .NET SDK obsahuje implicitní odkazy na balíčky, najdete v tématu .NET SDK: tabulka pro implicitní obor názvů.