Mejor práctica de contenedor Logger

Quiero utilizar un nlogger en mi aplicación, tal vez en el futuro necesite cambiar el sistema de registro. Entonces quiero usar una fachada de registro.

¿Conoces alguna recomendación de ejemplos existentes sobre cómo escribir esos? O simplemente dame un enlace a algunas mejores prácticas en esta área.

Solía ​​usar fachadas de registro como Common.Logging (incluso para ocultar mi propia biblioteca CuttingEdge.Logging ), pero hoy en día utilizo el patrón Dependency Injection y esto me permite ocultar madereros detrás de mi propia abstracción (simple) que se adhiere a ambas Dependencias Principio de inversión y el Principio de segregación de interfaz (ISP) porque tiene un miembro y porque la interfaz está definida por mi aplicación; no una biblioteca externa. Minimizando el conocimiento de que las partes centrales de su aplicación tienen sobre la existencia de bibliotecas externas, mejor; incluso si no tiene la intención de reemplazar su biblioteca de registro. La fuerte dependencia de la biblioteca externa hace que sea más difícil probar el código y complica su aplicación con una API que nunca fue diseñada específicamente para su aplicación.

Esto es lo que la abstracción a menudo se ve en mis aplicaciones:

public interface ILogger { void Log(LogEntry entry); } public enum LoggingEventType { Debug, Information, Warning, Error, Fatal }; // Immutable DTO that contains the log information. public class LogEntry { public readonly LoggingEventType Severity; public readonly string Message; public readonly Exception Exception; public LogEntry(LoggingEventType severity, string message, Exception exception = null) { if (message == null) throw new ArgumentNullException("message"); if (message == string.Empty) throw new ArgumentException("empty", "message"); this.Severity = severity; this.Message = message; this.Exception = exception; } } 

Opcionalmente, esta abstracción se puede ampliar con algunos métodos de extensión simples (lo que permite que la interfaz se mantenga estrecha y siga adhiriéndose al ISP). Esto hace que el código para los consumidores de esta interfaz sea mucho más simple:

 public static class LoggerExtensions { public static void Log(this ILogger logger, string message) { logger.Log(new LogEntry(LoggingEventType.Information, message)); } public static void Log(this ILogger logger, Exception exception) { logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception)); } // More methods here. } 

Como la interfaz contiene solo un método, puede crear fácilmente una implementación de ILogger que se redirija a log4net , a Serilog , Microsoft.Extensions.Logging , NLog o cualquier otra biblioteca de registro y configure su contenedor DI para inyectarlo en clases que tengan un ILogger en su constructor.

Tenga en cuenta que tener métodos de extensión estáticos en la parte superior de una interfaz con un único método es bastante diferente de tener una interfaz con muchos miembros. Los métodos de extensión son solo métodos auxiliares que crean un mensaje LogEntry y lo pasan a través del único método en la interfaz de ILogger . Los métodos de extensión se vuelven parte del código del consumidor; no es parte de la abstracción Esto no solo permite que los métodos de extensión evolucionen sin la necesidad de cambiar la abstracción, los métodos de extensión y el constructor LogEntry siempre se ejecutan cuando se utiliza la abstracción del registrador, incluso cuando ese registrador es anulado o burlado. Esto proporciona más certeza sobre la corrección de las llamadas al registrador cuando se ejecuta en un banco de pruebas. La interfaz de un solo miembro hace que las pruebas sean mucho más fáciles también; Tener una abstracción con muchos miembros hace que sea difícil crear implementaciones (como burlas, adaptadores y decoradores).

Cuando hace esto, casi nunca hay necesidad de abstracción estática que puedan ofrecer las fachadas de registro (o cualquier otra biblioteca).

Utilicé el envoltorio de la interfaz pequeña + adaptador de https://github.com/uhaciogullari/NLog.Interface que también está disponible a través de NuGet :

 PM> Install-Package NLog.Interface 

Generalmente prefiero crear una interfaz como

 public interface ILogger { void LogInformation(string msg); void LogError(string error); } 

y en el tiempo de ejecución, inyecté una clase concreta que se implementa desde esta interfaz.

Una gran solución a este problema ha surgido en la forma del proyecto LibLog .

LibLog es una abstracción de registro con soporte integrado para los principales registradores, incluidos Serilog, NLog, Log4net y Enterprise Logger. Se instala a través del administrador de paquetes NuGet en una biblioteca de destino como un archivo de origen (.cs) en lugar de una referencia .dll. Ese enfoque permite que la abstracción de registro se incluya sin forzar a la biblioteca a asumir una dependencia externa. También permite que un autor de la biblioteca incluya el registro sin forzar a la aplicación consumidora a proporcionar explícitamente un registrador a la biblioteca. LibLog utiliza la reflexión para descubrir qué registrador concreto está en uso y conectarlo sin ningún código explícito de cableado en los proyectos de la biblioteca.

Por lo tanto, LibLog es una gran solución para iniciar sesión en proyectos de biblioteca. Simplemente haga referencia y configure un registrador de concreto (Serilog para ganar) en su aplicación o servicio principal y agregue LibLog a sus bibliotecas.

En lugar de escribir su propia fachada, puede usar Castle Logging Services o Simple Logging Faade .

Ambos incluyen adaptadores para NLog y Log4net.

Desde 2015, también puede usar .NET Core Logging si está creando aplicaciones centrales de .NET.

El paquete para enganchar NLog es:

  • NLog.Web.AspNetCore para usuarios de ASP.NET Core
  • NLog.Extensions.Logging para usuarios de .NET Core, por ejemplo, proyectos de consola