Serilog tiene seis niveles de log, de menor a mayor gravedad: Verbose, Debug, Information, Warning, Error y Fatal. La propia documentación de configuración de Serilog indica el valor predeterminado: “si no se especifica MinimumLevel, se procesarán los eventos de nivel Information y superiores”. Ese es también el valor predeterminado razonable en producción para los logs de la aplicación.

En esta guía, establecemos niveles mínimos de forma global, por espacio de nombres y por sink: los tres controles que deciden qué se escribe realmente.

Para descargar el código fuente de este artículo, puedes visitar nuestro repositorio de GitHub.

Configuración de Serilog

Antes de ver los distintos niveles de log, tenemos que agregar el paquete NuGet Serilog.AspNetCore a nuestro proyecto ASP.NET Core MVC (dotnet add package Serilog.AspNetCore --version 10.0.0) y configurarlo para que use Serilog:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSerilog(options =>
{
    options.MinimumLevel.Information()
           .WriteTo.Console(LogEventLevel.Information);
});

Este fragmento de código da por hecho que using Serilog; y using Serilog.Events; están al principio del archivo. El primer argumento posicional de WriteTo.Console(LogEventLevel.Information) es restrictedToMinimumLevel; conviene mencionarlo de forma explícita, porque no presentamos esa opción hasta más adelante en este artículo.

Para ver con más detalle cómo usar Serilog, no dejes de consultar nuestro artículo sobre logging estructurado en ASP.NET Core con Serilog.

Empecemos por los niveles de log.

¿Cuáles son los distintos niveles de log de Serilog?

Los niveles de log nos permiten clasificar la gravedad del evento registrado. Serilog admite seis niveles de log:

Serilog asigna a cada evento de log uno de los seis niveles, ordenados por gravedad: Verbose, Debug, Information, Warning, Error y Fatal. Verbose es el que más ruido genera: flujo interno y payloads en bruto que solo habilitamos mientras investigamos un problema concreto. Debug contiene estado de diagnóstico útil para los desarrolladores, pero no para el equipo de operaciones.

Information registra la vida normal de la aplicación: solicitudes atendidas, pedidos realizados, trabajos completados. Warning señala situaciones inesperadas pero no fatales, como un reintento o un mecanismo de respaldo. Error indica que una operación ha fallado y que alguien debería revisarla, mientras que Fatal indica que la propia aplicación no puede continuar.

Serilog solo escribe un evento cuando su nivel es igual o superior al mínimo configurado: Information de forma predeterminada. Los nombres difieren ligeramente de los niveles de ILogger de Microsoft: el nivel Verbose de Serilog equivale a Trace y Fatal equivale a Critical; los otros cuatro coinciden uno a uno.

Nivel de SerilogMétodo de ILoggerPara qué usarlo¿Registrarlo en producción?
VerboseLogTrace()Trazar el funcionamiento interno, bucles, payloadsNo
DebugLogDebug()Estado de diagnóstico para los desarrolladoresNo (se habilita cuando hace falta)
InformationLogInformation()Eventos de negocio, flujo de las solicitudesSí
WarningLogWarning()Situaciones inesperadas pero manejadasSí
ErrorLogError()Operaciones fallidas que requieren atenciónSí
FatalLogCritical()Caídas, estados irrecuperablesSí (con alertas)

Serilog no tiene un nivel Critical ni un nivel Trace; Fatal y Verbose son sus equivalentes, y la correspondencia anterior es exactamente la que aplica Serilog.Extensions.Logging para traducirlos.

A medida que el nivel de log aumenta de Verbose a Fatal, también aumenta su importancia.

Generación de mensajes de log en los distintos niveles

ASP.NET Core proporciona la interfaz ILogger<T> para el logging, sea cual sea la infraestructura de logging que usemos. Basta con inyectar la interfaz ILogger<T> en el constructor para empezar a registrar mensajes de inmediato.

Con esto en mente, veamos cómo generar logs en cada nivel:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;

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

    public IActionResult Index()
    {
        _logger.LogTrace("Trace Log Message");
        _logger.LogDebug("Debug Log Message");
        _logger.LogInformation("Information Log Message");
        _logger.LogWarning("Warning Log Message");
        _logger.LogError("Error Log Message");
        _logger.LogCritical("Critical Log Message");

        return View();
    }
}

