Registros en .NET Core y ASP.NET Core

Por Kirk Larkin, Juergen Gutsch y Rick Anderson

.NET Core es compatible con una API de registro que funciona con una gran variedad de proveedores de registro integrados y de terceros. En este artículo se muestra cómo usar las API de registro con proveedores integrados.

La mayoría de los ejemplos de código que se muestran en este artículo son de aplicaciones de ASP.NET Core. Las partes específicas de registro de estos fragmentos de código son válidas para cualquier aplicación de .NET Core que use el host genérico. Las plantillas de aplicación Web de ASP.NET Core usan el host genérico.

Vea o descargue el código de ejemplo (cómo descargarlo)

Proveedores de registro

Los proveedores de registro almacenan los registros, a excepción del proveedor Console, que los muestra. Por ejemplo, el proveedor de Azure Application Insights almacena los registros en Azure Application Insights. Se pueden habilitar varios proveedores.

Las plantillas de aplicación web de ASP.NET Core predeterminadas:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

En el código anterior se muestra la clase Program creada con las plantillas de aplicación web de ASP.NET Core. En las siguientes secciones se proporcionan ejemplos basados en las plantillas de aplicación web de ASP.NET Core, que usan el host genérico. Las aplicaciones de consola que no son de host se explican más adelante en este documento.

Para invalidar el conjunto predeterminado de proveedores de registro agregados por Host.CreateDefaultBuilder, llame a ClearProviders y agregue los proveedores de registro necesarios. Por ejemplo, el código siguiente:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Para otros proveedores, vea:

Creación de registros

Para crear registros, use un objeto ILogger<TCategoryName> desde la inserción de dependencias (DI):

En el ejemplo siguiente:

  • Crea un registrador, ILogger<AboutModel>, que utiliza una categoría de registro del nombre completo del tipo AboutModel. La categoría de registro es una cadena que está asociada con cada registro.
  • Llama a LogInformation para realizar el registro en el nivel de Information. El nivel de registro indica la gravedad del evento registrado.
public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Los niveles y las categorías se explican con más detalle posteriormente en este artículo.

Para obtener información sobre Blazor, consulte Creación de registros en Blazor y Blazor WebAssembly en este documento.

Creación de registros en Main y Startup muestra cómo crear registros en Main y Startup.

registro

La configuración de registros suele proporcionarla la sección Logging de los archivos appsettings.{Environment} .json. El siguiente archivo appsettings.Development.json se genera mediante las plantillas de aplicación web de ASP.NET Core:

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

En el código JSON anterior:

  • Se especifican las categorías "Default", "Microsoft" y "Microsoft.Hosting.Lifetime".
  • La categoría "Microsoft" se aplica a todas las categorías que comienzan por "Microsoft". Por ejemplo, esta configuración se aplica a la categoría "Microsoft.AspNetCore.Routing.EndpointMiddleware".
  • La categoría "Microsoft" registra en el nivel de registro Warning y superiores.
  • La categoría "Microsoft.Hosting.Lifetime" es más específica que la categoría "Microsoft", por lo que la categoría "Microsoft.Hosting.Lifetime" registra en el nivel de registro "Information" y superiores.
  • No se especifica un proveedor de registro específico, por lo que LogLevel se aplica a todos los proveedores de registro habilitados, excepto Windows EventLog.

La propiedad Logging puede tener LogLevel y registrar propiedades del proveedor de registro. LogLevel especifica el nivel mínimo que se va a registrar para las categorías seleccionadas. En el código JSON anterior, se especifican los niveles de registro Information y Warning. LogLevel indica la gravedad del registro y los valores están entre 0 y 6:

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

Cuando se especifica LogLevel, el registro está habilitado para los mensajes tanto en el nivel especificado como en los superiores. En el código JSON anterior, se registra la categoría Default para Information y los niveles posteriores. Por ejemplo, se registran los mensajes Information, Warning, Error y Critical. Si no se especifica LogLevel, el nivel predeterminado del registro es Information. Para obtener más información, consulte Niveles de registro.

Una propiedad de proveedor puede especificar una propiedad de LogLevel. LogLevel en un proveedor especifica los niveles que se van a registrar para ese proveedor, e invalida la configuración de registro que no es de proveedor. Fíjese en el siguiente archivo appsettings.json:

{
  "Logging": {
    "LogLevel": { // All providers, LogLevel applies to all the enabled providers.
      "Default": "Error", // Default logging, Error and higher.
      "Microsoft": "Warning" // All Microsoft* categories, Warning and higher.
    },
    "Debug": { // Debug provider.
      "LogLevel": {
        "Default": "Information", // Overrides preceding LogLevel:Default setting.
        "Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
      }
    },
    "EventSource": { // EventSource provider
      "LogLevel": {
        "Default": "Warning" // All categories of EventSource provider.
      }
    }
  }
}

La configuración de Logging.{providername}.LogLevel invalida la configuración de Logging.LogLevel. En el código JSON anterior, el nivel de registro predeterminado del proveedor Debug se establece en Information:

Logging:Debug:LogLevel:Default:Information

La configuración anterior especifica el nivel de registro Information para cada categoría de Logging:Debug:, excepto Microsoft.Hosting. Cuando se muestra una categoría específica, esa categoría invalida la categoría predeterminada. En el JSON anterior, las categorías de Logging:Debug:LogLevel "Microsoft.Hosting" y "Default" invalidan la configuración de Logging:LogLevel

Se puede especificar el nivel de registro mínimo para:

  • Proveedores específicos: Por ejemplo, Logging:EventSource:LogLevel:Default:Information.
  • Categorías específicas: Por ejemplo, Logging:LogLevel:Microsoft:Warning.
  • Todos los proveedores y todas las categorías: Logging:LogLevel:Default:Warning

Los registros situados por debajo del nivel mínimo:

  • no se pasan proveedor;
  • no se registran ni se muestran.

Para suprimir todos los registros, especifique LogLevel.None. LogLevel.None tiene un valor de 6, que es mayor que LogLevel.Critical (5).

Si un proveedor admite ámbitos de registro, IncludeScopes indica si están habilitados. Para obtener más información, consulte Ámbitos de registro.

El siguiente archivo appSettings.json contiene todos los proveedores habilitados de forma predeterminada:

{
  "Logging": {
    "LogLevel": { // No provider, LogLevel applies to all the enabled providers.
      "Default": "Error",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Warning"
    },
    "Debug": { // Debug provider.
      "LogLevel": {
        "Default": "Information" // Overrides preceding LogLevel:Default setting.
      }
    },
    "Console": {
      "IncludeScopes": true,
      "LogLevel": {
        "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
        "Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
        "Microsoft.AspNetCore.Mvc.Razor": "Error",
        "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"
      }
    }
  }
}

En el ejemplo anterior:

  • Las categorías y los niveles no son valores sugeridos. El objetivo del ejemplo es mostrar todos los proveedores predeterminados.
  • La configuración de Logging.{providername}.LogLevel invalida la configuración de Logging.LogLevel. Por ejemplo, el nivel de Debug.LogLevel.Default invalida el nivel de LogLevel.Default.
  • Se usa cada alias de proveedor predeterminado. Cada proveedor define un alias que se puede utilizar en la configuración en lugar del nombre de tipo completo. Los alias de proveedores integrados son los siguientes:
    • Consola
    • Depuración
    • EventSource
    • EventLog
    • AzureAppServicesFile
    • AzureAppServicesBlob
    • ApplicationInsights

Establecimiento del nivel de registro mediante la línea de comandos, las variables de entorno y otra configuración

El nivel de registro se puede establecer con cualquiera de los proveedores de configuración.

El separador : no funciona con claves jerárquicas de variables de entorno en todas las plataformas. __, el carácter de subrayado doble, tiene las siguientes características:

  • Es compatible con todas las plataformas. Por ejemplo, el separador : no es compatible con Bash, pero __ sí.
  • Se reemplaza automáticamente por un signo :.

Los siguientes comandos:

  • establecen la clave de entorno Logging:LogLevel:Microsoft en un valor de Information en Windows;
  • prueban la configuración cuando use una aplicación creada con las plantillas de aplicación web de ASP.NET Core. El comando dotnet run debe ejecutarse en el directorio del proyecto después de usar set.
set Logging__LogLevel__Microsoft=Information
dotnet run

La configuración del entorno anterior:

  • solo se establece en procesos iniciados desde la ventana de comandos en la que se establecieron;
  • no podrá ser leída por los exploradores que se inician con Visual Studio.

El siguiente comando setx también establece la clave y el valor de entorno en Windows. A diferencia de set, la configuración de setx se conserva. El modificador /M establece la variable en el entorno del sistema. Si no se usa /M, se establece una variable de entorno de usuario.

setx Logging__LogLevel__Microsoft=Information /M

En Azure App Service, seleccione Nueva configuración de la aplicación en la página Configuración > Configuración. Los ajustes de configuración de Azure App Service:

  • Se cifran en reposo y se transmiten a través de un canal cifrado.
  • Se exponen como variables de entorno.

Para más información, consulte Aplicaciones de Azure: Invalidación de la configuración de la aplicación mediante Azure Portal.

Para obtener más información sobre cómo establecer los valores de configuración de ASP.NET Core mediante variables de entorno, vea Variables de entorno. Para obtener información sobre el uso de otros orígenes de configuración, como la línea de comandos, Azure Key Vault, Azure App Configuration, otros formatos de archivo, etc., vea Configuración en ASP.NET Core.

Cómo se aplican las reglas de filtro

Cuando se crea un objeto ILogger<TCategoryName>, el objeto ILoggerFactory selecciona una sola regla por proveedor para aplicar a ese registrador. Todos los mensajes escritos por una instancia ILogger se filtran según las reglas seleccionadas. De las reglas disponibles, se selecciona la más específica para cada par de categoría y proveedor.

