Sdílet prostřednictvím


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ár Description = 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 a ILogger 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 je LogInformation. 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");
    }
}
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á kategorie ILogger objekt má. Kontejner DI najde ILogger se správnou kategorií a poskytne ji jako argument konstruktoru. Pokud zatím neexistuje ILogger 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í funkce HandleRequest .

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řila ILogger<ExampleService> k použití jako argument konstruktoru.
  • Vyvoláno ExampleService.DoSomeWork, které použilo ILogger<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 úrovni Warning 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í v Logging.LogLevel. Například úroveň v Debug.LogLevel.Default přepíše úroveň v LogLevel.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:

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.ReadID 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 nebo Debug 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 a Debug do velkoobjemového a nízkonákladového úložiště dat. Zvažte omezení úrovní Trace a Debug na konkrétní kategorie.
    • Protokolování na úrovni WarningCritical 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.
  • Ve vývoji:
    • Nastavte na Warning.
    • Při řešení potíží přidejte zprávy Trace nebo Debug. Pokud chcete omezit výstup, nastavte úroveň Trace nebo Debug pouze pro kategorie, které zkoumáte.

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 a RunTime.
  • 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:

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:

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

Viz také