Como mencionamos antes, en el constructor de HomeController inyectamos la interfaz ILogger<HomeController>, que usamos para inicializar la instancia _logger. Después, en el método Index(), registramos con _logger un mensaje en cada uno de los niveles de log admitidos.

The Web API Production Checklist, ebook gratuito en inglés

Ebook gratis

¿Tu Web API está lista para producción?

33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.

El ebook está en inglés.

Descarga la checklist gratis

PDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.

Serilog se conecta con esta interfaz a través de Serilog.Extensions.Logging; explicamos la relación completa en artículos específicos, incluido cómo usar Serilog con la API ILogger de Microsoft y cómo encajan ILogger, ILoggerFactory e ILoggerProvider.

¿Cómo establecer el nivel mínimo de log en Serilog?

Serilog nos ofrece tres puntos para controlar el nivel mínimo, que se aplican en este orden. En primer lugar, MinimumLevel.Default (o MinimumLevel.Information() en la API fluida) fija el mínimo global: los eventos por debajo de él nunca se crean, así que su costo es casi nulo.

En segundo lugar, MinimumLevel.Override reemplaza ese mínimo para espacios de nombres concretos; sobrescribir (override) Microsoft y System con Warning es la forma habitual de silenciar el ruido del framework sin perder nuestros propios eventos Information. En tercer lugar, restrictedToMinimumLevel filtra por sink, de modo que la consola puede mostrar Information mientras el sink de archivo solo registra Warning y superiores.

El mínimo global se impone primero: un sink puede ser más restrictivo que el nivel global, pero nunca menos. Para los niveles que queremos cambiar en tiempo de ejecución sin volver a desplegar, pasamos un LoggingLevelSwitch a MinimumLevel.ControlledBy() y cambiamos su valor en tiempo de ejecución desde un endpoint de administración o con una recarga de la configuración. No hace falta volver a desplegar ni reiniciar, y el cambio se aplica de inmediato en todos los sinks.

En la práctica, el mínimo cambia según el entorno: Debug durante el desarrollo y Information en producción, donde Serilog solo registra los eventos Information, Warning, Error y Fatal.

Veamos cómo configurar MinimumLevel en el archivo appSettings.json:

"Serilog": {
  "MinimumLevel": {
    "Default": "Information",
    "Override": {
      "Microsoft": "Warning",
      "System": "Warning"
    }
  }
}

En este caso, le indicamos a Serilog que genere logs de la aplicación para los niveles iguales o superiores al nivel Information. Además, sobrescribimos el logging de los mensajes de Microsoft y System, de modo que solo se registren cuando sean de nivel Warning o superior. Para que Serilog lea esta sección, llamamos a options.ReadFrom.Configuration(builder.Configuration) dentro de AddSerilog().

Ahora vamos a hacer la misma configuración con la API fluida en Program.cs:

builder.Services.AddSerilog(options =>
{
    options.MinimumLevel.Information()
           .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
           .MinimumLevel.Override("System", LogEventLevel.Warning);
});

Para cambiar el nivel mínimo en tiempo de ejecución sin volver a desplegar, lo envolvemos en un LoggingLevelSwitch (que necesita using Serilog.Core; al principio del archivo) y se lo pasamos a MinimumLevel.ControlledBy(); después cambiamos el nivel del interruptor desde cualquier lugar que tenga una referencia a él, como un endpoint de administración:

var levelSwitch = new LoggingLevelSwitch(LogEventLevel.Information);

builder.Services.AddSerilog(options =>
{
    options.MinimumLevel.ControlledBy(levelSwitch)
           .WriteTo.Console();
});

// later, at runtime, e.g. from an admin endpoint:
levelSwitch.MinimumLevel = LogEventLevel.Debug;

Fíjate en que aquí solo cambia levelSwitch.MinimumLevel; la configuración del sink no se toca, y el nuevo mínimo se aplica desde la siguiente llamada de logging, sin reiniciar.

¿Dónde escribir los logs?

Podemos configurar Serilog para que escriba los logs en distintos destinos o sinks. Un sink, en el contexto del logging, es el destino de nuestros mensajes de log. Estos son algunos de los sinks más comunes:

  • La consola de la aplicación
  • El sistema de archivos
  • Bases de datos relacionales, como MS SQL Server
  • Bases de datos no relacionales, como Elasticsearch o MongoDB