Cuando se crea un ILogger para una categoría determinada, se usa el algoritmo siguiente para cada proveedor:

  • Se seleccionan todas las reglas que coinciden con el proveedor o su alias. Si no se encuentra ninguna coincidencia, se seleccionan todas las reglas con un proveedor vacío.
  • Del resultado del paso anterior, se seleccionan las reglas con el prefijo de categoría coincidente más largo. Si no se encuentra ninguna coincidencia, se seleccionan todas las reglas que no especifican una categoría.
  • Si se seleccionan varias reglas, se toma la última.
  • Si no se selecciona ninguna regla, se usa MinimumLevel.

Registro de la salida de dotnet run y Visual Studio

Se muestran los registros creados con los proveedores de registro predeterminados:

  • En Visual Studio
    • En la ventana de salida de Debug al depurar.
    • En la ventana de ASP.NET Core Web Server.
  • En la ventana de la consola cuando la aplicación se ejecuta con dotnet run.

Los registros que comienzan por categorías de "Microsoft" pertenecen al código de marco de ASP.NET Core. ASP.NET Core y el código de la aplicación usan la misma API y los mismos proveedores de registro.

Categoría de registro

Cuando se crea un objeto ILogger, se especifica una categoría. Esa categoría se incluye con cada mensaje de registro creado por esa instancia de ILogger. La cadena de categoría es arbitraria, pero la convención es usar el nombre de clase. Por ejemplo, en un controlador, el nombre podría ser "TodoApi.Controllers.TodoController". Las aplicaciones web de ASP.NET Core usan ILogger<T> para obtener automáticamente una instancia de ILogger que utiliza el nombre de tipo completo de T como la categoría:

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        _logger.LogInformation("GET Pages.PrivacyModel called.");
    }
}

Para especificar explícitamente la categoría, llame a ILoggerFactory.CreateLogger:

public class ContactModel : PageModel
{
    private readonly ILogger _logger;

    public ContactModel(ILoggerFactory logger)
    {
        _logger = logger.CreateLogger("MyCategory");
    }

    public void OnGet()
    {
        _logger.LogInformation("GET Pages.ContactModel called.");
    }

ILogger<T> es equivale a llamar a CreateLogger con el nombre de tipo completo de T.

Nivel de registro

En la tabla siguiente se enumeran los valores de LogLevel, el método de extensión Log{LogLevel} oportuno y el uso sugerido:

LogLevel Valor Método Descripción
Seguimiento 0 LogTrace Contienen los mensajes más detallados. Estos mensajes pueden contener datos confidenciales de la aplicación. Están deshabilitados de forma predeterminada y no se deben habilitar en un entorno de producción.
Depurar 1 LogDebug Para depuración y desarrollo. Debido al elevado volumen, tenga precaución cuando lo use en producción.
Información 2 LogInformation Realiza el seguimiento del flujo general de la aplicación. Puede tener un valor a largo plazo.
Advertencia 3 LogWarning Para eventos anómalos o inesperados. Normalmente incluye errores o estados que no provocan un error en la aplicación.
Error 4 LogError Para los errores y excepciones que no se pueden controlar. Estos mensajes indican un error en la operación o solicitud actual, no un error de toda la aplicación.
Critical) (Crítico) 5 LogCritical Para los errores que requieren atención inmediata. Ejemplos: escenarios de pérdida de datos, espacio en disco insuficiente.
Ninguno 6 Especifica que una categoría de registro no debe escribir ningún mensaje.

En la tabla anterior, LogLevel aparece de menor a mayor gravedad.

El primer parámetro del método Log, LogLevel, indica la gravedad del registro. En lugar de llamar a Log(LogLevel, ...), la mayoría de los desarrolladores llaman a los métodos de extensión Log{LogLevel}. Los métodos de extensión Log{LogLevel} llaman al método Log y especifican el LogLevel. Por ejemplo, las dos llamadas de registro siguientes son funcionalmente equivalentes y generan el mismo registro:

[HttpGet]
public IActionResult Test1(int id)
{
    var routeInfo = ControllerContext.ToCtxString(id);

    _logger.Log(LogLevel.Information, MyLogEvents.TestItem, routeInfo);
    _logger.LogInformation(MyLogEvents.TestItem, routeInfo);

    return ControllerContext.MyDisplayRouteInfo();
}

MyLogEvents.TestItem es el identificador del evento. MyLogEvents forma parte de la aplicación de ejemplo y se muestra en la sección Log event ID (id. de evento de registro).

El paquete NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y ToCtxString. Los métodos muestran la información de ruta de Controller.

El siguiente código crea los registros Information y Warning:

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    _logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        _logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

En el código anterior, el primer parámetro de Log{LogLevel}, MyLogEvents.GetItem, es el identificador de evento de registro. El segundo parámetro es una plantilla de mensaje con marcadores de posición para los valores de argumento proporcionados por el resto de parámetros de método. Los parámetros de método se explican detalladamente en la sección Plantilla de mensaje más adelante en este documento.

Llame al método Log{LogLevel} apropiado para controlar la cantidad de salida de registro que se escribe en un determinado medio de almacenamiento. Por ejemplo:

  • En producción:
    • El registro en los niveles Trace o Information genera un gran volumen de mensajes de registro detallados. Para controlar los costos y no superar los límites de almacenamiento de datos, registre los mensajes de nivel Trace a Information en un almacén de datos de alto volumen y bajo costo. Considere la posibilidad de limitar Trace y Information a categorías específicas.
    • El registro entre los niveles Warning y Critical debe generar pocos mensajes de registro.
      • Los costos y los límites de almacenamiento no suelen ser un problema.
      • Cuantos menos registros haya, mayor será la flexibilidad a la hora de elegir el almacén de datos.
  • En desarrollo:
    • Establézcalo en Warning.
    • Agregue los mensajes Trace oInformation al solucionar problemas. Para limitar la salida, establezca Trace o Information solo para las categorías que se están investigando.

ASP.NET Core escribe registros de eventos de marco. Por ejemplo, tomemos la salida del registro de:

  • Una aplicación de Razor Pages creada con las plantillas de ASP.NET Core.
  • Registro establecido en Logging:Console:LogLevel:Microsoft:Information.
  • Navegación a la página Privacy (privacidad):
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 GET https://localhost:5001/Privacy
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint '/Privacy'
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3]
      Route matched with {page = "/Privacy"}. Executing page /Privacy
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[101]
      Executing handler method DefaultRP.Pages.PrivacyModel.OnGet - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]
      Executed handler method OnGet, returned result .
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[103]
      Executing an implicit handler method - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[104]
      Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4]
      Executed page /Privacy in 74.5188ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/Privacy'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 149.3023ms 200 text/html; charset=utf-8

El siguiente JSON establece Logging:Console:LogLevel:Microsoft:Information:

{
  "Logging": {      // Default, all providers.
    "LogLevel": {
      "Microsoft": "Warning"
    },
    "Console": { // Console provider.
      "LogLevel": {
        "Microsoft": "Information"
      }
    }
  }
}

Id. de evento del registro

Cada registro se puede especificar un id. de evento. La aplicación de ejemplo usa la clase MyLogEvents para definir los identificadores de evento:

public class MyLogEvents
{
    public const int GenerateItems = 1000;
    public const int ListItems     = 1001;
    public const int GetItem       = 1002;
    public const int InsertItem    = 1003;
    public const int UpdateItem    = 1004;
    public const int DeleteItem    = 1005;

    public const int TestItem      = 3000;

    public const int GetItemNotFound    = 4000;
    public const int UpdateItemNotFound = 4001;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    _logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        _logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

Un id. de evento asocia un conjunto de eventos. Por ejemplo, todos los registros relacionados con la presentación de una lista de elementos en una página podrían ser 1001.

El proveedor de registro puede almacenar el id. de evento en un campo de identificador, en el mensaje de registro o no almacenarlo. El proveedor de depuración no muestra los identificadores de evento. El proveedor de consola muestra los identificadores de evento entre corchetes después de la categoría:

info: TodoApi.Controllers.TodoItemsController[1002]
      Getting item 1
warn: TodoApi.Controllers.TodoItemsController[4000]
      Get(1) NOT FOUND

Algunos proveedores de registro almacenan el identificador de evento en un campo, lo que permite filtrar por el id.

Plantilla de mensaje de registro

Cada API de registro usa una plantilla de mensaje. La plantilla de mensaje puede contener marcadores de posición para los que se proporcionan argumentos. Use los nombres de los marcadores de posición, no números.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    _logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        _logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

El orden de los marcadores de posición, no sus nombres, determina qué parámetros se usan para proporcionar sus valores. En el código siguiente, los nombres de parámetro están fuera de la secuencia en la plantilla de mensaje:

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

El código anterior crea un mensaje de registro con los valores de parámetro en secuencia:

Parameter values: param1, param2

Este enfoque permite a los proveedores de registro implementar registro semántico o estructurado. Los propios argumentos se pasan al sistema de registro, no solo a la plantilla de mensaje con formato. Esto permite a los proveedores de registro almacenar los valores de parámetro como campos. Por ejemplo, tomemos el siguiente método de registrador:

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

Por ejemplo, al registrar en Azure Table Storage:

