Condividi tramite


Logging in C# and .NET

.NET supporta la registrazione strutturata e a prestazioni elevate tramite l'API ILogger per monitorare il comportamento dell'applicazione e diagnosticare i problemi. I log possono essere scritti in destinazioni diverse configurando diversi provider di registrazione. I provider di registrazione di base sono incorporati e sono disponibili anche molti provider di terze parti.

Inizia

Questo primo esempio illustra le nozioni di base, ma è adatto solo per un'app console semplice. Questa app console di esempio si basa sui pacchetti NuGet seguenti:

Nella sezione successiva viene illustrato come migliorare il codice considerando la scalabilità, le prestazioni, la configurazione e i modelli di programmazione tipici.

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");

L'esempio precedente:

  • Crea un oggetto ILoggerFactory. ILoggerFactory archivia tutte le configurazioni che determinano dove vengono inviati i messaggi di log. In questo caso, si configura il provider di registrazione della console in modo che i messaggi di log vengano scritti nella console.
  • Crea un oggetto ILogger con una categoria denominata "Program". La categoria è un oggetto string associato a ogni messaggio registrato dall'oggetto ILogger. Viene usato per raggruppare i messaggi di log dalla stessa classe (o categoria) durante la ricerca o il filtro dei log.
  • Chiama LogInformation per registrare un messaggio a livello Information. Il livello di log indica la gravità dell'evento registrato e viene usato per filtrare i messaggi di log meno importanti. La voce di log include anche un modello di messaggio"Hello World! Logging is {Description}." e una coppia chiave-valore Description = fun. The key name (or placeholder) comes from the word inside the curly braces in the template and the value comes from the remaining method argument.

Il file di progetto per questo esempio include due pacchetti 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.4" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
  </ItemGroup>

</Project>

Suggerimento

Tutto il codice sorgente di esempio di registrazione è disponibile nel browser Samples per il download. Per altre informazioni, vedere Esplorare gli esempi di codice: Registrazione in .NET.

Registrazione in un'app non semplice

Quando si esegue l'accesso in uno scenario meno semplice, è consigliabile apportare diverse modifiche all'esempio precedente:

  • Se l'applicazione usa l’Inserimento delle dipendenze (DI) o un host, ad esempio WebApplication di ASP.NET o Host generico, è quindi consigliabile usare oggetti ILoggerFactory e ILogger dai rispettivi contenitori DI anziché crearli direttamente. Per ulteriori informazioni, vedere Integrazione con DI e Host.

  • La registrazione della generazione del codice sorgente in fase di compilazione è di solito un'alternativa migliore ai metodi di estensione ILogger come LogInformation. Logging source generation offers better performance, stronger typing, and avoids spreading string constants throughout your methods. Il compromesso consiste nel fatto che l'uso di questa tecnica richiede un po' più di codice.

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);
}
  • La procedura consigliata per i nomi delle categorie di log consiste nell'usare il nome completo della classe che crea il messaggio di log. Ciò consente di correlare i messaggi di log al codice che li ha generati e offre un buon livello di controllo durante il filtro dei log. CreateLogger accetta un Type per semplificare questa denominazione.
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");

Integration with hosts and dependency injection

Se l'applicazione usa l'Iniezione delle Dipendenze (DI) o un host, ad esempio WebApplication di ASP.NET o Generic Host, è quindi consigliabile usare oggetti ILoggerFactory e ILogger dal contenitore DI anziché crearli direttamente.

Get an ILogger from DI

Questo esempio ottiene un oggetto ILogger in un'app ospitata usando ASP.NET API minime:

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

L'esempio precedente:

  • È stato creato un servizio singleton denominato ExampleHandler ed è stato eseguito il mapping delle richieste Web in ingresso per eseguire la funzione ExampleHandler.HandleRequest.
  • Line 12 defines a primary constructor for the ExampleHandler, a feature added in C# 12. L'uso del costruttore C# della vecchia scuola funziona altrettanto bene, ma è un po' più verboso.
  • Il costruttore definisce un parametro di tipo ILogger<ExampleHandler>. ILogger<TCategoryName> deriva da ILogger e indica la categoria di cui dispone l'oggetto ILogger. Il contenitore DI individua un oggetto ILogger con la categoria corretta e lo specifica come argomento del costruttore. Se non esiste ancora alcuna ILogger con tale categoria, il contenitore DI lo crea automaticamente dal ILoggerFactory nel provider di servizi.
  • Il parametro logger ricevuto nel costruttore è stato usato per la registrazione nella funzione HandleRequest.

