Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 15 de junio de 2016
ASP.NET CoreEste post es una reedición de un contenido anterior para actualizarlo a la Release Candidate 2 de ASP.NET Core. ¡A ver si se calma todo un poco y conseguimos que los contenidos valgan más de un par de meses! ;D

Uno de los requisitos más habituales que tenemos en cualquier aplicación medianamente compleja es la creación de logs donde guardar información cuando ocurran determinados eventos.

En ASP.NET "clásico" teníamos varias opciones, como utilizar las propias herramientas de tracing del marco de trabajo, o bien frameworks especializados de terceros como los célebres Log4Net, NLog o muchos otros, pero al final la idea era la misma, poder disponer de un componente en el que poder registrar sucesos como el siguiente:
public ActionResult RemoveInvoice(string id)
{
    ... // Remove the invoice  
    _logger.LogInformation($"Invoice {id} was removed by {User.Identity?.Name}");
    return RedirectToAction("Index");
}
El nuevo ASP.NET Core viene equipado de serie con un sistema de logging interno, integrado en el propio marco de trabajo, que proporciona algunas funcionalidades pero, sobre todo, los mimbres básicos sobre los que montar sistemas de trazas más complejos y personalizables.

Dedicaremos este post a ver cómo se configura y utiliza este mecanismo que, como otros aspectos del framework, está construido pensando en su extensibilidad y hace uso intensivo del sistema de inyección de dependencias.

Nota: aunque la RTM se acerca, sigue siendo bueno recordar que todavía estamos utilizando una release candidate de ASP.NET Core y, por tanto, algunos detalles aún podrían variar antes de que aparezca la versión final del producto.

1. Creación y uso básico de un logger

Como comentaba algo más arriba, el sistema de logging hace uso de las herramientas internas de inyección de dependencias, y esto es algo que queda patente desde su configuración. De hecho, para configurar un logger lo primero que haremos es obtener una instancia de ILoggerFactory en algún punto inyectable, como el método Configure() de la clase de inicialización:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddDebug();
    ...
}
ILoggerFactory es una factoría de loggers, es decir, un componente cuya misión es crear objetos ILogger que son los que realmente utilizaremos para registrar nuestros eventos, utilizando para ello una serie de proveedores configurables. La configuración que apliquemos a la factoría es la que heredarán más adelante los loggers que utilicemos desde la aplicación.

Por ejemplo, la llamada a AddDebug() que hemos visto anteriormente configura la factoría para que los loggers que sean creados desde ella utilicen como canal de salida la ventana de resultados de depuración de Visual Studio. Es decir, desde nuestro código registraremos eventos en el log, y los veremos aparecer por esta ventana de nuestro entorno de desarrollo.

Pero tenemos otras opciones para configurar a dónde irán a parar las trazas. Igual que antes hemos utilizado AddDebug(), tenemos también disponible AddConsole() para hacer que los mensajes aparezcan en la ventana de consola (útil cuando ejecutamos la aplicación web desde línea de comandos), que vayan directamente al registro de evento de Windows (obviamente esta opción sólo estará disponible sobre .NET 4.x en Windows), o crear nuestros propios destinos personalizados implementando proveedores personalizados, como de hecho ya existen para NLog, Loggr, Elmah.io, o Serilog.

Incluso podemos configurar nuestra factoría con más de uno de ellos para que los eventos se registren en distintos puntos.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddDebug(); 
    loggerFactory.AddConsole(); 
    // Los eventos logados aparecerán en la consola y en la ventana de VS  
    ...
}
Bien, una vez configurada la factoría, el siguiente paso del proceso sería crear el objeto ILogger que usaremos para registrar eventos en los canales que hayamos configurado. Esto lo hacemos mediante el método CreateLogger() de ILoggerFactory, al que debemos suministrar un nombre de categoría.

Este nombre se suele utilizar para indicar el componente origen del evento, pero en realidad podemos utilizar cualquier string que nos ayude a puntualizar el contexto en el que ha sido registrado:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddDebug();
    ILogger logger = loggerFactory.CreateLogger("Startup");     

    // Usar logger para registrar eventos
    ...
}
También podemos utilizar una versión genérica de CreateLogger(), que usará como categoría el nombre del tipo que usemos en su invocación:
ILogger logger = loggerFactory.CreateLogger<Startup>();
En cualquier caso, tras ello, para registrar un mensaje informativo lo único que tendríamos que hacer invocar el método LogInformation() del ILogger pasándole el texto:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddDebug();
    ILogger logger = loggerFactory.CreateLogger<Startup>();
    logger.LogInformation("Application starting");

    ...
}
Observad que dado que la factoría ha sido configurada para que los loggers usen la ventana de depuración de Visual Studio mediante AddDebug(), al ejecutar la aplicación veríamos en ella algo similar a lo siguiente:
MyRc2App.Startup:Information: Application starting