  • Cada entidad de Azure Table puede tener propiedades ID y RequestTime.
  • Las tablas con propiedades simplifican las consultas en los datos registrados. Por ejemplo, una consulta puede buscar todos los registros dentro de un intervalo RequestTime determinado sin necesidad de analizar el tiempo de espera del mensaje de texto.

Registro de excepciones

Los métodos de registrador tienen sobrecargas que toman un parámetro de excepción:

[HttpGet("{id}")]
public IActionResult TestExp(int id)
{
    var routeInfo = ControllerContext.ToCtxString(id);
    _logger.LogInformation(MyLogEvents.TestItem, routeInfo);

    try
    {
        if (id == 3)
        {
            throw new Exception("Test exception");
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(MyLogEvents.GetItemNotFound, ex, "TestExp({Id})", id);
        return NotFound();
    }

    return ControllerContext.MyDisplayRouteInfo();
}

El paquete NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y ToCtxString. Los métodos muestran la información de ruta de Controller.

El registro de excepciones es específico del proveedor.

Nivel de registro predeterminado

Si no se establece el nivel de registro predeterminado, su valor será Information.

Por ejemplo, considere la siguiente aplicación web:

  • Creada con las plantillas de aplicación web de ASP.NET.
  • appsettings.json y appsettings.Development.json eliminados o con el nombre cambiado.

Con la configuración anterior, al navegar a la página de privacidad o de inicio, se generan muchos mensajes de Trace, Debug y Information con Microsoft en el nombre de la categoría.

El código siguiente establece el nivel de registro predeterminado cuando este no se establece en la configuración:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning))
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

En general, los niveles de registro se deben especificar en la configuración y no en el código.

Función de filtro

Se invoca una función de filtro para todos los proveedores y las categorías que no tienen reglas asignadas mediante configuración o código:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                logging.AddFilter((provider, category, logLevel) =>
                {
                    if (provider.Contains("ConsoleLoggerProvider")
                        && category.Contains("Controller")
                        && logLevel >= LogLevel.Information)
                    {
                        return true;
                    }
                    else if (provider.Contains("ConsoleLoggerProvider")
                        && category.Contains("Microsoft")
                        && logLevel >= LogLevel.Information)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                });
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

El código anterior muestra los registros de la consola cuando la categoría contiene Controller o Microsoft y el nivel de registro es Information o superior.

En general, los niveles de registro se deben especificar en la configuración y no en el código.

Categorías ASP.NET Core y EF Core

La tabla siguiente conti9ene algunas categorías usadas por ASP.NET Core y Entity Framework Core, con notas sobre los registros:

Categoría Notas
Microsoft.AspNetCore Diagnósticos generales de ASP.NET Core.
Microsoft.AspNetCore.DataProtection Qué claves se tuvieron en cuenta, encontraron y usaron.
Microsoft.AspNetCore.HostFiltering Hosts permitidos.
Microsoft.AspNetCore.Hosting Cuánto tiempo tardaron en completarse las solicitudes HTTP y a qué hora comenzaron. Qué ensamblados de inicio de hospedaje se cargaron.
Microsoft.AspNetCore.Mvc Diagnósticos de MVC y Razor. Enlace de modelos, ejecución de filtros, compilación de vistas y selección de acciones.
Microsoft.AspNetCore.Routing Información de coincidencia de ruta.
Microsoft.AspNetCore.Server Inicio y detención de conexión y mantener las respuestas activas. Información de certificado HTTPS.
Microsoft.AspNetCore.StaticFiles Archivos servidos.
Microsoft.EntityFrameworkCore Diagnósticos generales de Entity Framework Core. Actividad y la configuración de bases de datos, detección de cambios y migraciones.

Para ver más categorías en la ventana de la consola, establezca appsettings.Development.json en lo siguiente:

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

Ámbitos de registro

Un ámbito puede agrupar un conjunto de operaciones lógicas. Esta agrupación se puede utilizar para adjuntar los mismos datos para cada registro que se crea como parte de un conjunto. Por ejemplo, cada registro creado como parte del procesamiento de una transacción puede incluir el identificador de dicha transacción.

Un ámbito:

Los siguientes proveedores admiten ámbitos:

Use un ámbito encapsulando las llamadas de registrador en un bloque using:

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    TodoItem todoItem;

    using (_logger.BeginScope("using block message"))
    {
        _logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

        todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            _logger.LogWarning(MyLogEvents.GetItemNotFound, 
                "Get({Id}) NOT FOUND", id);
            return NotFound();
        }
    }

    return ItemToDTO(todoItem);
}

El JSON siguiente habilita ámbitos para el proveedor de la consola:

{
  "Logging": {
    "Debug": {
      "LogLevel": {
        "Default": "Information"
      }
    },
    "Console": {
      "IncludeScopes": true, // Required to use Scopes.
      "LogLevel": {
        "Microsoft": "Warning",
        "Default": "Information"
      }
    },
    "LogLevel": {
      "Default": "Debug"
    }
  }
}

El código siguiente permite ámbitos para el proveedor de la consola:

En general, el registro se debe especificar en la configuración y no en el código.

Proveedores de registro integrados

ASP.NET Core incluye los siguientes proveedores de registro:

Para obtener información sobre stdout y el registro de depuración con el módulo ASP.NET Core, consulte Solución de problemas de ASP.NET Core en Azure App Service e IIS y Módulo ASP.NET Core.

Consola

El proveedor Console registra la salida en la consola. Para obtener más información sobre cómo ver los registros de Console en desarrollo, consulte Registro de la salida de dotnet run y Visual Studio.

Depuración

El proveedor Debug escribe la salida del registro mediante la clase System.Diagnostics.Debug. Las llamadas a System.Diagnostics.Debug.WriteLine escriben en el proveedor Debug.

En Linux, la ubicación del registro del proveedor Debug depende de la distribución y puede ser una de las siguientes:

  • /var/log/message
  • /var/log/syslog

Origen de eventos

El proveedor EventSource escribe en un origen de eventos multiplataforma con el nombre Microsoft-Extensions-Logging. En Windows, el proveedor utiliza ETW.

herramienta de seguimiento de dotnet

La herramienta de seguimiento de dotnet es una herramienta global de CLI multiplataforma que permite la recopilación de seguimientos de .NET Core de un proceso en ejecución. La herramienta recopila datos del proveedor Microsoft.Extensions.Logging.EventSource mediante un LoggingEventSource.

Vea dotnet-trace para obtener instrucciones de instalación.

Use la herramienta de seguimiento de dotnet para recopilar un seguimiento de una aplicación:

  1. Ejecute la aplicación con el comando dotnet run.

  2. Determine el identificador del proceso (PID) de la aplicación .NET Core:

    Busque el PID del proceso que tenga el mismo nombre que el ensamblado de la aplicación.

  3. Ejecute el comando dotnet trace.

    Sintaxis general del comando:

    dotnet trace collect -p {PID} 
        --providers Microsoft-Extensions-Logging:{Keyword}:{Event Level}
            :FilterSpecs=\"
                {Logger Category 1}:{Event Level 1};
                {Logger Category 2}:{Event Level 2};
                ...
                {Logger Category N}:{Event Level N}\"
    

    Al usar un shell de comandos de PowerShell, incluya el valor --providers entre comillas simples ('):

    dotnet trace collect -p {PID} 
        --providers 'Microsoft-Extensions-Logging:{Keyword}:{Event Level}
            :FilterSpecs=\"
                {Logger Category 1}:{Event Level 1};
                {Logger Category 2}:{Event Level 2};
                ...
                {Logger Category N}:{Event Level N}\"'
    

    En plataformas que no sean Windows, agregue la opción -f speedscope para cambiar el formato del archivo de seguimiento de salida a speedscope.

    Palabra clave Descripción
    1 Registre los eventos meta sobre el elemento LoggingEventSource. No registre eventos de ILogger).
    2 Activa el evento Message cuando se llama a ILogger.Log(). Proporciona la información mediante programación (sin formato).
    4 Activa el evento FormatMessage cuando se llama a ILogger.Log(). Proporciona la versión de cadena con formato de la información.
    8 Activa el evento MessageJson cuando se llama a ILogger.Log(). Proporciona una representación JSON de los argumentos.
    Nivel de evento Descripción
    0 LogAlways
    1 Critical
    2 Error
    3 Warning
    4 Informational
    5 Verbose

    Las entradas FilterSpecs de {Logger Category} y {Event Level} representan condiciones de filtrado de registros adicionales. Separe las entradas FilterSpecs con un punto y coma (;).

    Ejemplo de uso de un shell de comandos de Windows (no hay comillas simples alrededor del valor --providers):

    dotnet trace collect -p {PID} --providers Microsoft-Extensions-Logging:4:2:FilterSpecs=\"Microsoft.AspNetCore.Hosting*:4\"
    

    El comando anterior activa lo siguiente:

    • Registrador de origen del evento para generar cadenas con formato (4) de los errores (2).
    • Registro Microsoft.AspNetCore.Hosting en el nivel de registro Informational (4).
  4. Presione la tecla Entrar o Ctrl + C para detener las herramientas de seguimiento de dotnet.

    El seguimiento se guarda con el nombre trace.nettrace en la carpeta en la que se ejecuta el comando dotnet trace.

  5. Abra el seguimiento con Perfview. Abra el archivo trace.nettrace y explore los eventos de seguimiento.

Si la aplicación no compila el host con CreateDefaultBuilder, agregue el proveedor de origen del evento a la configuración de registro de la aplicación.

Para obtener más información, consulte:

Perfview

Use la utilidad PerfView para recopilar y ver los registros. Hay otras herramientas para ver los registros ETW, pero PerfView proporciona la mejor experiencia para trabajar con los eventos ETW emitidos por ASP.NET Core.

Para configurar PerfView para la recopilación de eventos registrados por este proveedor, agregue la cadena *Microsoft-Extensions-Logging a la lista Proveedores adicionales. No olvide el símbolo * al principio de la cadena.

Registro de eventos de Windows

El proveedor EventLog envía la salida del registro al Registro de eventos de Windows. A diferencia de otros proveedores, el proveedor EventLog no hereda la configuración de no proveedor predeterminada. Si no se especifican valores de registro de EventLog, el valor predeterminado será LogLevel.Warning.

Para registrar eventos inferiores a LogLevel.Warning, establezca el nivel de registro de forma explícita. En el ejemplo siguiente se establece el nivel de registro predeterminado del registro de eventos en LogLevel.Information:

"Logging": {
  "EventLog": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

Las sobrecargas de AddEventLog pueden pasar EventLogSettings. Si es null o no se especifica, se usa la siguiente configuración predeterminada:

  • LogName: "Application"
  • SourceName: ".NET Runtime"
  • MachineName: se usa el nombre del equipo local.

En el código siguiente se cambia el valor predeterminado de SourceName (".NET Runtime") por MyLogs:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                logging.AddEventLog(eventLogSettings =>
                {
                    eventLogSettings.SourceName = "MyLogs"; 
                });
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Azure App Service

El paquete de proveedor Microsoft.Extensions.Logging.AzureAppServices escribe los registros en archivos de texto en el sistema de archivos de una aplicación de Azure App Service y en Blob Storage en una cuenta de Azure Storage.

El paquete del proveedor no se incluye en el marco compartido. Para usar el proveedor, agregue el paquete del proveedor al proyecto.

Para configurar las opciones de proveedor, use AzureFileLoggerOptions y AzureBlobLoggerOptions, tal y como se muestra en el ejemplo siguiente:

public class Scopes
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
                .ConfigureServices(serviceCollection => serviceCollection
                    .Configure<AzureFileLoggerOptions>(options =>
                    {
                        options.FileName = "azure-diagnostics-";
                        options.FileSizeLimit = 50 * 1024;
                        options.RetainedFileCountLimit = 5;
                    })
                    .Configure<AzureBlobLoggerOptions>(options =>
                    {
                        options.BlobName = "log.txt";
                    }))
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Cuando se implementa en Azure App Service, la aplicación usa la configuración de la sección Registros de App Service de la página App Service de Azure Portal. Cuando se actualiza la configuración siguiente, los cambios se aplican de inmediato sin necesidad de reiniciar ni de volver a implementar la aplicación.

  • Registro de la aplicación (sistema de archivos)
  • Registro de la aplicación (blob)

La ubicación predeterminada de los archivos de registro es la carpeta D:\home\LogFiles\Application y el nombre de archivo predeterminado es diagnostics-aaaammdd.txt. El límite de tamaño de archivo predeterminado es 10 MB, y el número máximo predeterminado de archivos que se conservan es 2. El nombre de blob predeterminado es {nombre-de-la-aplicación}{marca de tiempo}/aaaa/mm/dd/hh/{guid}-applicationLog.txt.

El proveedor solo realiza registros cuando el proyecto se ejecuta en el entorno de Azure.

Secuencias de registro de Azure

El streaming de registro de Azure permiten ver la actividad de registro en tiempo real desde:

  • El servidor de aplicaciones
  • El servidor web
  • Error del seguimiento de solicitudes

Para configurar las secuencias de registro de Azure:

  • Desplácese a la página Registros de App Service desde la página del portal de la aplicación.
  • Establezca Registro de la aplicación (sistema de archivos) en Activado.
  • Elija el Nivel de registro. Esta configuración solo se aplica al streaming de registro de Azure.

Desplácese a la página Secuencia de registro para ver los registros. Los mensajes que se registran lo hacen con la interfaz ILogger.

Azure Application Insights

El paquete de proveedor Microsoft.Extensions.Logging.ApplicationInsights escribe los registros en Azure Application Insights. Application Insights es un servicio que supervisa una aplicación web y proporciona herramientas para consultar y analizar los datos de telemetría. Si usa este proveedor, puede consultar y analizar los registros mediante las herramientas de Application Insights.

El proveedor de registro se incluye como dependencia de Microsoft.ApplicationInsights.AspNetCore, que es el paquete que proporciona toda la telemetría disponible para ASP.NET Core. Si usa este paquete, no tiene que instalar el proveedor de paquete.

El paquete Microsoft.ApplicationInsights.Web es para ASP.NET 4.x, no para ASP.NET Core.

Para obtener más información, vea los siguientes recursos:

Proveedores de registro de terceros

Plataformas de registro de terceros que funcionan con ASP.NET Core:

Algunas plataformas de terceros pueden realizar registro semántico, también conocido como registro estructurado.

El uso de una plataforma de terceros es similar al uso de uno de los proveedores integrados:

  1. Agregue un paquete NuGet al proyecto.
  2. Llame a un método de extensión ILoggerFactory proporcionado por el marco de registro.

Para más información, vea la documentación de cada proveedor. Microsoft no admite los proveedores de registro de terceros.

Aplicación de consola que no es de host

Para ver un ejemplo de cómo usar el host genérico en una aplicación de consola que no sea web, vea el archivo Program.cs de la aplicación de ejemplo de tareas en segundo plano (Tareas en segundo plano con servicios hospedados en ASP.NET Core).

El código de registro para las aplicaciones sin un host genérico es distinto en la forma en que se agregan los proveedores y se crean los registradores.

Proveedores de registro

En una aplicación de consola que no sea de host, llame al método de extensión Add{provider name} del proveedor al crear un elemento LoggerFactory:

class Program
{
    static void Main(string[] args)
    {
        using var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder
                .AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
                .AddConsole()
                .AddEventLog();
        });
        ILogger logger = loggerFactory.CreateLogger<Program>();
        logger.LogInformation("Example log message");
    }
}

Creación de registros

Para crear registros, use un objeto de ILogger<TCategoryName>. Use LoggerFactory para crear ILogger.

En el ejemplo siguiente, se crea un registrador con LoggingConsoleApp.Program como la categoría.

class Program
{
    static void Main(string[] args)
    {
        using var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder
                .AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
                .AddConsole()
                .AddEventLog();
        });
        ILogger logger = loggerFactory.CreateLogger<Program>();
        logger.LogInformation("Example log message");
    }
}

En los siguientes ejemplos de ASP.NET Core, el registrador se usa para crear registros con nivel de Information. El nivel de registro indica la gravedad del evento registrado.

class Program
{
    static void Main(string[] args)
    {
        using var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder
                .AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
                .AddConsole()
                .AddEventLog();
        });
        ILogger logger = loggerFactory.CreateLogger<Program>();
        logger.LogInformation("Example log message");
    }
}

Los niveles y las categorías se explican con más detalle en este documento.

Registro durante la construcción del host

No se admite directamente el registro durante la construcción del host. Sin embargo, se puede usar un registrador independiente. En el ejemplo siguiente, se usa un registrador Serilog para registrarse en CreateHostBuilder. AddSerilog usa la configuración estática especificada en Log.Logger:

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args)
    {
        var builtConfig = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddCommandLine(args)
            .Build();

        Log.Logger = new LoggerConfiguration()
            .WriteTo.Console()
            .WriteTo.File(builtConfig["Logging:FilePath"])
            .CreateLogger();

        try
        {
            return Host.CreateDefaultBuilder(args)
                .ConfigureServices((context, services) =>
                {
                    services.AddRazorPages();
                })
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.AddConfiguration(builtConfig);
                })
                .ConfigureLogging(logging =>
                {   
                    logging.AddSerilog();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host builder error");

            throw;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

Configuración de un servicio que dependa de ILogger

La inserción del constructor de un registrador en Startup funciona en versiones anteriores de ASP.NET Core, ya que se crea un contenedor de inserción de dependencias independiente para el host de web. Para conocer por qué solo se crea un contenedor para el host genérico, vea el anuncio de cambios importantes.

Para configurar un servicio que dependa de ILogger<T>, use la inserción de constructores o proporcione un método Factory. Usar un Factory Method es la opción recomendada si no tiene otra alternativa. Por ejemplo, tomemos un servicio que necesita una instancia de ILogger<T> proporcionada por DI:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddRazorPages();

    services.AddSingleton<IMyService>((container) =>
    {
        var logger = container.GetRequiredService<ILogger<MyService>>();
        return new MyService() { Logger = logger };
    });
}

El código resaltado anterior es un elemento Func que se ejecuta la primera vez que el contenedor de inserción de dependencias necesita crear una instancia de MyService. Puede acceder a cualquiera de los servicios registrados de esta forma.

Creación de registros en Main

El código siguiente registra en Main mediante la obtención de una instancia de ILogger de inserción de dependencias después de compilar el host:

public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Host created.");

    host.Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Creación de registros durante el inicio

El código siguiente escribe registros en Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
                      ILogger<Startup> logger)
{
    if (env.IsDevelopment())
    {
        logger.LogInformation("In Development.");
        app.UseDeveloperExceptionPage();
    }
    else
    {
        logger.LogInformation("Not Development.");
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
    });
}

No se admite la escritura de registros antes de la finalización del contenedor de inserción de dependencias configurado en el método Startup.ConfigureServices:

  • No se admite la inyección del registrador en el constructor Startup.
  • No se admite la inyección del registrador en la signatura del método Startup.ConfigureServices.

El motivo de esta restricción es que los registros dependen de la inserción de dependencias y de la configuración, que a su vez depende de la inserción de dependencias. El contenedor de inserción de dependencias no se configura hasta que finaliza ConfigureServices.

Para obtener información sobre la configuración de un servicio que depende de ILogger<T> o sobre el motivo de que la inserción de constructor de un registrador en Startup funcionase en versiones anteriores, vea Configuración de un servicio que depende de ILogger

No hay métodos de registrador asincrónicos

El registro debe ser tan rápido que no merezca la pena el costo de rendimiento del código asincrónico. Si el almacén de datos de registro es lento, no escriba directamente en él. Considere la posibilidad de escribir primero los mensajes de registro en un almacén rápido y, después, moverlos al almacén lento. Por ejemplo, al iniciar sesión en SQL Server, no lo haga directamente en un método Log, ya que los métodos Log son sincrónicos. En su lugar, agregue sincrónicamente mensajes de registro a una cola en memoria y haga que un trabajo en segundo plano extraiga los mensajes de la cola para realizar el trabajo asincrónico de insertar datos en SQL Server. Para obtener más información, vea este problema de GitHub.

Cambio de los niveles de registro en una aplicación en ejecución