ILoggerFactory fornito dall'host

Host builders initialize default configuration, then add a configured ILoggerFactory object to the host's DI container when the host is built. Prima che l'host venga compilato, è possibile modificare la configurazione della registrazione tramite HostApplicationBuilder.Logging, WebApplicationBuilder.Logging o API simili in altri host. Hosts also apply logging configuration from default configuration sources as appsettings.json and environment variables. Per altre informazioni, vedi Configurazione in .NET.

In questo esempio si amplia quello precedente per personalizzare l'elemento ILoggerFactory fornito da WebApplicationBuilder. Aggiunge OpenTelemetry come provider di registrazione che trasmette i log tramite OTLP (protocollo OpenTelemetry):

var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();

Creare un ILoggerFactory con DI

Se stai usando un contenitore DI senza un host, usa AddLogging per configurare e aggiungi ILoggerFactory al contenitore.

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

L'esempio precedente:

  • È stato creato un contenitore dei servizi DI contenente un oggetto ILoggerFactory configurato per scrivere nella console
  • Aggiunto un singleton ExampleService al contenitore
  • È stata creata un'istanza di ExampleService dal contenitore DI, che ha creato automaticamente un ILogger<ExampleService> da usare come argomento del costruttore.
  • Invoked ExampleService.DoSomeWork which used the ILogger<ExampleService> to log a message to the console.

Configurare la registrazione

La configurazione della registrazione viene impostata nel codice o tramite origini esterne, ad esempio file di configurazione e variabili di ambiente. L'uso della configurazione esterna è utile quando possibile perché può essere modificato senza ricompilare l'applicazione. Tuttavia, alcune attività, ad esempio l'impostazione dei provider di registrazione, possono essere configurate solo dal codice.

Configurare la registrazione senza codice

Per le app che usano un host, la configurazione della registrazione viene in genere fornita dalla sezione "Logging" di file appsettings.{Environment}.json. Per le app che non usano un host, le origini di configurazione esterne vengono configurate in modo esplicito o configurate nel codice.

Di seguito il file appsettings. Development.json viene generato dai modelli di servizio .NET Worker:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Nel codice JSON precedente:

  • Vengono specificate le categorie "Default", "Microsoft" e "Microsoft.Hosting.Lifetime" livello di log.
  • Il valore "Default" viene applicato a tutte le categorie non specificate diversamente, rendendo effettivamente tutti i valori predefiniti per tutte le categorie "Information". È possibile eseguire l'override di questo comportamento specificando un valore per una categoria.
  • La categoria "Microsoft" si applica a tutte le categorie che iniziano con "Microsoft".
  • The "Microsoft" category logs at a log level of Warning and higher.
  • La categoria "Microsoft.Hosting.Lifetime" è più specifica della categoria "Microsoft", quindi la categoria "Microsoft.Hosting.Lifetime" registra a livello di log "Information" e livelli superiori.
  • Non viene specificato un provider di log specifico, quindi LogLevel si applica a tutti i provider di registrazione abilitati, ad eccezione del Registro eventi di Windows.

La proprietà Logging può avere le proprietà LogLevel e quelle del provider di log. LogLevel specifica il livello minimo per la registrazione per le categorie selezionate. Nel codice JSON precedente vengono specificati i livelli di log Information e Warning. LogLevel indica la gravità del log con valori compresi da 0 a 6:

Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5 e None = 6.

Quando si specifica un LogLevel, la registrazione viene abilitata per i messaggi al livello specificato e superiori. Nel JSON precedente, la categoria Default viene registrata per Information e superiori. Ad esempio, vengono registrati i messaggi Information, Warning, Error e Critical. Se non viene specificato un LogLevel, viene impostato il livello di log predefinito Information. Per altre informazioni, vedere Livelli di log.

Una proprietà del provider può specificare una proprietà LogLevel. LogLevel under a provider specifies levels to log for that provider, and overrides the non-provider log settings. Considerare il file appsettings.json seguente:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.Hosting": "Trace"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    }
}

