Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Rejestrowanie generowane przez źródło jest przeznaczone do dostarczania wysoce użytecznego i wysoce wydajnego rozwiązania do rejestrowania dla nowoczesnych aplikacji platformy .NET. Wygenerowany automatycznie kod źródłowy opiera się na interfejsie w połączeniu ILogger z funkcjami LoggerMessage.Define .
Generator źródła jest wyzwalany, gdy LoggerMessageAttribute jest używany w partial metodach rejestrowania. Po wyzwoleniu, autogeneruje implementację metod partial, które dekoruje. Jeśli wystąpi problem, tworzy diagnostykę czasu kompilacji z wskazówkami dotyczącymi odpowiedniego użycia. To rozwiązanie do rejestrowania w czasie kompilacji jest znacznie szybsze w czasie wykonywania niż wcześniej dostępne metody rejestrowania. Eliminuje boksowanie, przydziały tymczasowe i kopiowanie w maksymalnym możliwym stopniu.
Podstawowy sposób użycia
Aby użyć LoggerMessageAttributeklasy , klasa i metoda zużywania muszą mieć wartość partial. Generator kodu jest wyzwalany w czasie kompilacji i generuje implementację partial metody.
public static partial class Log
{
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
ILogger logger, string hostName);
}
W poprzednim przykładzie metoda rejestrowania jest static i poziom dziennika jest określony w definicji atrybutu. W przypadku używania atrybutu w kontekście ILogger statycznym wystąpienie jest wymagane jako parametr lub zmodyfikuj definicję, aby użyć this słowa kluczowego , aby zdefiniować metodę jako metodę rozszerzenia.
public static partial class Log
{
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
this ILogger logger, string hostName);
}
Możesz również użyć atrybutu w kontekście niestacjonanym. Rozważmy następujący przykład, w którym metoda rejestrowania jest zadeklarowana jako metoda wystąpienia. W tym kontekście metoda rejestrowania pobiera rejestrator przez uzyskanie ILogger dostępu do pola w klasie zawierającej.
public partial class InstanceLoggingExample
{
private readonly ILogger _logger;
public InstanceLoggingExample(ILogger logger)
{
_logger = logger;
}
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public partial void CouldNotOpenSocket(string hostName);
}
Począwszy od platformy .NET 9, metoda rejestrowania może dodatkowo pobrać rejestrator z podstawowego ILogger parametru konstruktora w klasie zawierającej.
public partial class InstanceLoggingExample(ILogger logger)
{
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public partial void CouldNotOpenSocket(string hostName);
}
Jeśli istnieje zarówno ILogger pole, jak i podstawowy parametr konstruktora, metoda logowania pobiera logger z pola.
Czasami poziom dziennika musi być dynamiczny, a nie statycznie wbudowany w kod. Można to zrobić, pomijając poziom dziennika z atrybutu i zamiast tego wymagając go jako parametru do metody rejestrowania.
public static partial class Log
{
[LoggerMessage(
EventId = 0,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
ILogger logger,
LogLevel level, /* Dynamic log level as parameter, rather than defined in attribute. */
string hostName);
}
Możesz pominąć komunikat rejestrowania, a String.Empty jest podany dla komunikatu. Stan zawiera argumenty sformatowane jako pary klucz-wartość.
using System.Text.Json;
using Microsoft.Extensions.Logging;
using ILoggerFactory loggerFactory = LoggerFactory.Create(
builder =>
builder.AddJsonConsole(
options =>
options.JsonWriterOptions = new JsonWriterOptions()
{
Indented = true
}));
ILogger<SampleObject> logger = loggerFactory.CreateLogger<SampleObject>();
logger.PlaceOfResidence(logLevel: LogLevel.Information, name: "Liana", city: "Seattle");
readonly file record struct SampleObject { }
public static partial class Log
{
[LoggerMessage(EventId = 23, Message = "{Name} lives in {City}.")]
public static partial void PlaceOfResidence(
this ILogger logger,
LogLevel logLevel,
string name,
string city);
}
Rozważmy przykładowe dane wyjściowe rejestrowania JsonConsole podczas korzystania z formatera.
{
"EventId": 23,
"LogLevel": "Information",
"Category": "\u003CProgram\u003EF...9CB42__SampleObject",
"Message": "Liana lives in Seattle.",
"State": {
"Message": "Liana lives in Seattle.",
"name": "Liana",
"city": "Seattle",
"{OriginalFormat}": "{Name} lives in {City}."
}
}
Ograniczenia metody dziennika
Metody rejestrowania, które są oznakowane LoggerMessageAttribute, muszą spełniać następujące wymagania:
- Metody rejestrowania muszą być
partiali zwrócić wartośćvoid. - Nazwy metod rejestrowania nie mogą rozpoczynać się od podkreślenia.
- Nazwy parametrów metod rejestrowania nie mogą rozpoczynać się od podkreślenia.
- Metody rejestrowania nie mogą być ogólne.
- Jeśli metoda rejestrowania to
static,ILoggerwystąpienie jest wymagane jako parametr.
Model generowania kodu zależy od nowoczesnego kompilatora języka C#, w wersji 9 lub nowszej. Aby uzyskać informacje na temat zmiany wersji językowej, zobacz Wersjonowanie języka C#.
Anatomia metody dziennika
Podpis ILogger.Log akceptuje LogLevel oraz opcjonalnie Exception, jak pokazano w poniższym przykładzie kodu.
public interface ILogger
{
void Log<TState>(
Microsoft.Extensions.Logging.LogLevel logLevel,
Microsoft.Extensions.Logging.EventId eventId,
TState state,
System.Exception? exception,
Func<TState, System.Exception?, string> formatter);
}
Ogólnie rzecz biorąc, pierwsze wystąpienie ILogger, LogLeveli Exception są traktowane specjalnie w podpisie metody dziennika generatora źródłowego. Kolejne wystąpienia są traktowane jak normalne parametry szablonu komunikatu:
// This is a valid attribute usage
[LoggerMessage(
EventId = 110, Level = LogLevel.Debug, Message = "M1 {Ex3} {Ex2}")]
public static partial void ValidLogMethod(
ILogger logger,
Exception ex,
Exception ex2,
Exception ex3);
// This causes a warning
[LoggerMessage(
EventId = 0, Level = LogLevel.Debug, Message = "M1 {Ex} {Ex2}")]
public static partial void WarningLogMethod(
ILogger logger,
Exception ex,
Exception ex2);
Ważne
Emitowane ostrzeżenia zawierają szczegółowe informacje dotyczące poprawnego użycia obiektu LoggerMessageAttribute. W poprzednim przykładzie WarningLogMethod podaje DiagnosticSeverity.WarningSYSLIB0025.
Don't include a template for `ex` in the logging message since it is implicitly taken care of.
Obsługa nazw szablonów bez uwzględniania wielkości liter
Generator wykonuje porównanie bez uwzględniania wielkości liter między elementami w szablonie komunikatu i nazwami argumentów w komunikacie dziennika. Oznacza to, że gdy ILogger wyliczy stan, argument jest pobierany przez szablon komunikatu, co może sprawić, że dzienniki będą lepiej używane:
public partial class LoggingExample
{
private readonly ILogger _logger;
public LoggingExample(ILogger logger)
{
_logger = logger;
}
[LoggerMessage(
EventId = 10,
Level = LogLevel.Information,
Message = "Welcome to {City} {Province}!")]
public partial void LogMethodSupportsPascalCasingOfNames(
string city, string province);
public void TestLogging()
{
LogMethodSupportsPascalCasingOfNames("Vancouver", "BC");
}
}
Rozważ przykładowe dane wyjściowe rejestrowania podczas korzystania z JsonConsole formatera:
{
"EventId": 13,
"LogLevel": "Information",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"City": "Vancouver",
"Province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}
Nieokreślona kolejność parametrów
Nie ma żadnych ograniczeń dotyczących kolejności parametrów metody dziennika. Deweloper może zdefiniować ILogger parametr jako ostatni, chociaż może wydawać się nieco niezręczny.
[LoggerMessage(
EventId = 110,
Level = LogLevel.Debug,
Message = "M1 {Ex3} {Ex2}")]
static partial void LogMethod(
Exception ex,
Exception ex2,
Exception ex3,
ILogger logger);
Wskazówka
Kolejność parametrów metody dziennika nie jest wymagana do odpowiadania kolejności symboli zastępczych szablonu. Zamiast tego nazwy symboli zastępczych w szablonie powinny być zgodne z parametrami. Rozważ następujące JsonConsole dane wyjściowe i kolejność błędów.
{
"EventId": 110,
"LogLevel": "Debug",
"Category": "ConsoleApp.Program",
"Message": "M1 System.Exception: Third time's the charm. System.Exception: This is the second error.",
"State": {
"Message": "M1 System.Exception: Third time's the charm. System.Exception: This is the second error.",
"ex2": "System.Exception: This is the second error.",
"ex3": "System.Exception: Third time's the charm.",
"{OriginalFormat}": "M1 {Ex3} {Ex2}"
}
}
Więcej przykładów logowania
W poniższych przykładach pokazano, jak pobrać nazwę zdarzenia, dynamicznie ustawić poziom dziennika i sformatować parametry rejestrowania. Metody rejestrowania to:
-
LogWithCustomEventName: pobierz nazwę zdarzenia za pomocąLoggerMessageatrybutu. -
LogWithDynamicLogLevel: ustaw poziom dziennika dynamicznie, aby zezwolić na ustawianie poziomu dziennika na podstawie danych wejściowych konfiguracji. -
UsingFormatSpecifier: Użyj specyfikatorów formatu, aby sformatować parametry rejestrowania.
public partial class LoggingSample
{
private readonly ILogger _logger;
public LoggingSample(ILogger logger)
{
_logger = logger;
}
[LoggerMessage(
EventId = 20,
Level = LogLevel.Critical,
Message = "Value is {Value:E}")]
public static partial void UsingFormatSpecifier(
ILogger logger, double value);
[LoggerMessage(
EventId = 9,
Level = LogLevel.Trace,
Message = "Fixed message",
EventName = "CustomEventName")]
public partial void LogWithCustomEventName();
[LoggerMessage(
EventId = 10,
Message = "Welcome to {City} {Province}!")]
public partial void LogWithDynamicLogLevel(
string city, LogLevel level, string province);
public void TestLogging()
{
LogWithCustomEventName();
LogWithDynamicLogLevel("Vancouver", LogLevel.Warning, "BC");
LogWithDynamicLogLevel("Vancouver", LogLevel.Information, "BC");
UsingFormatSpecifier(logger, 12345.6789);
}
}
Rozważ przykładowe dane wyjściowe rejestrowania podczas korzystania z SimpleConsole formatera:
trce: LoggingExample[9]
Fixed message
warn: LoggingExample[10]
Welcome to Vancouver BC!
info: LoggingExample[10]
Welcome to Vancouver BC!
crit: LoggingExample[20]
Value is 1.234568E+004
Rozważ przykładowe dane wyjściowe rejestrowania podczas korzystania z JsonConsole formatera:
{
"EventId": 9,
"LogLevel": "Trace",
"Category": "LoggingExample",
"Message": "Fixed message",
"State": {
"Message": "Fixed message",
"{OriginalFormat}": "Fixed message"
}
}
{
"EventId": 10,
"LogLevel": "Warning",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"city": "Vancouver",
"province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}
{
"EventId": 10,
"LogLevel": "Information",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"city": "Vancouver",
"province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}
{
"EventId": 20,
"LogLevel": "Critical",
"Category": "LoggingExample",
"Message": "Value is 1.234568E+004",
"State": {
"Message": "Value is 1.234568E+004",
"value": 12345.6789,
"{OriginalFormat}": "Value is {Value:E}"
}
}
Redagowanie poufnych informacji w dziennikach
Podczas rejestrowania poufnych danych ważne jest, aby zapobiec przypadkowemu narażeniu. Nawet w przypadku metod rejestrowania generowanych w czasie kompilacji rejestrowanie nieprzetworzonych wartości poufnych może prowadzić do wycieków danych i problemów ze zgodnością.
Biblioteka Microsoft.Extensions.Telemetry udostępnia zaawansowane funkcje rejestrowania i wzbogacania telemetrii dla aplikacji platformy .NET. Rozszerza potok rejestrowania, aby automatycznie stosować zaciemnianie na sklasyfikowanych danych podczas zapisywania logów. Umożliwia wymuszanie zasad ochrony danych w całej aplikacji poprzez zintegrowanie anonimizacji z procesem rejestrowania. Została zaprojektowana dla aplikacji wymagających zaawansowanej telemetrii i analizy dzienników.
Aby włączyć redaction, użyj biblioteki Microsoft.Extensions.Compliance.Redaction . Ta biblioteka udostępnia redagatory — składniki przekształcające poufne dane (na przykład przez wymazywanie, maskowanie lub skrót), dzięki czemu dane wyjściowe są bezpieczne. Redaktorzy są wybierani na podstawie klasyfikacji danych, która umożliwia oznaczanie danych zgodnie z ich poufnością (na przykład osobistą, prywatną lub publiczną).
Aby użyć redaction z metodami rejestrowania generowanymi przez źródło, należy:
- Klasyfikowanie poufnych danych przy użyciu systemu klasyfikacji danych.
- Zarejestruj i skonfiguruj redaktory dla każdej klasyfikacji w kontenerze DI.
- Włącz redaction w potoku rejestrowania.
- Sprawdź dzienniki, aby upewnić się, że żadne poufne dane nie są widoczne.
Jeśli na przykład masz komunikat dziennika z parametrem, który jest uważany za prywatny:
[LoggerMessage(0, LogLevel.Information, "User SSN: {SSN}")]
public static partial void LogPrivateInformation(
this ILogger logger,
[MyTaxonomyClassifications.Private] string SSN);
Musisz mieć ustawienie podobne do następującego:
using Microsoft.Extensions.Telemetry;
using Microsoft.Extensions.Compliance.Redaction;
var services = new ServiceCollection();
services.AddLogging(builder =>
{
// Enable redaction.
builder.EnableRedaction();
});
services.AddRedaction(builder =>
{
// configure redactors for your data classifications
builder.SetRedactor<StarRedactor>(MyTaxonomyClassifications.Private);
});
public void TestLogging()
{
LogPrivateInformation("MySSN");
}
Dane wyjściowe powinny wyglądać następująco:
User SSN: *****
Takie podejście gwarantuje, że rejestrowane są tylko dane zredagowane, nawet w przypadku korzystania z interfejsów API rejestrowania wygenerowanych w czasie kompilacji. Możesz użyć różnych redaktorów dla różnych typów danych lub klasyfikacji i centralnie zaktualizować logikę redagowania.
Aby uzyskać więcej informacji na temat klasyfikowania danych, zobacz Klasyfikacja danych na platformie .NET. Aby uzyskać więcej informacji na temat redagowania i redagatorów, zobacz Redagacja danych na platformie .NET.
Podsumowanie
Dzięki pojawieniu się generatorów źródeł języka C# pisanie wysoce wydajnych interfejsów API rejestrowania jest łatwiejsze. Korzystanie z podejścia generatora źródła ma kilka kluczowych korzyści:
- Umożliwia zachowanie struktury rejestrowania i włączenie dokładnej składni formatu wymaganej przez szablony komunikatów.
- Umożliwia podawanie alternatywnych nazw symboli zastępczych szablonu i używanie specyfikatorów formatu.
- Umożliwia przekazywanie wszystkich oryginalnych danych w taki sposób, jak jest, bez żadnych komplikacji dotyczących sposobu ich przechowywania, zanim coś zostanie wykonane z nim (inne niż utworzenie obiektu
string). - Zapewnia diagnostykę specyficzną dla rejestrowania i emituje ostrzeżenia dotyczące zduplikowanych identyfikatorów zdarzeń.
Ponadto istnieją korzyści wynikające z ręcznego używania polecenia LoggerMessage.Define:
- Krótsza i prostsza składnia: Użycie atrybutów deklaratywnych, a nie kodowanie standardowy.
- Środowisko deweloperskie z przewodnikiem: generator wyświetla ostrzeżenia ułatwiające deweloperom wykonywanie odpowiednich czynności.
- Obsługa dowolnej liczby parametrów rejestrowania.
LoggerMessage.Defineobsługuje maksymalnie sześć. - Obsługa dynamicznego poziomu dziennika. To nie jest możliwe za pomocą samego
LoggerMessage.Define.