Solía usar fachadas de registro como Common.Logging (incluso para ocultar mi propia biblioteca CuttingEdge.Logging ), pero hoy en día uso el patrón de Inyección de dependencia y esto me permite ocultar los registradores detrás de mi propia (simple) abstracción que se adhiere tanto a Dependency Principio de inversión y el principio de segregación de interfaces(ISP) porque tiene un miembro y porque mi aplicación define la interfaz; no una biblioteca externa. Minimizando el conocimiento que tienen las partes centrales de su aplicación 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 su código y complica su aplicación con una API que nunca fue diseñada específicamente para su aplicación.
Así es como suele verse la abstracción 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 (permitiendo 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.
}
Dado que la interfaz contiene solo un método, puede crear fácilmente una ILogger
implementación que sustituye al log4net , a Serilog , Microsoft.Extensions.Logging , Nlog o cualquier otra biblioteca de registro y configurar el contenedor de DI para inyectarlo en las clases que tienen una 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 solo 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 LogEntry
mensaje y lo pasan a través del único método en la ILogger
interfaz. Los métodos de extensión pasan a formar parte del código del consumidor; no forma 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 laLogEntry
constructor siempre se ejecutan cuando se usa la abstracción del registrador, incluso cuando ese registrador es anulado / simulado. Esto brinda más certeza sobre la exactitud de las llamadas al registrador cuando se ejecuta en un conjunto de pruebas. La interfaz de un miembro también facilita las pruebas; Tener una abstracción con muchos miembros dificulta la creación de implementaciones (como simulacros, adaptadores y decoradores).
Cuando hace esto, casi nunca hay necesidad de alguna abstracción estática que las fachadas de registro (o cualquier otra biblioteca) puedan ofrecer.