La API de registro no incluye un escenario que permita cambiar los niveles de registro mientras se ejecuta una aplicación. No obstante, algunos proveedores de configuración pueden volver a cargar la configuración, lo que tiene efecto inmediato en la configuración del registro. Por ejemplo, el Proveedor de configuración de archivo vuelve a cargar la configuración de registro de forma predeterminada. Si se cambia la configuración en el código mientras se ejecuta una aplicación, la aplicación puede llamar a IConfigurationRoot.Reload para actualizar la configuración de registro de la aplicación.

ILogger e ILoggerFactory

Las implementaciones y las interfaces de ILogger<TCategoryName> y ILoggerFactory se incluyen en el SDK de .NET Core. También están disponibles en los siguientes paquetes NuGet:

Aplicación de reglas de filtro en el código

El método preferido para establecer las reglas de filtro de registro es mediante la Configuración.

En el siguiente ejemplo se muestra cómo registrar reglas de filtro en el código:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
               logging.AddFilter("System", LogLevel.Debug)
                  .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information)
                  .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace))
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

logging.AddFilter("System", LogLevel.Debug) especifica la categoría System y el nivel de registro Debug. El filtro se aplica a todos los proveedores porque no se ha configurado un proveedor específico.

AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) especifica:

  • El proveedor de registro Debug.
  • Nivel de registro Information y superiores.
  • Todas las categorías que empiezan con "Microsoft".

Creación de un registrador personalizado

Para agregar un registrador personalizado, agregue ILoggerProvider con ILoggerFactory:

public void Configure(
    IApplicationBuilder app,
    IWebHostEnvironment env,
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddProvider(new CustomLoggerProvider(new CustomLoggerConfiguration()));

ILoggerProvider crea una o más instancias de ILogger. El marco de trabajo usa las instancias de ILogger para registrar la información.

Configuración de registrador personalizado de ejemplo

Ejemplo:

  • Está diseñado para ser un ejemplo muy básico que establece el color de la consola de registro por el identificador de evento y el nivel de registro. Normalmente, los registradores no cambian por identificador de evento y no son específicos del nivel de registro.
  • Crea diferentes entradas de consola de color por nivel de registro e identificador de evento con el siguiente tipo de configuración:
public class ColorConsoleLoggerConfiguration
{
    public LogLevel LogLevel { get; set; } = LogLevel.Warning;
    public int EventId { get; set; } = 0;
    public ConsoleColor Color { get; set; } = ConsoleColor.Yellow;
}

El código anterior establece el nivel predeterminado en Warning y el color en Yellow. Si EventId se establece en 0, se registrarán todos los eventos.

Creación del registrador personalizado

El nombre de la categoría de implementación ILogger es normalmente el origen del registro. Por ejemplo, el tipo en el que se crea el registrador:

public class ColorConsoleLogger : ILogger
{
    private readonly string _name;
    private readonly ColorConsoleLoggerConfiguration _config;

    public ColorConsoleLogger(string name, ColorConsoleLoggerConfiguration config)
    {
        _name = name;
        _config = config;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel == _config.LogLevel;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, 
                        Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        if (_config.EventId == 0 || _config.EventId == eventId.Id)
        {
            var color = Console.ForegroundColor;
            Console.ForegroundColor = _config.Color;
            Console.WriteLine($"{logLevel} - {eventId.Id} " +
                              $"- {_name} - {formatter(state, exception)}");
            Console.ForegroundColor = color;
        }
    }
}

El código anterior:

  • Crea una instancia del registrador por nombre de categoría.
  • Comprueba logLevel == _config.LogLevel en IsEnabled, por lo que cada logLevel tiene un registrador único. Por lo general, los registradores también se deben habilitar para todos los niveles de registro superiores:
public bool IsEnabled(LogLevel logLevel)
{
    return logLevel >= _config.LogLevel;
}

Creación del proveedor LoggerProvider personalizado

LoggerProvider es la clase que crea las instancias del registrador. Tal vez no sea necesario crear una instancia del registrador por categoría, pero esto tiene sentido para algunos registradores, como NLog o log4net. Al hacerlo, también se podrán elegir diferentes destinos de salida de registro por categoría, en caso necesario:

public class ColorConsoleLoggerProvider : ILoggerProvider
{
    private readonly ColorConsoleLoggerConfiguration _config;
    private readonly ConcurrentDictionary<string, ColorConsoleLogger> _loggers = new ConcurrentDictionary<string, ColorConsoleLogger>();

    public ColorConsoleLoggerProvider(ColorConsoleLoggerConfiguration config)
    {
        _config = config;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName, name => new ColorConsoleLogger(name, _config));
    }

    public void Dispose()
    {
        _loggers.Clear();
    }
}

En el código anterior, CreateLogger crea una única instancia de ColorConsoleLogger por nombre de categoría y la almacena en ConcurrentDictionary<TKey,TValue>.

Uso y registro del registrador personalizado

Registre el registrador en Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
                      ILoggerFactory loggerFactory)
{
    // Default registration.
    loggerFactory.AddProvider(new ColorConsoleLoggerProvider(
                              new ColorConsoleLoggerConfiguration
    {
        LogLevel = LogLevel.Error,
        Color = ConsoleColor.Red
    }));

    // Custom registration with default values.
    loggerFactory.AddColorConsoleLogger();

    // Custom registration with a new configuration instance.
    loggerFactory.AddColorConsoleLogger(new ColorConsoleLoggerConfiguration
    {
        LogLevel = LogLevel.Debug,
        Color = ConsoleColor.Gray
    });

    // Custom registration with a configuration object.
    loggerFactory.AddColorConsoleLogger(c =>
    {
        c.LogLevel = LogLevel.Information;
        c.Color = ConsoleColor.Blue;
    });

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Para el código anterior, proporcione al menos un método de extensión para ILoggerFactory:

public static class ColorConsoleLoggerExtensions
{
    public static ILoggerFactory AddColorConsoleLogger(
                                      this ILoggerFactory loggerFactory, 
                                      ColorConsoleLoggerConfiguration config)
    {
        loggerFactory.AddProvider(new ColorConsoleLoggerProvider(config));
        return loggerFactory;
    }
    public static ILoggerFactory AddColorConsoleLogger(
                                      this ILoggerFactory loggerFactory)
    {
        var config = new ColorConsoleLoggerConfiguration();
        return loggerFactory.AddColorConsoleLogger(config);
    }
    public static ILoggerFactory AddColorConsoleLogger(
                                    this ILoggerFactory loggerFactory, 
                                    Action<ColorConsoleLoggerConfiguration> configure)
    {
        var config = new ColorConsoleLoggerConfiguration();
        configure(config);
        return loggerFactory.AddColorConsoleLogger(config);
    }
}

Recursos adicionales

Por Tom Dykstra y Steve Smith

.NET Core es compatible con una API de registro que funciona con una gran variedad de proveedores de registro integrados y de terceros. En este artículo se muestra cómo usar las API de registro con proveedores integrados.

Vea o descargue el código de ejemplo (cómo descargarlo)

Incorporación de proveedores

Un proveedor de registro muestra o almacena registros. Por ejemplo, el proveedor de consola muestra los registros en la consola y el proveedor de Azure Application Insights los almacena en Azure Application Insights. Los registros se pueden enviar a varios destinos mediante la incorporación de varios proveedores.

Para usar un proveedor, llame al método de extensión Add{provider name} del proveedor en Program.cs:

public static void Main(string[] args)
{
    var webHost = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            var env = hostingContext.HostingEnvironment;
            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", 
                      optional: true, reloadOnChange: true);
            config.AddEnvironmentVariables();
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            // Requires `using Microsoft.Extensions.Logging;`
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
            logging.AddEventSourceLogger();
        })
        .UseStartup<Startup>()
        .Build();

    webHost.Run();
}

El código anterior requiere referencias a Microsoft.Extensions.Logging y Microsoft.Extensions.Configuration.

La plantilla de proyecto predeterminada llama a CreateDefaultBuilder, que agrega los siguientes proveedores de registro:

  • Consola
  • Depuración
  • EventSource (a partir de ASP.NET Core 2.2)
public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();

Si usa CreateDefaultBuilder, puede reemplazar los proveedores predeterminados por sus propios valores. Llame a ClearProviders y agregue los proveedores que desee.

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });

    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Seeded the database.");

    host.Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        });

Obtenga más información sobre los proveedores de registro integrados y los proveedores de registro de terceros más adelante en el artículo.

Creación de registros

Para crear registros, use un objeto de ILogger<TCategoryName>. En una aplicación web o servicio hospedado, obtenga un elemento ILogger de la inserción de dependencias. En aplicaciones de consola que no sean de host, use LoggerFactory para crear un elemento ILogger.

En el ejemplo siguiente de ASP.NET Core, se crea un registrador con TodoApiSample.Pages.AboutModel como la categoría. La categoría de registro es una cadena que está asociada con cada registro. La instancia de ILogger<T> proporcionada por la inserción de dependencias genera registros que tienen el nombre completo del tipo T como la categoría.

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }

En los siguientes ejemplos de aplicación de consola y ASP.NET Core, el registrador se usa para crear registros con Information como el nivel. El nivel de registro indica la gravedad del evento registrado.

public void OnGet()
{
    Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
    _logger.LogInformation("Message displayed: {Message}", Message);
}

Los niveles y las categorías se explican detalladamente más adelante en este artículo.

Creación de registros durante el inicio

Para escribir registros en la clase Startup, incluya un parámetro ILogger en la signatura de construcción:

public class Startup
{
    private readonly ILogger _logger;

    public Startup(IConfiguration configuration, ILogger<Startup> logger)
    {
        Configuration = configuration;
        _logger = logger;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

        // Add our repository type
        services.AddSingleton<ITodoRepository, TodoRepository>();
        _logger.LogInformation("Added TodoRepository to services");
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            _logger.LogInformation("In Development environment");
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc();
    }
}

Crear registros en la clase del programa

Para escribir registros la clase Program, obtenga una instancia ILogger de inserción de dependencias:

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });

    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Seeded the database.");

    host.Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        });