Le impostazioni in Logging.{ProviderName}.LogLevel sostituiscono le impostazioni in Logging.LogLevel. Nel codice JSON precedente il livello di log predefinito del provider di Debug è impostato su Information:

Logging:Debug:LogLevel:Default:Information

L'impostazione precedente specifica il livello di log Information per ogni categoria Logging:Debug:, ad eccezione di Microsoft.Hosting. Quando viene elencata una categoria specifica, la categoria specifica sostituisce la categoria predefinita. Nel codice JSON precedente le categorie di Logging:Debug:LogLevel"Microsoft.Hosting" e "Default" ignorano le impostazioni in Logging:LogLevel

È possibile specificare il livello minimo di log per:

  • Specific providers: For example, Logging:EventSource:LogLevel:Default:Information
  • Categorie specifiche: ad esempio, Logging:LogLevel:Microsoft:Warning
  • Tutti i provider e tutte le categorie: Logging:LogLevel:Default:Warning

Any logs below the minimum level are not:

  • Passed to the provider.
  • Logged or displayed.

Per eliminare tutti i log, specificare LogLevel.None. Il valore di LogLevel.None è 6, che è maggiore di LogLevel.Critical (5).

If a provider supports log scopes, IncludeScopes indicates whether they're enabled. For more information, see log scopes.

Il file di appsettings.json seguente contiene le impostazioni per tutti i provider predefiniti:

{
    "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"
            }
        }
    }
}

Nell'esempio precedente:

  • Le categorie e i livelli non sono valori suggeriti. L'esempio viene fornito per visualizzare tutti i provider predefiniti.
  • Le impostazioni in Logging.{ProviderName}.LogLevel sostituiscono le impostazioni in Logging.LogLevel. Ad esempio, il livello in Debug.LogLevel.Default sostituisce il livello in LogLevel.Default.
  • Viene usato l'alias di ogni provider. Ogni provider definisce un alias che può essere utilizzato nella configurazione al posto del nome completo di tipo. Gli alias dei provider predefiniti sono:
    • Console
    • Debug
    • EventSource
    • EventLog
    • AzureAppServicesFile
    • AzureAppServicesBlob
    • ApplicationInsights

Impostare il livello di log tramite riga di comando, variabili di ambiente e altre configurazioni

Il livello di log può essere impostato da uno qualsiasi dei provider di configurazione. Ad esempio, è possibile creare una variabile di ambiente persistente denominata Logging:LogLevel:Microsoft con un valore di Information.

Creare e assegnare una variabile di ambiente persistente, in base al valore del livello di log.

:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M

In a new instance of the Command Prompt, read the environment variable.

:: Prints the env var value
echo %Logging__LogLevel__Microsoft%

L'impostazione dell'ambiente precedente è persistente nell'ambiente. Per testare le impostazioni quando si usa un'app creata con i modelli di servizio di lavoro .NET, usare il comando dotnet run nella directory del progetto dopo l'assegnazione della variabile di ambiente.

dotnet run

Suggerimento

Dopo aver impostato una variabile di ambiente, riavviare l'ambiente di sviluppo integrato (IDE) per assicurarsi che siano disponibili le variabili di ambiente appena aggiunte.

In Servizio app di Azure selezionare Nuova impostazione applicazione nella pagina Impostazioni > Configurazione. Le impostazioni applicazione del Servizio app di Azure sono:

  • Crittografate mentre sono inattive e trasmesse su un canale crittografato.
  • Exposed as environment variables.

Per altre informazioni sull'impostazione dei valori di configurazione di ASP.NET Core usando le variabili di ambiente, vedere Variabili di ambiente.

Configurare la registrazione con il codice

Per configurare l'accesso al codice, usare l'API ILoggingBuilder. È possibile accedervi da posizioni diverse:

This example shows setting the console logging provider and several filters.

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");

Nell'esempio precedente AddFilter viene usato per regolare il livello di log abilitato per varie categorie. AddConsole viene usato per aggiungere il provider di log della console. Per impostazione predefinita, i log con gravità Debug non sono abilitati, ma poiché la configurazione ha modificato i filtri, nella console viene visualizzato il messaggio di debug "Hello Everyone".

Applicazione delle regole di filtro

Quando viene creato un oggetto ILogger<TCategoryName>, l'oggetto ILoggerFactory seleziona una singola regola per ogni provider da applicare al logger. Tutti i messaggi scritti da un'istanza di ILogger vengono filtrati in base alle regole selezionate. Tra le regole disponibili viene selezionata la regola più specifica per ogni coppia di categoria e provider.

