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. 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ár Description = funklíč-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.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
  </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 a ILogger 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 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á 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");
    }
}
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á kategorie ILogger objekt má. Kontejner DI vyhledá ILogger správnou kategorii a poskytne ji jako argument konstruktoru. Pokud tato kategorie ještě neexistuje ILogger , kontejner DI ho ILoggerFactory automaticky vytvoří 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 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řila ILogger<ExampleService> použití jako argument konstruktoru.
  • Vyvoláno ExampleService.DoSomeWork , které použilo ILogger<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 úrovni Warning 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 vlastnosti Logging.LogLevel. Například úroveň ve vlastnosti Debug.LogLevel.Default přepíše úroveň ve vlastnosti 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í 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:

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

První parametr metody Log{LogLevel}, AppLogEvents.Read, ve výše uvedeném kódu je 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 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 úrovně Trace nebo Debug. Pokud chcete omezit výstup, nastavte úroveň Trace nebo Debug pouze pro kategorie, které zkoumáte.

Následující sady Logging:Console:LogLevel:Microsoft:InformationJSON:

{
    "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 EventIdEventId.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 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 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:

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:

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é