No se admite directamente el registro durante la construcción del host. Sin embargo, se puede usar un registrador independiente. En el ejemplo siguiente, se usa un registrador Serilog para registrarse en CreateWebHostBuilder. AddSerilog usa la configuración estática especificada en Log.Logger:

using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        var builtConfig = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddCommandLine(args)
            .Build();

        Log.Logger = new LoggerConfiguration()
            .WriteTo.Console()
            .WriteTo.File(builtConfig["Logging:FilePath"])
            .CreateLogger();

        try
        {
            return WebHost.CreateDefaultBuilder(args)
                .ConfigureServices((context, services) =>
                {
                    services.AddMvc();
                })
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.AddConfiguration(builtConfig);
                })
                .ConfigureLogging(logging =>
                {
                    logging.AddSerilog();
                })
                .UseStartup<Startup>();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host builder error");

            throw;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

No hay métodos de registrador asincrónicos

El registro debe ser tan rápido que no merezca la pena el costo de rendimiento del código asincrónico. Si el almacén de datos de registro es lento, no escriba directamente en él. Considere la posibilidad de escribir los mensajes de registro en un almacén rápido inicialmente y luego moverlos a la tienda lenta. Por ejemplo, si inicia sesión en SQL Server, no desea hacerlo directamente en un método Log, ya que los métodos Log son sincrónicos. En su lugar, agregue sincrónicamente mensajes de registro a una cola en memoria y haga que un trabajo en segundo plano extraiga los mensajes de la cola para realizar el trabajo asincrónico de insertar datos en SQL Server. Para obtener más información, vea este problema de GitHub.

Configuración

Uno o varios proveedores de configuración proporcionan la configuración del proveedor de registro:

  • Formatos de archivo (INI, JSON y XML).
  • Argumentos de la línea de comandos.
  • Variables de entorno.
  • Objetos de .NET en memoria.
  • El almacenamiento de administrador secreto sin cifrar.
  • Un almacén de usuario cifrado, como Azure Key Vault.
  • Proveedores personalizados (instalados o creados).

Por ejemplo, la sección Logging de archivos de configuración de aplicación suele proporcionar la configuración de registro. En el ejemplo siguiente se muestra el contenido de un archivo appsettings.Development.json típico:

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

La propiedad Logging puede tener LogLevel y propiedades del proveedor de registro (se muestra la consola).

La propiedad LogLevel bajo Logging especifica el nivel mínimo que se va a registrar para las categorías seleccionadas. En el ejemplo, las categorías System y Microsoft se registran en el nivel Information y todas las demás se registran en el nivel Debug.

Otras propiedades bajo Logging especifican proveedores de registro. El ejemplo es para el proveedor de consola. Si un proveedor admite ámbitos de registro, IncludeScopes indica si están habilitados. Una propiedad de proveedor (como Console en el ejemplo) también puede especificar una propiedad LogLevel. LogLevel en un proveedor especifica niveles de registro para ese proveedor.

Si los niveles se especifican en Logging.{providername}.LogLevel, invalidan todo lo establecido en Logging.LogLevel. Por ejemplo, tomemos el siguiente código JSON:

{
  "Logging": {      // Default, all providers.
    "LogLevel": {
      "Microsoft": "Warning"
    },
    "Console": { // Console provider.
      "LogLevel": {
        "Microsoft": "Information"
      }
    }
  }
}

En el JSON anterior, la configuración del proveedor Console invalida el nivel de registro anterior (predeterminado).

La API de registro no incluye un escenario que permita cambiar los niveles de registro mientras se ejecuta una aplicación. No obstante, algunos proveedores de configuración pueden volver a cargar la configuración, lo que tiene efecto inmediato en la configuración del registro. Por ejemplo, FileConfigurationProvider —agregado por CreateDefaultBuilder para leer los archivos de configuración— vuelve a cargar la configuración de registro de forma predeterminada. Si se cambia la configuración en el código mientras se ejecuta una aplicación, la aplicación puede llamar a IConfigurationRoot.Reload para actualizar la configuración de registro de la aplicación.

Para obtener información sobre cómo implementar proveedores de configuración, consulte Configuración en ASP.NET Core.

Salida de registro de ejemplo

Con el código de ejemplo que se muestra en la sección anterior, los registros aparecen en la consola cuando la aplicación se ejecuta desde la línea de comandos. Este es un ejemplo de salida de la consola:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/todo/0
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
info: TodoApi.Controllers.TodoController[1002]
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      GetById(0) NOT FOUND
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 404
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 42.9286ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 148.889ms 404

Los registros anteriores se generaron mediante la realización de una solicitud HTTP GET a la aplicación de ejemplo en http://localhost:5000/api/todo/0.

Este es un ejemplo de los mismos registros tal y como aparecen en la ventana de depuración cuando se ejecuta la aplicación de ejemplo en Visual Studio:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:53104/api/todo/0  
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
TodoApi.Controllers.TodoController:Information: Getting item 0
TodoApi.Controllers.TodoController:Warning: GetById(0) NOT FOUND
Microsoft.AspNetCore.Mvc.StatusCodeResult:Information: Executing HttpStatusCodeResult, setting HTTP status code 404
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 152.5657ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 316.3195ms 404

Los registros creados por las llamadas de ILogger se muestran en la sección anterior, empezando por “TodoApi”. Los registros que comienzan por categorías de "Microsoft" son del código de marco de ASP.NET Core. ASP.NET Core y el código de la aplicación usan la misma API y los mismos proveedores de registro.

En el resto de este artículo se explican algunos detalles y opciones para el registro.

Paquetes NuGet

Las interfaces ILogger e ILoggerFactory se encuentran en Microsoft.Extensions.Logging.Abstractions, y sus implementaciones predeterminadas en Microsoft.Extensions.Logging.

Categoría de registro

Cuando se crea un objeto ILogger, se ha especificado una categoría para él. Esa categoría se incluye con cada mensaje de registro creado por esa instancia de ILogger. La categoría puede ser cualquier cadena, pero la convención es usar el nombre de clase, como "TodoApi.Controllers.TodoController".

Use ILogger<T> para obtener una instancia ILogger que utiliza el nombre de tipo completo de T como la categoría:

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILogger<TodoController> logger)
    {
        _todoRepository = todoRepository;
        _logger = logger;
    }

Para especificar explícitamente la categoría, llame a ILoggerFactory.CreateLogger:

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILoggerFactory logger)
    {
        _todoRepository = todoRepository;
        _logger = logger.CreateLogger("TodoApiSample.Controllers.TodoController");
    }

ILogger<T> es equivale a llamar a CreateLogger con el nombre de tipo completo de T.

Nivel de registro

Cara registro especifica un valor LogLevel. El nivel de registro indica la gravedad o importancia. Por ejemplo, podría escribir un registro Information cuando un método termina con normalidad y un registro Warning cuando un método devuelve un código de estado 404 No encontrado.

El siguiente código crea los registros Information y Warning:

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {Id}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({Id}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}

En el código anterior, los parámetros MyLogEvents.GetItem y MyLogEvents.GetItemNotFound son el id. de evento de registro. El segundo parámetro es una plantilla de mensaje con marcadores de posición para los valores de argumento proporcionados por el resto de parámetros de método. Los parámetros de método se explican detalladamente en la sección Plantilla de mensaje de registro en este artículo.

Los métodos de registro que incluyen el nivel en el nombre del método (por ejemplo LogInformation y LogWarning) son métodos de extensión para ILogger. Estos métodos llaman a un método Log que toma un parámetro LogLevel. Puede llamar directamente al método Log en lugar de a uno de estos métodos de extensión, pero la sintaxis es relativamente complicada. Para más información, vea la ILogger y el código fuente de las extensiones de registrador.

ASP.NET Core define los niveles de registro siguientes, que aquí se ordenan de menor a mayor gravedad.

  • Seguimiento = 0

    Para información que normalmente solo es útil para la depuración. Estos mensajes pueden contener datos confidenciales de la aplicación, por lo que no deben habilitarse en un entorno de producción. Deshabilitado de forma predeterminada.

  • Depurar = 1

    Para información que puede ser útil para el desarrollo y la depuración. Ejemplo: Entering method Configure with flag set to true. Habilite los registros de nivel Debug en producción cuando esté solucionando un problema, debido al elevado volumen de registros.

  • Información = 2

    Para realizar el seguimiento del flujo general de la aplicación. Estos registros suelen tener algún valor a largo plazo. Ejemplo: Request received for path /api/todo

  • Advertencia = 3

    Para los eventos anómalos o inesperados en el flujo de la aplicación. Estos pueden incluir errores u otras condiciones que no hacen que la aplicación se detenga, pero que puede que sea necesario investigar. Las excepciones controladas son un lugar común para usar el nivel de registro Warning. Ejemplo: FileNotFoundException for file quotes.txt.

  • Error = 4

    Para los errores y excepciones que no se pueden controlar. Estos mensajes indican un error en la actividad u operación actual (por ejemplo, la solicitud HTTP actual), no un error de toda la aplicación. Mensaje de registro de ejemplo: Cannot insert record due to duplicate key violation.

  • Crítico = 5

    Para los errores que requieren atención inmediata. Ejemplos: escenarios de pérdida de datos, espacio en disco insuficiente.

Use el nivel de registro para controlar la cantidad de salida del registro que se escribe en un medio de almacenamiento determinado o ventana de presentación. Por ejemplo:

  • En producción:
    • El registro en los niveles Trace a Information genera un gran volumen de mensajes de registro detallados. Para controlar los costos y no superar los límites de almacenamiento de datos, registre los mensajes de nivel Trace a Information en un almacén de datos de alto volumen y bajo costo.
    • El registro en los niveles Warning a Critical normalmente produce menos mensajes de registro y de menor tamaño. Por lo tanto, los costos y los límites de almacenamiento no suelen ser un problema, lo que da lugar a una mayor flexibilidad a la hora de elegir el almacén de datos.
  • Durante el desarrollo:
    • Registre los mensajes Warning a Critical en la consola.
    • Agregue los mensajes Trace a Information al solucionar problemas.