L'algoritmo seguente viene usato per ogni provider quando viene creato un ILogger per una determinata categoria:

  • Selezionare tutte le regole corrispondenti al provider o al relativo alias. If no match is found, select all rules with an empty provider.
  • Dal risultato del passaggio precedente, selezionare le regole con il prefisso di categoria corrispondente più lungo. Se non viene trovata alcuna corrispondenza, selezionare tutte le regole che non specificano una categoria.
  • Se sono selezionate più regole, scegliere l'ultima.
  • Se non sono selezionate regole, usare LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) per specificare il livello di registrazione minimo.

Categoria di log

Quando viene creato un oggetto ILogger, viene specificata una categoria. La categoria è inclusa in ogni messaggio di log creato da tale istanza di ILogger. La stringa di categoria è arbitraria, ma per convenzione si usa il nome completo e qualificato della classe. Ad esempio, in un'applicazione con un servizio definito come l'oggetto seguente, la categoria potrebbe essere "Example.DefaultService":

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger<DefaultService> _logger;

        public DefaultService(ILogger<DefaultService> logger) =>
            _logger = logger;

        // ...
    }
}

Se si vuole categorizzare ulteriormente, per convenzione si usa un nome gerarchico aggiungendo una sottocategoria al nome della classe completo e specificando in modo esplicito la categoria usando LoggerFactory.CreateLogger:

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger _logger;

        public DefaultService(ILoggerFactory loggerFactory) =>
            _logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");

        // ...
    }
}

La chiamata CreateLogger con un nome fisso può essere utile quando viene usata in più classi/tipi in modo che gli eventi possano essere organizzati per categoria.

Utilizzare ILogger<T> equivale a chiamare CreateLogger con il nome di tipo completamente qualificato di T.

Log level

Nella tabella seguente sono elencati i valori LogLevel, il metodo di estensione Log{LogLevel} pratico e l'utilizzo suggerito:

LogLevel Valore Method Descrizione
Trace 0 LogTrace Contengono i messaggi più dettagliati. Questi messaggi possono contenere dati sensibili dell'app. Questi messaggi sono disabilitati per impostazione predefinita e non devono essere abilitati in produzione.
Debug 1 LogDebug Per il debug e lo sviluppo. Utilizzare con cautela nell'ambiente di produzione a causa dell'elevato volume.
Informazioni 2 LogInformation Tenere traccia del flusso generale dell'app. Può avere valore a lungo termine.
Avvertenza 3 LogWarning Per gli eventi imprevisti o anomali. In genere include errori o condizioni che non causano l'esito negativo dell'app.
Errore 4 LogError For errors and exceptions that cannot be handled. Questi messaggi indicano un errore nell'operazione o nella richiesta corrente, non un errore a livello di app.
Critico 5 LogCritical Per gli errori che richiedono attenzione immediata. Esempi: scenari di perdita di dati, spazio su disco insufficiente.
Nessuno 6 Specifica che non deve essere scritto alcun messaggio.

Nella tabella precedente, LogLevel è elencato in ordine crescente di gravità.

Il primo parametro del metodo Log, LogLevel, indica la gravità del log. Anziché chiamare Log(LogLevel, ...), la maggior parte degli sviluppatori chiama i metodi di estensione Log{LogLevel}. I Log{LogLevel} metodi di estensione chiamano il metodo Log e specificano il LogLevel. Ad esempio, le due chiamate di registrazione seguenti sono equivalenti a livello funzionale e producono lo stesso log:

public void LogDetails()
{
    var logMessage = "Details for log.";

    _logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
    _logger.LogInformation(AppLogEvents.Details, logMessage);
}

AppLogEvents.Details è l'ID evento ed è rappresentato in modo implicito da un valore Int32 costante. AppLogEvents è una classe che espone varie costanti di identificatore denominato e viene visualizzata nella sezione ID evento log.

Il codice seguente crea i log Information e 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;
}

Nel codice precedente, il primo Log{LogLevel} parametro, AppLogEvents.Read, è l'ID evento Log. Il secondo parametro è un modello di messaggio con segnaposto per i valori degli argomenti forniti dai parametri dei metodi rimanenti. I parametri dei metodi sono descritti nella sezione relativa al modello di messaggio più avanti in questo articolo.

