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 Uloží
ILoggerFactory
veškerou konfiguraci, která určuje, kam se odesílají zprávy protokolu. 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 přidružena
string
ke každé zprávě protokolované objektemILogger
. 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 obsahuje také šablonu"Hello World! Logging is {Description}."
zprávy 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="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
</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 jiné než triviá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 ASP. WebApplication nebo Generic Host technologie NET pak byste měli místo jejich přímého vytváření použít
ILoggerFactory
aILogger
objekty z příslušných kontejnerů DI. Další informace najdete v tématu Integrace s DI a hostiteli.Protokolování generování zdroje času kompilace je obvykle 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á možnost
Type
snadného 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 hostiteli a injektáží závislostí
Pokud vaše aplikace používá injektáž závislostí (DI) nebo hostitele, jako je ASP. WebApplication nebo Generic Host technologie NET pak byste měli místo přímého vytváření použít ILoggerFactory
a ILogger
objekty z kontejneru DI.
Získání ILoggeru z DI
Tento příklad získá objekt ILogger v hostované aplikaci pomocí ASP.NET minimálních rozhraní API:
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 8 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 vyhledáILogger
správnou kategorii a poskytne ji jako argument konstruktoru. Pokud tato kategorie ještě neexistujeILogger
, kontejner DI hoILoggerFactory
automaticky vytvoří 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 vytvořením hostitele můžete upravit konfiguraci protokolování prostřednictvím HostApplicationBuilder.Loggingrozhraní , WebApplicationBuilder.Loggingnebo podobných rozhraní API na jiných hostitelích. Hostitelé také používají konfiguraci protokolování z výchozích zdrojů konfigurace jako appsettings.json a proměnné 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 poskytnutého ILoggerFactory
uživatelem 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 obsahující nakonfigurovaný
ILoggerFactory
zápis do konzoly. - Přidání jednohotonu
ExampleService
do kontejneru - Vytvořili jste instanci
ExampleService
kontejneru DI, která také automaticky vytvořilaILogger<ExampleService>
použití jako argument konstruktoru. - Vyvoláno
ExampleService.DoSomeWork
, které použiloILogger<ExampleService>
k protokolování zprávy do konzoly.
Konfigurace 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 .
Následující nastavení aplikace. Development.json soubor 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:
- Jsou zadány
"Default"
kategorie na úrovni protokolu"Microsoft"
a"Microsoft.Hosting.Lifetime"
. - 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ž"Microsoft"
kategorie, takže"Microsoft.Hosting.Lifetime"
protokoly kategorií na úrovni"Information"
protokolu 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 kódu JSON Information
a Warning
úrovně protokolu jsou zadané. 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 kódu JSON Default
se kategorie zaprotokoluje a Information
je vyšší. Protokolují se například zprávy úrovně 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
v rámci zprostředkovatele určuje úrovně protokolování pro daného zprostředkovatele a přepisuje nastavení protokolování pro všechny 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í ve vlastnosti Logging.{ProviderName}.LogLevel
přepíše nastavení ve vlastnosti 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 kódu JSON Logging:Debug:LogLevel
kategorie "Microsoft.Hosting"
a "Default"
přepsání 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:
- Nepředávají zprostředkovateli.
- Neprotokolují ani nezobrazují.
Pokud chcete potlačit všechny protokoly, 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 najdete v části Obory protokolování.
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í ve vlastnosti
Logging.{ProviderName}.LogLevel
přepíše nastavení ve vlastnostiLogging.LogLevel
. Například úroveň ve vlastnostiDebug.LogLevel.Default
přepíše úroveň ve vlastnostiLogLevel.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í vzhledem k hodnotě na úrovni protokolu.
:: 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 trvalé v prostředí. 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 se:
- Šifrují v neaktivním uloženém stavu a přenášejí přes šifrovaný kanál.
- Vystavují 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 rozhraní 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 LoggerFactory.Createsouboru . - Při použití DI bez hostitele nakonfigurujte v LoggingServiceCollectionExtensions.AddLoggingsouboru .
- Pokud používáte hostitele, nakonfigurujte rozhraní HostApplicationBuilder.LoggingWebApplicationBuilder.Logging API specifické pro hostitele nebo jiného hostitele.
Tento příklad ukazuje nastavení zprostředkovatele protokolování konzoly a několik 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í zprostředkovatele protokolování konzoly. Ve výchozím nastavení nejsou protokoly se Debug
závažností povolené, ale protože konfigurace upravila filtry, zobrazí se v konzole zpráva ladění "Hello Everyone".
Uplatňování pravidel filtrování
Když se vytvoří objekt ILogger<TCategoryName>, objekt ILoggerFactory vybere pro každého zprostředkovatele jedno pravidlo, které se použije na daný protokolovací nástroj. 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:
- Vyberou se 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ýsledku předchozího kroku se vyberou pravidla s nejdelší shodou předpony kategorie. Pokud se nenajde žádná shoda, vyberou se 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 se službou definovanou jako následující objekt může být "Example.DefaultService"
kategorie:
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í metody 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 | 0 | LogDebug | Slouží pro účely ladění a vývoje. Vzhledem k velkému objemu používejte tuto úroveň opatrně v produkčním prostředí. |
Informace | 2 | LogInformation | Sleduje obecný tok aplikace. Může mít dlouhodobou hodnotu. |
Upozorňující | 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 | Slouží k protokolování událostí, 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í protokolování jsou funkčně ekvivalentní a generují stejné protokoly:
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á zveřejňuje různé pojmenované identifikátor konstanty a je zobrazena v oddílu ID události protokolu.
Následující kód vytvoří protokoly 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 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 úrovně
Trace
neboDebug
. Pokud chcete omezit výstup, nastavte úroveňTrace
neboDebug
pouze pro kategorie, které zkoumáte.
- Nastavte na
Následující sady Logging:Console:LogLevel:Microsoft:Information
JSON:
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
ID události protokolu
Každý protokol může zadat identifikátor události, jedná se EventId o strukturu s volitelnými Name
vlastnostmi Id
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 operátor EventId
EventId.Implicit(Int32 na Id události)
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žití více zástupných symbolů v rámci jedné šablony zprávy mějte na paměti, protože jsou založené na řadách. Názvy se nepoužívají k zarovná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 DateTimeOffset
je instance typem, který odpovídá PlaceHolderName
šabloně zprávy protokolovacího nástroje. 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 DateTime
formátování a DateTimeOffset
formátování 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. Kromě toho se pomocí výstupu zobrazí příklad escapingu {}
zástupné syntaxe. 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 při protokolování měli použít formátování šablony zpráv protokolu. 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 protokolovacího nástroje obsahují přetížení, která jako parametr přebírají výjimku:
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 nastavení aplikací. Development.json odstraněno nebo přejmenováno.
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šší.
Obory protokolování
Obor seskupí sadu logických operací. Toto seskupení je možné využít k připojení stejných dat ke všem protokolům vytvořeným 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 obory pro zprostředkovatele 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();
Vytváření protokolů v třídě Main
Následující kód po sestavení hostitele získá z injektáže závislostí instanci ILogger
a nastaví protokolování v třídě Main
:
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é asynchronní metody protokolovacího nástroje
Protokolování by mělo být tak rychlé, že asynchronní kód nedává smysl z hlediska dopadu na výkon. Pokud je úložiště dat 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í neumožňuje změnit ú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 ILoggerFactory implementace jsou součástí většiny sad .NET SDK jako implicitní odkaz na balíčky. 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ů.