En la sección Filtrado del registro de este artículo se explica cómo controlar los niveles de registro que controla un proveedor.

ASP.NET Core escribe registros de eventos de marco. En los ejemplos de registro anteriores de este artículo se excluyeron los registros por debajo del nivel Information, por lo que no se crearon los registros de nivel Debug o Trace. Este es un ejemplo de registros de consola generados mediante la ejecución de la aplicación de ejemplo configurada para mostrar registros Debug:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:62555/api/todo/0
dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1]
      Request successfully matched the route with name 'GetTodo' and template 'api/Todo/{id}'.
dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2]
      Action 'TodoApi.Controllers.TodoController.Update (TodoApi)' with id '089d59b6-92ec-472d-b552-cc613dfd625d' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint'
dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2]
      Action 'TodoApi.Controllers.TodoController.Delete (TodoApi)' with id 'f3476abe-4bd9-4ad3-9261-3ead09607366' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint'
dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action TodoApi.Controllers.TodoController.GetById (TodoApi)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
info: TodoApi.Controllers.TodoController[1002]
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      GetById(0) NOT FOUND
dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action method TodoApi.Controllers.TodoController.GetById (TodoApi), returned result Microsoft.AspNetCore.Mvc.NotFoundResult.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 404
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 0.8788ms
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
      Connection id "0HL6L7NEFF2QD" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 2.7286ms 404

Id. de evento del registro

Cada registro se puede especificar un id. de evento. La aplicación de ejemplo lo hace mediante una clase LoggingEvents definida de forma local:

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {Id}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({Id}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}
public class LoggingEvents
{
    public const int GenerateItems = 1000;
    public const int ListItems = 1001;
    public const int GetItem = 1002;
    public const int InsertItem = 1003;
    public const int UpdateItem = 1004;
    public const int DeleteItem = 1005;

    public const int GetItemNotFound = 4000;
    public const int UpdateItemNotFound = 4001;
}

Un id. de evento asocia un conjunto de eventos. Por ejemplo, todos los registros relacionados con la presentación de una lista de elementos en una página podrían ser 1001.

El proveedor de registro puede almacenar el id. de evento en un campo de identificador, en el mensaje de registro o no almacenarlo. El proveedor de depuración no muestra los identificadores de evento. El proveedor de consola muestra los identificadores de evento entre corchetes después de la categoría:

info: TodoApi.Controllers.TodoController[1002]
      Getting item invalidid
warn: TodoApi.Controllers.TodoController[4000]
      GetById(invalidid) NOT FOUND

Plantilla de mensaje de registro

Cada registro especifica una plantilla de mensaje. La plantilla de mensaje puede contener marcadores de posición para los que se proporcionan argumentos. Use los nombres de los marcadores de posición, no números.

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {Id}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({Id}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}

El orden de los marcadores de posición, no sus nombres, determina qué parámetros se usan para proporcionar sus valores. En el código siguiente, tenga en cuenta que los nombres de parámetro están fuera de la secuencia en la plantilla de mensaje:

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

Este código crea un mensaje de registro con los valores de parámetro en secuencia:

Parameter values: parm1, parm2

La plataforma de registro funciona de esta manera para que los proveedores de registro puedan implementar el registro semántico, también conocido como registro estructurado. Los propios argumentos se pasan al sistema de registro, no solo a la plantilla de mensaje con formato. Esta información permite a los proveedores de registro almacenar los valores de parámetro como campos. Por ejemplo, suponga que las llamadas del método del registrador tiene el aspecto siguiente:

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

Si envía los registros a Azure Table Storage, cada entidad de Azure Table puede tener propiedades ID y RequestTime, lo que simplifica las consultas en los datos de registro. Una consulta puede buscar todos los registros dentro de un intervalo RequestTime determinado sin analizar el tiempo de espera del mensaje de texto.

Excepciones de registro

Los métodos de registrador tienen sobrecargas que le permiten pasar una excepción, como en el ejemplo siguiente:

catch (Exception ex)
{
    _logger.LogWarning(LoggingEvents.GetItemNotFound, ex, "GetById({Id}) NOT FOUND", id);
    return NotFound();
}
return new ObjectResult(item);

Cada proveedor controla la información de la excepción de maneras diferentes. Este es un ejemplo de salida del proveedor de depuración del código mostrado anteriormente.

TodoApiSample.Controllers.TodoController: Warning: GetById(55) NOT FOUND

System.Exception: Item not found exception.
   at TodoApiSample.Controllers.TodoController.GetById(String id) in C:\TodoApiSample\Controllers\TodoController.cs:line 226

Filtrado del registro

Puede especificar un nivel de registro mínimo para un proveedor y una categoría específicos, o para todos los proveedores o todas las categorías. Los registros por debajo del nivel mínimo no se pasan a ese proveedor, de modo que no se muestran o almacenan.

Para suprimir todos los registros, especifique LogLevel.None como el nivel de registro mínimo. El valor entero de LogLevel.None es 6, que es superior a LogLevel.Critical (5).

Creación de reglas de filtro en la configuración

El código de la plantilla de proyecto llama a CreateDefaultBuilder para configurar el registro para los proveedores de Console, Debug y EventSource (ASP.NET Core 2.2 o versiones posteriores). El método CreateDefaultBuilder configura el registro para buscar la configuración en una sección de Logging, como se explica anteriormente en este artículo.

Los datos de configuración especifican niveles de registro mínimo por proveedor y categoría, como en el ejemplo siguiente:

{
  "Logging": {
    "Debug": {
      "LogLevel": {
        "Default": "Information"
      }
    },
    "Console": {
      "IncludeScopes": false,
      "LogLevel": {
        "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
        "Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
        "Microsoft.AspNetCore.Mvc.Razor": "Error",
        "Default": "Information"
      }
    },
    "LogLevel": {
      "Default": "Debug"
    }
  }
}

Este archivo JSON crea seis reglas de filtro, una para el proveedor de depuración, cuatro para el proveedor de la consola y una para todos los proveedores. Se elige una sola regla para cada proveedor cuando se crea un objeto ILogger.

Reglas de filtro en el código

En el siguiente ejemplo se muestra cómo registrar reglas de filtro en el código:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logging =>
        logging.AddFilter("System", LogLevel.Debug)
               .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace));

El segundo AddFilter especifica el proveedor de depuración mediante su nombre de tipo. El primer AddFilter se aplica a todos los proveedores, dado que no especifica un tipo de proveedor.

Cómo se aplican las reglas de filtro

Los datos de configuración y el código de AddFilter que se muestran en los ejemplos anteriores crean las reglas que se muestran en la tabla siguiente. Las seis primeras proceden del ejemplo de configuración y las dos últimas del ejemplo de código.

número Proveedor Categorías que comienzan por... Nivel de registro mínimo
1 Depuración Todas las categorías Información
2 Consola Microsoft.AspNetCore.Mvc.Razor.Internal Advertencia
3 Consola Microsoft.AspNetCore.Mvc.Razor.Razor Depuración
4 Consola Microsoft.AspNetCore.Mvc.Razor Error
5 Consola Todas las categorías Información
6 Todos los proveedores Todas las categorías Depuración
7 Todos los proveedores Sistema Depuración
8 Depuración Microsoft Seguimiento

Cuando se crea un objeto ILogger, el objeto ILoggerFactory selecciona una sola regla por proveedor para aplicar a ese registrador. Todos los mensajes escritos por una instancia ILogger se filtran según las reglas seleccionadas. De las reglas disponibles se selecciona la más específica posible para cada par de categoría y proveedor.

Cuando se crea un ILogger para una categoría determinada, se usa el algoritmo siguiente para cada proveedor:

  • Se seleccionan todas las reglas que coinciden con el proveedor o su alias. Si no se encuentra ninguna coincidencia, se seleccionan todas las reglas con un proveedor vacío.
  • Del resultado del paso anterior, se seleccionan las reglas con el prefijo de categoría coincidente más largo. Si no se encuentra ninguna coincidencia, se seleccionan todas las reglas que no especifican una categoría.
  • Si se seleccionan varias reglas, se toma la última.
  • Si no se selecciona ninguna regla, se usa MinimumLevel.

Con la lista de reglas anterior, supongamos que crea un objeto ILogger para la categoría "Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine":

  • Para el proveedor de depuración, se aplican las reglas 1, 6 y 8. La regla 8 es la más específica, por lo que se selecciona.
  • Para el proveedor de la consola, se aplican las reglas 3, 4, 5 y 6. La regla 3 es la más específica.

La instancia ILogger resultante envía los registros de nivel Trace y superiores al proveedor de depuración. Los registros de nivel Debug y superiores se envían al proveedor de consola.

Alias de proveedor

Cada proveedor define un alias que se puede utilizar en la configuración en lugar del nombre de tipo completo. Para los proveedores integrados, use los alias siguientes:

  • Consola
  • Depuración
  • EventSource
  • EventLog
  • TraceSource
  • AzureAppServicesFile
  • AzureAppServicesBlob
  • ApplicationInsights

Nivel mínimo predeterminado

Hay una configuración de nivel mínimo que solo tiene efecto si no se aplica ninguna regla de configuración o código para un proveedor y una categoría determinados. En el ejemplo siguiente se muestra cómo establecer el nivel mínimo:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning));

Si no establece explícitamente el nivel mínimo, el valor predeterminado es Information, lo que significa que los registros Trace y Debug se omiten.

Funciones de filtro

Se invoca una función de filtro para todos los proveedores y categorías que no tienen reglas asignadas mediante configuración o código. El código de la función tiene acceso al tipo de proveedor, la categoría y el nivel de registro. Por ejemplo:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logBuilder =>
    {
        logBuilder.AddFilter((provider, category, logLevel) =>
        {
            if (provider == "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider" &&
                category == "TodoApiSample.Controllers.TodoController")
            {
                return false;
            }
            return true;
        });
    });