Configurare il livello di log appropriato e chiamare i metodi Log{LogLevel} corretti per controllare la quantità di output di log registrata su un determinato supporto di archiviazione. Ad esempio:

  • In produzione:
    • La registrazione ai livelli Trace o Debug produce un volume elevato di messaggi di log dettagliati. Per controllare i costi e non superare i limiti di archiviazione dei dati, registrare i messaggi di livello Trace e Debug in un archivio dati a basso costo per volumi elevati. Prendere in considerazione la limitazione di Trace e Debug a categorie specifiche.
    • La registrazione ai livelli da Warning a Critical dovrebbe produrre pochi messaggi di log.
      • I costi e i limiti di archiviazione in genere non sono un problema.
      • Pochi log consentono una maggiore flessibilità nelle scelte dell'archivio dati.
  • In fase di sviluppo:
    • Set to Warning.
    • Aggiungere messaggi Trace o Debug durante la risoluzione dei problemi. Per limitare l'output, impostare Trace o Debug solo per le categorie in fase di indagine.

Il seguente set JSON Logging:Console:LogLevel:Microsoft:Information:

{
    "Logging": {
        "LogLevel": {
            "Microsoft": "Warning"
        },
        "Console": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        }
    }
}

Log event ID

Ogni log può specificare un identificatore di evento, EventId è una struttura con Id e proprietà Name di sola lettura e facoltative. Il codice sorgente di esempio usa la classe AppLogEvents per definire gli ID evento:

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;

    // ...
}

Suggerimento

Per altre informazioni sulla conversione di un int in un EventId, vedere Operatore EventId.Implicit(Int32 in EventId).

Un ID evento associa un set di eventi. Ad esempio, tutti i log correlati alla lettura dei valori da un repository potrebbero essere 1001.

The logging provider may log the event ID in an ID field, in the logging message, or not at all. Il provider Debug non visualizza gli ID evento. The console provider shows event IDs in brackets after the category:

info: Example.DefaultService.GetAsync[1001]
      Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
      GetAsync(a1b2c3) not found

Alcuni provider di registrazione archiviano l'ID evento in un campo, che consente di filtrare le informazioni in base all'ID.

Modello di messaggio di registrazione

Ogni API di log specifica un modello di messaggio. Gli argomenti forniti possono essere inclusi nel modello di messaggio come segnaposto. Usare nomi per i segnaposto, non numeri. L'ordine dei segnaposto, non il loro nome, determina i parametri da usare per fornire i valori corrispondenti. Nel codice seguente i nomi dei parametri non sono in sequenza nel modello di messaggio:

string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

Il codice precedente crea un messaggio di log con i valori dei parametri in sequenza:

Parameter values: param1, param2

Nota

Be mindful when using multiple placeholders within a single message template, as they're ordinal-based. I nomi non sono usati per allineare gli argomenti ai segnaposto.

Questo approccio consente ai provider di registrazione di implementare la registrazione semantica o strutturata. Al sistema di registrazione vengono passati gli argomenti e non solo il modello di messaggio formattato. Ciò consente ai provider di registrazione di archiviare i valori dei parametri come campi. Consider the following logger method:

_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);

For example, when logging to Azure Table Storage:

  • Ogni entità Tabella di Azure può avere le proprietà ID e RunTime.
  • Le tabelle con proprietà semplificano le query sui dati registrati. Ad esempio una query può trovare tutti i log entro un determinato intervallo RunTime senza che sia necessario analizzare il tempo fuori dal messaggio di testo.

Formattazione del modello di messaggio di log

Log message templates support placeholder formatting. I modelli sono liberi di specificare qualsiasi formato valido per l'argomento di tipo dato. Si consideri ad esempio il modello di messaggio di logger Information seguente:

_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022

Nell'esempio precedente l'istanza DateTimeOffset è il tipo che corrisponde a PlaceHolderName nel modello di messaggio del logger. Questo nome può essere qualsiasi elemento perché i valori sono basati su ordinali. Il formato MMMM dd, yyyy è valido per il tipo DateTimeOffset.

Per altre informazioni sulla formattazione di DateTime e DateTimeOffset, vedere Stringhe di formato di data e ora personalizzate.

Examples

