Mejores prácticas de envoltura de registrador


Quiero usar un nlogger en mi aplicación, tal vez en el futuro necesite cambiar el sistema de registro. Así que quiero usar una fachada maderera.

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

Author: Uwe Keim, 2011-04-13

6 answers

Solía usar fachadas de registro como Común.Logging (incluso para ocultar mi propio CuttingEdge.Logging library), pero hoy en día uso el Dependency Injection pattern y esto me permite ocultar registradores detrás de mi propia abstracción (simple) que se adhiere tanto al Principio de Inversión de Dependencias como al Principio de Segregación de Interfaces (ISP) porque tiene un miembro y porque la interfaz está definida por mi aplicación; no una biblioteca externa. Minimizar el conocimiento que las partes principales 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 su código y complica su aplicación con una API que nunca se diseñó específicamente para su aplicación.

Así es como se ve a menudo 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, esto la abstracción se puede extender 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 un solo método, puede crear fácilmente una implementación ILogger que proxy a log4net, a Serilog, Microsoft.Ampliación.Logging , NLog o cualquier otra biblioteca de logging y configure su contenedor DI para inyectarlo en clases que tienen 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 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 mensaje LogEntry y lo pasan a través del único método en la interfaz ILogger. Los métodos de extensión se convierten en parte del código del consumidor; no 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 logger, incluso cuando ese logger es stubbed/mocked. Esto da más certeza sobre la corrección de las llamadas al registrador cuando se ejecuta en un conjunto de pruebas. La interfaz de un miembro hace que las pruebas también sean mucho más fáciles; Tener una abstracción con muchos miembros hace que sea difícil crear implementaciones (como simulaciones, adaptadores y decoradores).

Cuando lo haces esto, casi nunca hay necesidad de alguna abstracción estática que logging facades (o cualquier otra biblioteca) pueda ofrecer.

 174
Author: Steven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-23 14:07:31

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

PM> Install-Package NLog.Interface 
 9
Author: Jon Adams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-20 15:59:41

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

LibLog es una abstracción de registro con soporte incorporado para los principales registradores, incluidos Serilog, NLog, Log4net y Enterprise logger. Se instala a través del gestor de paquetes NuGet en una biblioteca de destino como fuente (.cs) en lugar de a .referencia dll. Este enfoque permite incluir la abstracción de registro sin forzar a la biblioteca a asumir una dependencia externa. También permite una autor de la biblioteca para incluir el registro sin forzar a la aplicación consumidora a proporcionar explícitamente un registrador a la biblioteca. LibLog utiliza la reflexión para averiguar qué registrador concreto está en uso y conectarse a él sin ningún código de cableado explícito en el proyecto(s) de la biblioteca.

Por lo tanto, LibLog es una gran solución para el registro dentro de proyectos de bibliotecas. Simplemente haga referencia y configure un registrador concreto (Serilog for the win) en su aplicación o servicio principal y agregue LibLog a sus bibliotecas.

 4
Author: Rob Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-09 18:31:07

Generalmente prefiero crear una interfaz como

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

Y en el tiempo de ejecución inyecto una clase concreta que se implementa desde esta interfaz.

 3
Author: crypted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-13 09:13:38

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

Ambos incluyen adaptadores para NLog y Log4net.

 2
Author: Jonas Kongslund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-13 09:10:15

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

El paquete al que NLog puede conectarse es:

 1
Author: Julian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-27 21:40:12