Niveles y categorías del sistema

Estas son algunas categorías que ASP.NET Core y Entity Framework Core usan, con notas sobre lo que los registros de espera de ellas:

Categoría Notas
Microsoft.AspNetCore Diagnósticos generales de ASP.NET Core.
Microsoft.AspNetCore.DataProtection Qué claves se tuvieron en cuenta, encontraron y usaron.
Microsoft.AspNetCore.HostFiltering Hosts permitidos.
Microsoft.AspNetCore.Hosting Cuánto tiempo tardaron en completarse las solicitudes HTTP y a qué hora comenzaron. Qué ensamblados de inicio de hospedaje se cargaron.
Microsoft.AspNetCore.Mvc Diagnósticos de MVC y Razor. Enlace de modelos, ejecución de filtros, compilación de vistas y selección de acciones.
Microsoft.AspNetCore.Routing Información de coincidencia de ruta.
Microsoft.AspNetCore.Server Inicio y detención de conexión y mantener las respuestas activas. Información de certificado HTTPS.
Microsoft.AspNetCore.StaticFiles Archivos servidos.
Microsoft.EntityFrameworkCore Diagnósticos generales de Entity Framework Core. Actividad y la configuración de bases de datos, detección de cambios y migraciones.

Ámbitos de registro

Un ámbito puede agrupar un conjunto de operaciones lógicas. Esta agrupación se puede utilizar para adjuntar los mismos datos para cada registro que se crea como parte de un conjunto. Por ejemplo, cada registro creado como parte del procesamiento de una transacción puede incluir el identificador de dicha transacción.

Un ámbito es un tipo IDisposable devuelto por el método BeginScope y se conserva hasta que se elimina. Use un ámbito encapsulando las llamadas de registrador en un bloque using:

public IActionResult GetById(string id)
{
    TodoItem item;
    using (_logger.BeginScope("Message attached to logs created in the using block"))
    {
        _logger.LogInformation(LoggingEvents.GetItem, "Getting item {Id}", id);
        item = _todoRepository.Find(id);
        if (item == null)
        {
            _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({Id}) NOT FOUND", id);
            return NotFound();
        }
    }
    return new ObjectResult(item);
}

El código siguiente permite ámbitos para el proveedor de la consola:

Program.cs:

.ConfigureLogging((hostingContext, logging) =>
{
    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole(options => options.IncludeScopes = true);
    logging.AddDebug();
})

Nota

Es necesario configurar la opción del registrador de consola IncludeScopes para habilitar el registro basado en el ámbito.

Para obtener información sobre la configuración, consulte la sección Configuración.

Cada mensaje de registro incluye la información de ámbito:

info: TodoApiSample.Controllers.TodoController[1002]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApiSample.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      Getting item 0
warn: TodoApiSample.Controllers.TodoController[4000]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApiSample.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      GetById(0) NOT FOUND

Proveedores de registro integrados

ASP.NET Core incluye los proveedores siguientes:

Para obtener información sobre stdout y el registro de depuración con el módulo ASP.NET Core, consulte Solución de problemas de ASP.NET Core en Azure App Service e IIS y Módulo ASP.NET Core.

Proveedor de la consola

El paquete de proveedor Microsoft.Extensions.Logging.Console envía la salida del registro a la consola.

logging.AddConsole();

Para ver una salida de registro de la consola, abra un símbolo del sistema en la carpeta del proyecto y ejecute este comando:

dotnet run

Proveedor de depuración

El paquete de proveedor Microsoft.Extensions.Logging.Debug escribe la salida del registro mediante la clase System.Diagnostics.Debug (llamadas a métodos Debug.WriteLine).

En Linux, este proveedor escribe registros en /var/log/message.

logging.AddDebug();

Proveedor de origen del evento

El paquete del proveedor Microsoft.Extensions.Logging.EventSource escribe en una multiplataforma de origen del evento con el nombre Microsoft-Extensions-Logging. En Windows, el proveedor utiliza ETW.

logging.AddEventSourceLogger();

El proveedor de origen del evento se agrega automáticamente cuando se llama a CreateDefaultBuilder para compilar el host.

Use la utilidad PerfView para recopilar y ver los registros. Hay otras herramientas para ver los registros ETW, pero PerfView proporciona la mejor experiencia para trabajar con los eventos ETW emitidos por ASP.NET Core.

Para configurar PerfView para la recopilación de eventos registrados por este proveedor, agregue la cadena *Microsoft-Extensions-Logging a la lista Proveedores adicionales. (No olvide el asterisco al principio de la cadena).

Proveedores adicionales de Perfview

Proveedor EventLog de Windows

El paquete de proveedor Microsoft.Extensions.Logging.EventLog envía la salida del registro al Registro de eventos de Windows.

logging.AddEventLog();

Las sobrecargas de AddEventLog permiten pasar EventLogSettings. Si es null o no se especifica, se usa la siguiente configuración predeterminada:

  • LogName: "Application"
  • SourceName: ".NET Runtime"
  • MachineName: se usa el nombre del equipo local.

Los eventos se registran para el Nivel de advertencia y superior. En el ejemplo siguiente se establece el nivel de registro predeterminado del Registro de eventos en LogLevel.Information:

"Logging": {
  "EventLog": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

Proveedor TraceSource

El paquete de proveedor Microsoft.Extensions.Logging.TraceSource usa las bibliotecas y proveedores de TraceSource.

logging.AddTraceSource(sourceSwitchName);

Las sobrecargas de AddTraceSource permiten pasar un modificador de origen y un agente de escucha de seguimiento.

Para usar este proveedor, una aplicación debe ejecutarse en .NET Framework (en lugar de .NET Core). El proveedor puede enrutar mensajes a una variedad de agentes de escucha, como TextWriterTraceListener que se usa en la aplicación de ejemplo.

Proveedor Azure App Service

El paquete de proveedor Microsoft.Extensions.Logging.AzureAppServices escribe los registros en archivos de texto en el sistema de archivos de una aplicación de Azure App Service y en Blob Storage en una cuenta de Azure Storage.

logging.AddAzureWebAppDiagnostics();

El paquete de proveedor no está incluido en el metapaquete Microsoft.AspNetCore.App. Si el destino es .NET Framework o hace referencia al metapaquete Microsoft.AspNetCore.App, agregue el paquete del proveedor al proyecto.

Una sobrecarga AddAzureWebAppDiagnostics permite pasar AzureAppServicesDiagnosticsSettings. El objeto de configuración puede invalidar la configuración predeterminada, como la plantilla de salida de registro, el nombre de blob y el límite de tamaño de archivo. (La plantilla salida es una plantilla de mensaje que se aplica a todos los registros además de que se proporciona con una llamada de método ILogger).

Al realizar una implementación en una aplicación de App Service, esta respeta la configuración de la sección Registros de App Service de la página App Service de Azure Portal. Cuando se actualiza la configuración siguiente, los cambios se aplican de inmediato sin necesidad de reiniciar ni de volver a implementar la aplicación.

  • Registro de la aplicación (sistema de archivos)
  • Registro de la aplicación (blob)

La ubicación predeterminada de los archivos de registro es la carpeta D:\home\LogFiles\Application y el nombre de archivo predeterminado es diagnostics-aaaammdd.txt. El límite de tamaño de archivo predeterminado es 10 MB, y el número máximo predeterminado de archivos que se conservan es 2. El nombre de blob predeterminado es {nombre-de-la-aplicación}{marca de tiempo}/aaaa/mm/dd/hh/{guid}-applicationLog.txt.

El proveedor solo funciona cuando el proyecto se ejecuta en el entorno de Azure. No tiene ningún efecto cuando el proyecto se ejecuta de manera local (no escribe en los archivos locales ni en el almacenamiento de desarrollo local de blobs).

Secuencias de registro de Azure

Las secuencias de registro de Azure permiten ver la actividad de registro en tiempo real desde:

  • El servidor de aplicaciones
  • El servidor web
  • Error del seguimiento de solicitudes

Para configurar las secuencias de registro de Azure:

  • Navegue hasta la página Registros de App Service desde la página de portal de la aplicación.
  • Establezca Registro de la aplicación (sistema de archivos) en Activado.
  • Elija el Nivel de registro. Este valor solo se aplica a las secuencias de registro de Azure, no a otros proveedores de registro de la aplicación.

Navegue hasta la página Secuencia de registro para consultar los mensajes de la aplicación. La aplicación los registra a través de la interfaz ILogger.

Registro de seguimiento de Azure Application Insights

El proveedor de paquete Microsoft.Extensions.Logging.ApplicationInsights escribe los registros en Azure Application Insights. Application Insights es un servicio que supervisa una aplicación web y proporciona herramientas para consultar y analizar los datos de telemetría. Si usa este proveedor, puede consultar y analizar los registros mediante las herramientas de Application Insights.

El proveedor de registro se incluye como dependencia de Microsoft.ApplicationInsights.AspNetCore, que es el paquete que proporciona toda la telemetría disponible para ASP.NET Core. Si usa este paquete, no tiene que instalar el proveedor de paquete.

No use el paquete Microsoft.ApplicationInsights.Web—que es para ASP.NET 4.x.

Para obtener más información, vea los siguientes recursos:

Proveedores de registro de terceros

Plataformas de registro de terceros que funcionan con ASP.NET Core:

Algunas plataformas de terceros pueden realizar registro semántico, también conocido como registro estructurado.

El uso de una plataforma de terceros es similar al uso de uno de los proveedores integrados:

  1. Agregue un paquete NuGet al proyecto.
  2. Llame a un método de extensión ILoggerFactory proporcionado por el marco de registro.

Para más información, vea la documentación de cada proveedor. Microsoft no admite los proveedores de registro de terceros.

Recursos adicionales