Mensaje de información apareciendo en la ventana "Output" de Visual Studio

2. Niveles de traza

Por otra parte, como podríamos esperar, a la hora de registrar eventos en el log tenemos distintos niveles mediante los cuales indicamos la importancia de los mensajes y que pueden ser utilizados a posteriori para filtrar las trazas y evitar el exceso de información. ASP.NET Core define los siguientes niveles en la enumeración LogLevel:
  • LogLevel.Trace, el nivel más detallado con todo tipo de información técnica destinada a desarrolladores. Pueden contener información sensible, por lo que por defecto está deshabilitado y no se recomienda su activación en entornos de producción.
  • LogLevel.Debug, para mensajes de depuración sobre cambios o estado puntual de un componente, pero sin utilidad en el largo plazo.
  • LogLevel.Information, destinado a mostrar mensajes que indican el flujo de ejecución de una aplicación.
  • LogLevel.Warning deberíamos usarlo para registrar acontecimientos anormales en el flujo de ejecución que no paran el sistema pero que deberían ser estudiados, como puede ser la captura de una excepción controlada.
  • LogLevel.Error debería usarse para registrar errores en la ejecución (por ejemplo, excepciones no controladas) que abortan un proceso, pero que no impiden que la ejecución de la aplicación continúe.
  • LogLevel.Critical, reservado para errores críticos que requieren atención inmediata, como que un disco se haya quedado sin espacio o se haya perdido de forma permanente la conexión con la base de datos.
Intellisense mostrando métodos extensores de ILogger para registrar eventosPara cada uno de estos niveles encontraremos un método extensor en ILogger, de forma que podremos utilizarlos de forma muy sencilla y cómoda a la hora de registrar eventos: LogTrace(), LogDebug(), LogInformation(), etc.

Para evitar el exceso de ruido, a la hora de configurar los proveedores que vamos a utilizar para almacenar los logs podemos indicar el nivel mínimo de logging que soportará, de forma que todos los registros que se encuentren por arriba serán ignorados. Por ejemplo, si añadimos la siguiente línea a la configuración, configurará la factoría para que los loggers usen la ventana de depuración de VS, pero en ella sólo se muestren los eventos de tipo Warning, Error o Critical:
loggerFactory.AddDebug(minLevel: LogLevel.Warning);
Fijaos que esto, unido al hecho de que podemos configurar varios proveedores en la factoría, puede posibilitarnos escenarios muy interesantes, como registrar eventos en distintos orígenes en función del nivel de traza, o incluso de otros criterios más sofisticados.

3. Simplificar la creación del logger

Habréis observado que crear el logger desde cualquier punto de nuestra aplicación y usarlo sería bastante sencillo porque, una vez configurada, sólo habría que obtener esa instancia de ILoggerFactory   valiéndonos del inyector de dependencias, crear a continuación el objeto ILogger a partir de ella, y usar este último para registrar los eventos, como en el siguiente ejemplo:
public class HomeController : Controller
{
    private readonly ILoggerFactory _loggerFactory;

    public HomeController(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }
    
    public IActionResult Index()
    {
        var logger = _loggerFactory.CreateLogger("HomeController");
        logger.LogInformation("Index action executed");
        return View();
    }
}
Este código podemos simplificarlo un poco, a la vez que ganamos en organización, si utilizamos el servicio de inyección de dependencias para obtener directamente una instancia de ILogger para la clase actual. Observad que utilizamos una variante tipada ILogger<T>:
public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;

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

    public IActionResult Index()
    {
        _logger.LogInformation("Index action executed");
        return View();
    }
}
El ejecutar la acción Index() del código anterior, en la ventana "Output" de Visual Studio podríamos observar que aparece el texto de la traza asociado a la clase HomeController:
MyRc2App.Controllers.HomeController:Information: Index action executed

Mensaje informativo en la ventana "Output" de Visual Studio

Y lo dejamos aquí, al menos de momento ;) Espero que el post os haya resultado útil para ver el funcionamiento básico del sistema de logging de ASP.NET Core y sus posibilidades. Más adelante a ver si me animo y vemos cómo crear un proveedor de log personalizado, por ejemplo para guardar las trazas en un buffer  en memoria o quizás en una base de datos, o tal vez enviarnos por email los errores críticos… las posibilidades, como siempre, infinitas :)

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!