Registro en .NET

.NET 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 se aplican a cualquier aplicación .NET que use el host genérico. En el caso de las aplicaciones que no usan el host genérico, vea Aplicación de consola que no es de host.

Sugerencia

Todo el código fuente del ejemplo de registro está disponible en el Explorador de ejemplos para su descarga. Para obtener más información, consulte Examinación de ejemplos de código: registro en .NET.

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<Worker>, que utiliza una categoría de registro del nombre completo del tipo Worker. 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 Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

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

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.UtcNow);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

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

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 servicio de trabajo de .NET:

{
  "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".
  • 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": {
            "Default": "Error",
            "Microsoft": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.Hosting": "Trace"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    }
}

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 la configuración de todos los proveedores integrados:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft.Extensions.Hosting": "Warning",
                "Default": "Information"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "EventLog": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "AzureAppServicesFile": {
            "IncludeScopes": true,
            "LogLevel": {
                "Default": "Warning"
            }
        },
        "AzureAppServicesBlob": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "ApplicationInsights": {
            "LogLevel": {
                "Default": "Information"
            }
        }
    }
}

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. 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. Por ejemplo, puede crear una variable de entorno persistente denominada Logging:LogLevel:Microsoft con un valor de Information.

Cree y asigne una variable de entorno persistente según el valor de nivel de registro.

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

En una nueva instancia del símbolo del sistema, lea la variable de entorno.

:: Prints the env var value
echo %Logging__LogLevel__Microsoft%

La configuración del entorno anterior se conserva en el entorno. Para probar la configuración cuando se usa una aplicación creada con las plantillas de servicio de trabajo de .NET, use el comando dotnet run en el directorio del proyecto después de asignar la variable de entorno.

dotnet run

Sugerencia

Después de establecer una variable de entorno, reinicie su entorno de desarrollo integrado (IDE) para asegurarse de que las variables de entorno recién agregadas están disponibles.

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 sobre cómo establecer los valores de configuración de .NET mediante variables de entorno, vea Variables de entorno.

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 hay ninguna regla seleccionada, use LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) para especificar el nivel de registro mínimo.

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 una aplicación con un servicio definido como el objeto siguiente, la categoría podría ser "Example.DefaultService":

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

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

        // ...
    }
}

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

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

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

        // ...
    }
}

La llamada a CreateLogger con un nombre fijo puede ser útil cuando se usa en varias clases o tipos, por lo que los eventos se pueden organizar por categoría.

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 no se 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:

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

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

AppLogEvents.Details es el identificador del evento y se representa implícitamente mediante un valor Int32 de constante. AppLogEvents es una clase que expone varias constantes de identificador con nombre y se muestra en la sección Id. de evento del registro.

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

public async Task<T> GetAsync<T>(string id)
{
    _logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);

    var result = await _repository.GetAsync(id);
    if (result is null)
    {
        _logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
    }

    return result;
}

En el código anterior, el primer parámetro de Log{LogLevel}, AppLogEvents.Read, 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 de la plantilla de mensaje más adelante en este artículo.

Configure el nivel de registro adecuado y llame a los métodos Log{LogLevel} correctos para controlar el volumen de resultados del registro que se escriben en un soporte de almacenamiento determinado. 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.

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

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

Id. de evento del registro

Cada registro puede especificar un identificador de evento; EventId es una estructura con Id y propiedades opcionales Name de solo lectura. El código fuente de ejemplo usa la clase AppLogEvents para definir los identificadores de evento:

internal static class AppLogEvents
{
    internal const int Create = 1000;
    internal const int Read = 1001;
    internal const int Update = 1002;
    internal const int Delete = 1003;

    internal const int Details = 3000;
    internal const int Error = 3001;

    internal const int ReadNotFound = 4000;
    internal const int UpdateNotFound = 4001;

    // ...
}

Un id. de evento asocia un conjunto de eventos. Por ejemplo, todos los registros relacionados con la lectura de valores de un repositorio pueden ser 1001.

El proveedor de registro puede registrar 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: Example.DefaultService.GetAsync[1001]
      Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
      GetAsync(a1b2c3) 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. 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. Observe el siguiente método de registrador:

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

Por ejemplo, al registrar en Azure Table Storage:

  • Cada entidad de Azure Table puede tener propiedades ID y RunTime.
  • Las tablas con propiedades simplifican las consultas en los datos registrados. Por ejemplo, una consulta puede buscar todos los registros dentro de un intervalo RunTime 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:

public void Test(string id)
{
    try
    {
        if (id == "none")
        {
            throw new Exception("Default Id detected.");
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(
            AppLogEvents.Error, ex,
            "Failed to process iteration: {Id}", id);
    }
}

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, observe la siguiente aplicación de servicio de trabajo:

  • Creada con las plantillas de trabajo de .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:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning));
}

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:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
                logging.AddFilter((provider, category, logLevel) =>
                {
                    return provider.Contains("ConsoleLoggerProvider")
                        && (category.Contains("Example") || category.Contains("Microsoft"))
                        && logLevel >= LogLevel.Information;
                }));
}

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

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

public async Task<T> GetAsync<T>(string id)
{
    T result;

    using (_logger.BeginScope("using block message"))
    {
        _logger.LogInformation(
            AppLogEvents.Read, "Reading value for {Id}", id);

        var result = await _repository.GetAsync(id);
        if (result is null)
        {
            _logger.LogWarning(
                AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
        }
    }

    return result;
}

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

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

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

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging((_, logging) =>
                logging.ClearProviders()
                    .AddConsole(options => options.IncludeScopes = true));
}

Aplicación de consola que no es de host

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

        ILogger logger = loggerFactory.CreateLogger<Program>();
        logger.LogInformation("Example log message");
    }
}

El objeto loggerFactory se usa para crear una instancia de ILogger.

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:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

class Program
{
    static Task Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder(args).Build();

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

        return host.RunAsync();
    }
}

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

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.

Paquetes NuGet

Las implementaciones y las interfaces de ILogger<TCategoryName> y ILoggerFactory se incluyen en el SDK de .NET. 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:

class Program
{
    static Task Main(string[] args) =>
        CreateHostBuilder(args).Build().RunAsync();

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

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

Vea también