Fordítási idő naplózási forrás létrehozása
A .NET 6 bemutatja a típust LoggerMessageAttribute
. Ez az attribútum a Microsoft.Extensions.Logging
névtér része, és használat esetén a forrás létrehoz egy teljesítménynaplózási API-kat. A forrásgenerációs naplózási támogatás úgy lett kialakítva, hogy magas szintű használható és nagy teljesítményű naplózási megoldást biztosítson a modern .NET-alkalmazásokhoz. Az automatikusan létrehozott forráskód a felületre és a ILogger funkciókra LoggerMessage.Define támaszkodik.
A forrásgenerátor akkor aktiválódik, ha LoggerMessageAttribute
naplózási módszereken partial
használják. Ha aktiválódik, képes automatikusan létrehozni a dekorálási metódusok implementálását partial
, vagy lefordított idejű diagnosztikát készíteni a megfelelő használatra vonatkozó tippekkel. A fordítási idő naplózási megoldása általában lényegesen gyorsabb futásidőben, mint a meglévő naplózási módszerek. Ezt úgy éri el, hogy a lehető legnagyobb mértékben megszünteti a boxolást, az ideiglenes kiosztásokat és a másolatokat.
Alapszintű használat
A használathoz a LoggerMessageAttribute
fogyasztó osztálynak és a metódusnak kell lennie partial
. A kódgenerátor fordításkor aktiválódik, és létrehozza a metódus implementációját partial
.
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);
}
Az előző példában a naplózási módszer, static
a naplószint pedig az attribútumdefinícióban van megadva. Ha statikus környezetben használja az attribútumot, akkor vagy a ILogger
példányra van szükség paraméterként, vagy módosítsa a definíciót, hogy a this
kulcsszóval bővítménymetódusként definiálja a metódust.
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);
}
Az attribútumot nem statikus környezetben is használhatja. Vegye figyelembe az alábbi példát, ahol a naplózási módszer példánymetódusként van deklarálva. Ebben az összefüggésben a naplózási módszer a naplózót a tartalmazó osztály egyik ILogger
mezőjének elérésével kapja meg.
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);
}
A .NET 9-től kezdődően a naplózási módszer emellett lekérheti a naplózót a tartalmazó osztály egyik ILogger
elsődleges konstruktorparaméteréből.
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);
}
Ha egy mező és egy elsődleges konstruktorparaméter is ILogger
létezik, a naplózási módszer lekéri a naplózót a mezőből.
Néha a naplószintnek dinamikusnak kell lennie, nem pedig statikusan be kell építeni a kódba. Ezt úgy teheti meg, hogy kihagyja a naplószintet az attribútumból, és ehelyett paraméterként kell megadnia a naplózási módszerhez.
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);
}
Kihagyhatja a naplózási üzenetet, és String.Empty meg lesz adva az üzenethez. Az állapot tartalmazza a kulcs-érték párokként formázott argumentumokat.
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);
}
A formázó használatakor vegye figyelembe a példa naplózási kimenetét JsonConsole
.
{
"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}."
}
}
Naplómetódus korlátozásai
A naplózási LoggerMessageAttribute
módszerek használatakor bizonyos korlátozásokat be kell tartani:
- A naplózási módszereknek meg kell lenniük
partial
, és vissza kell adniukvoid
. - A naplózási metódusok neve nem kezdődhet aláhúzásjellel.
- A naplózási módszerek paraméternevei nem kezdődnek aláhúzásjellel.
- A naplózási módszerek nem definiálhatók beágyazott típusban.
- A naplózási módszerek nem lehetnek általánosak.
- Ha naplózási módszerről van szó
static
, aILogger
példány paraméterként szükséges.
A kódgenerálási modell attól függ, hogy a kód egy modern, 9-es vagy újabb verziójú C#-fordítóval van lefordítva. A C# 9.0 fordító elérhetővé vált a .NET 5-tel. Modern C#-fordítóra való frissítéshez szerkessze a projektfájlt a C# 9.0-s célként.
<PropertyGroup>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
További információ: C# nyelvi verziószámozás.
Naplómetódus anatómiája
Az ILogger.Log aláírás az alább látható módon elfogadja az LogLevel és opcionálisan egy aláírást Exceptionis.
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);
}
Általános szabályként a , LogLevel
első példányát ILogger
kezelik, és Exception
kifejezetten a forrásgenerátor naplómetódusában kezelik. A következő példányok az üzenetsablon normál paramétereiként lesznek kezelve:
// 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);
Fontos
A megjelenő figyelmeztetések részletesen ismertetik a helyes használatot LoggerMessageAttribute
. Az előző példában a WarningLogMethod
következőt fogja jelenteni: SYSLIB0025
aDiagnosticSeverity.Warning
.
Don't include a template for `ex` in the logging message since it is implicitly taken care of.
Kis- és nagybetűket nem megkülönböztető sablonnév támogatása
A generátor kis- és nagybetűk közötti összehasonlítást végez az üzenetsablon elemei és a naplóüzenet argumentumnevei között. Ez azt jelenti, hogy amikor az ILogger
enumerálja az állapotot, az argumentumot az üzenetsablon veszi fel, így a naplók sokkal hasznosabbak lehetnek:
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");
}
}
Fontolja meg a példa naplózási kimenetet a JsonConsole
formázó használatakor:
{
"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}!"
}
}
Meghatározatlan paramétersorrend
A naplómetódus paramétereinek sorrendjére nincsenek korlátozások. A fejlesztő az ILogger
utolsó paraméterként definiálhatja a paramétert, bár kissé kínosnak tűnhet.
[LoggerMessage(
EventId = 110,
Level = LogLevel.Debug,
Message = "M1 {Ex3} {Ex2}")]
static partial void LogMethod(
Exception ex,
Exception ex2,
Exception ex3,
ILogger logger);
Tipp.
A naplómetódus paramétereinek sorrendje nem szükséges ahhoz, hogy megfeleljen a sablon helyőrzőinek sorrendjének. Ehelyett a sablon helyőrzőinek nevei várhatóan megfelelnek a paramétereknek. Vegye figyelembe a következő JsonConsole
kimenetet és a hibák sorrendjét.
{
"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}"
}
}
További naplózási példák
Az alábbi minták bemutatják, hogyan kérhető le az esemény neve, hogyan állíthatja be dinamikusan a naplószintet, és hogyan formázhatja a naplózási paramétereket. A naplózási módszerek a következők:
LogWithCustomEventName
: Eseménynév lekérése attribútumon keresztülLoggerMessage
.LogWithDynamicLogLevel
: Dinamikusan állítsa be a naplószintet, hogy a naplószint a konfigurációs bemenet alapján legyen beállítva.UsingFormatSpecifier
: A naplózási paraméterek formázásához használjon formátumjelölőket.
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);
}
}
Fontolja meg a példa naplózási kimenetet a SimpleConsole
formázó használatakor:
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
Fontolja meg a példa naplózási kimenetet a JsonConsole
formázó használatakor:
{
"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}"
}
}
Összegzés
A C#-forrásgenerátorok megjelenésével a nagy teljesítményű naplózási API-k írása sokkal egyszerűbb. A forrásgenerátor megközelítésének számos fő előnye van:
- Lehetővé teszi a naplózási struktúra megőrzését, és lehetővé teszi az üzenetsablonok által megkövetelt pontos formátumszintaxis megőrzését.
- Lehetővé teszi alternatív nevek megadását a sablon helyőrzőinek és formátumjelölők használatával.
- Lehetővé teszi az összes eredeti adat átadását, anélkül, hogy bonyodalmak merülnek fel azzal kapcsolatban, hogy hogyan tárolják őket, mielőtt valami befejeződik vele (a létrehozáson
string
kívül). - Naplózásspecifikus diagnosztikát biztosít, és figyelmeztetéseket bocsát ki ismétlődő eseményazonosítókhoz.
Emellett a manuális használatnak LoggerMessage.Defineis vannak előnyei:
- Rövidebb és egyszerűbb szintaxis: Deklaratív attribútumhasználat a sablon kódolása helyett.
- Irányított fejlesztői élmény: A generátor figyelmeztetéseket ad, amelyek segítenek a fejlesztőknek a helyes lépésben.
- Tetszőleges számú naplózási paraméter támogatása.
LoggerMessage.Define
legfeljebb hatot támogat. - Dinamikus naplószint támogatása. Ez egyedül nem lehetséges
LoggerMessage.Define
.