La propia documentación de configuración de Serilog lo expresa así: “Los sinks de eventos de log suelen registrar los eventos de log en alguna representación externa, normalmente la consola, un archivo o un almacén de datos”. Los conectamos con el objeto de configuración WriteTo, y todos los sinks disponibles aceptan un argumento restrictedToMinimumLevel, de modo que cada destino puede tener su propio mínimo por encima del global.

Hay una lista seleccionada de los sinks disponibles aquí. Para ver los sinks con más detalle, consulta nuestro artículo sobre logging estructurado en ASP.NET Core con Serilog. Cuando el sink es el sistema de archivos, el artículo sobre logging con rotación de archivos en Serilog explica en detalle la rotación por tamaño y por tiempo.

The Web API Production Checklist, ebook gratuito en inglés

Ebook gratis

¿Tu Web API está lista para producción?

33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.

El ebook está en inglés.

Descarga la checklist gratis

PDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.

Podemos configurar los sinks de Serilog en el archivo appSettings.json o con la API fluida en el archivo Program.cs.

Configuración de los sinks en appsettings.json

Vamos a configurar un sink de consola y otro de archivo mediante appSettings.json:

"Serilog": {
  "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console" ],
  "WriteTo": [
    {
      "Name": "Console",
      "Args": {
        "restrictedToMinimumLevel": "Information",
        "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
      }
    },
    {
      "Name": "File",
      "Args": {
        "path": "logs/log-.txt",
        "rollOnFileSizeLimit": true,
        "rollingInterval": "Day",
        "fileSizeLimitBytes": "1000000",
        "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
        "restrictedToMinimumLevel": "Warning"
      }
    }
  ]
}

Primero, especificamos los ensamblados de los sinks mediante la opción Using. Después, configuramos cada sink con la opción WriteTo, que recibe un array de sinks con sus opciones de configuración asociadas.

Configuración de los sinks con la API fluida

Ahora veamos cómo configurar los sinks de logging con la API fluida en Program.cs:

builder.Services.AddSerilog(options =>
{
    options.MinimumLevel.Information()
           .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
           .MinimumLevel.Override("System", LogEventLevel.Warning)
           .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information,
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
           .WriteTo.File("logs/log-.txt",
                rollOnFileSizeLimit: true,
                rollingInterval: RollingInterval.Day,
                fileSizeLimitBytes: 1000000,
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
                restrictedToMinimumLevel: LogEventLevel.Warning);
});

Diferenciar los sinks según el nivel de log

Según los requisitos de la aplicación y el entorno, puede que necesitemos configurar Serilog para que escriba determinados niveles de log en determinados sinks. Por ejemplo, quizá queramos escribir en la consola los logs con nivel mínimo Information, pero en un archivo los logs con nivel mínimo Warning. En estos casos, usamos la opción restrictedToMinimumLevel. Permite que Serilog diferencie los destinos de los logs según el nivel mínimo de log.

Podemos configurar la opción restrictedToMinimumLevel a través de appSettings.json:

"Serilog": {
    "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console" ],
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "restrictedToMinimumLevel": "Information"
        }
      },
      {
        "Name": "File",
        "Args": {
          "path": "logs/log-.txt",
          "restrictedToMinimumLevel": "Warning"
        }
      }
    ]
  }

Del mismo modo, podemos configurar restrictedToMinimumLevel con la API fluida:

builder.Services.AddSerilog(options =>
{
    options.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
           .WriteTo.File("logs/log-.txt", 
                   restrictedToMinimumLevel: LogEventLevel.Warning);
});

Conclusión

En este artículo, hemos visto los niveles de log de Serilog y cómo usarlos en una aplicación. Hemos fijado el mínimo con la opción MinimumLevel, hemos configurado los sinks tanto con el archivo appSettings.json como con la API fluida y hemos usado restrictedToMinimumLevel para dar a cada sink su propio umbral.

Para un conjunto más amplio de buenas prácticas de logging con Serilog, más allá de la elección del nivel, ofrecemos el panorama completo en una guía específica.

Probado con .NET 10.0.10, Serilog 4.4.0 y Serilog.AspNetCore 10.0.0.