miércoles, 4 de mayo de 2016
Nota: existe una actualización de este post a la RC2 de ASP.NET Core. Mejor que leas esa ;)
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 framework o 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 funcionalidades básicas 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 este componente no es un gran candidato a cambiar dada las pocas actualizaciones que ha sufrido en los últimos meses, es 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 deILoggerFactory
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. 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 tomará el nombre de la categoría del tipo que usemos en su invocación:ILogger logger = loggerFactory.CreateLogger<Startup>();En cualquier caso, tras ello ya lo único que tendríamos que hacer invocar el método
LogInformation()
del ILogger
pasándole el mensaje a registrar: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, al ejecutar la aplicación veríamos en ella algo similar a lo siguiente:
2. Niveles de traza
Por otra parte, como podríamos esperar, a la hora de registrar eventos en el log, tenemos también 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ónLogLevel
:LogLevel.Debug
, 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.Verbose
, 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.
Para 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: LogDebug()
, LogVerbose()
, 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 deILoggerFactory
, crear el objeto ILogger
a partir de ella, y usarlo 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: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(); } }
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!
Enviar un nuevo comentario