Negli esempi seguenti viene illustrato come formattare un modello di messaggio usando la sintassi del segnaposto {}. Additionally, an example of escaping the {} placeholder syntax is shown with its output. Finally, string interpolation with templating placeholders is also shown:

logger.LogInformation("Number: {Number}", 1);               // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3);           // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5);    // {Number}: 5

Suggerimento

  • Nella maggior parte dei casi, è consigliabile usare la formattazione del modello di messaggio di log durante la registrazione. L'uso dell'interpolazione di stringhe può causare problemi di prestazioni.
  • Regola di analisi del codice CA2254: il modello deve essere un'espressione statica consente di avvisare le posizioni in cui i messaggi di log non usano la formattazione corretta.

Registrare le eccezioni

The logger methods have overloads that take an exception parameter:

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

Exception logging is provider-specific.

Livello di log predefinito

Se il livello di log predefinito non è impostato, il valore predefinito del livello di log è Information.

Si consideri ad esempio l'app del servizio di lavoro seguente:

  • Creato con i modelli di lavoro .NET.
  • appsettings.json e appsettings.Development.json eliminato o rinominato.

Con la configurazione precedente, il passaggio alla pagina della privacy o alla home page produce molti messaggi Trace, Debug e Information con Microsoft nel nome della categoria.

Il codice seguente imposta il livello di log predefinito quando non è impostato nella configurazione:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

await host.RunAsync();

Funzione di filtro

Una funzione di filtro viene richiamata per tutti i provider e le categorie a cui non sono assegnate regole tramite la configurazione o il codice:

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();

Il codice precedente visualizza i log della console quando la categoria contiene Example o Microsoft e il livello di log è Information o superiore.

Ambiti dei log

Un ambito raggruppa un set di operazioni logiche. Questo raggruppamento può essere usato per collegare gli stessi dati a ogni log creato come parte di un set. Ad esempio, ogni log creato come parte dell'elaborazione di una transazione può includere l'ID transazione.

Un ambito:

I provider seguenti supportano gli ambiti:

Use a scope by wrapping logger calls in a using block:

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

The following JSON enables scopes for the console provider:

{
    "Logging": {
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Warning",
                "Default": "Information"
            }
        },
        "LogLevel": {
            "Default": "Debug"
        }
    }
}

The following code enables scopes for the console provider:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);

using IHost host = builder.Build();

await host.RunAsync();

Create logs in Main

Il codice seguente effettua il login in Main ottenendo un'istanza ILogger da DI dopo aver costruito l'host:

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();

Il codice precedente si basa su due pacchetti NuGet:

Il file di progetto sarà simile al seguente:

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

No asynchronous logger methods

La registrazione deve essere così rapida da non giustificare l'impatto sulle prestazioni del codice asincrono. If a logging datastore is slow, don't write to it directly. Valutare invece la possibilità di scrivere i messaggi di log prima in un archivio veloce e quindi spostarli nell'archivio lento in un secondo momento. Ad esempio, quando la registrazione viene eseguita in SQL Server, non farlo direttamente in un metodo Log, poiché i metodi Log sono sincroni. Al contrario, aggiungere i messaggi di log sincronamente a una coda in memoria e usare un processo in background per prelevare i messaggi dalla coda per eseguire le operazioni asincrone di invio dei dati a SQL Server.

Modificare i livelli di log in un'app in esecuzione

L'API di registrazione non include uno scenario per modificare i livelli di log mentre un'app è in esecuzione. However, some configuration providers are capable of reloading configuration, which takes immediate effect on logging configuration. For example, the File Configuration Provider reloads logging configuration by default. Se la configurazione viene modificata nel codice durante l'esecuzione di un'app, l'app può chiamare IConfigurationRoot.Reload per aggiornare la configurazione di registrazione dell'app.

Pacchetti NuGet

Le interfacce e le implementazioni ILogger<TCategoryName> e ILoggerFactory sono incluse nella maggior parte degli elementi SDK .NET come riferimento implicito al pacchetto. Sono disponibili anche in modo esplicito nei pacchetti NuGet seguenti quando non viene fatto alcun riferimento in modo implicito:

Per altre informazioni su quali elementi .NET SDK includano riferimenti impliciti ai pacchetti, vedere .NET SDK: tabella per lo spazio dei nomi implicito.

Vedi anche