Pregunta Cómo usar log4net con Dependency Injection


Estoy tratando de averiguar cuál es el patrón y el uso correctos de log4net con un marco de inyección de dependencia.

Log4Net usa la interfaz ILog pero requiere que llame

LogManager.GetLogger(Reflection.MethodBase.GetCurrentMethod().DeclaringType)

en cada clase o método donde necesito ingresar información. Esto parece ir en contra de los principios de IoC y me conecta con el uso de Log4Net.

¿Debería poner de alguna manera otra capa de abstracción en alguna parte?

Además, necesito registrar propiedades personalizadas como el nombre de usuario actual como este:

log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;

¿Cómo puedo encapsular esto para que no tenga que recordar hacerlo cada vez y mantener el método actual que está registrando? ¿Debería hacer algo como esto o me estoy perdiendo la marca?

public static class Logger
{
    public static void LogException(Type declaringType, string message, Exception ex)
    {
        log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;
        ILog log = LogManager.GetLogger(declaringType);
        log.Error(message, ex);
    }
}

74
2018-06-02 17:32


origen


Respuestas:


Creo que no estás viendo el bosque por los árboles aquí. ILog y LogManager son una fachada liviana de casi 1: 1 equivalente al registro de Apache commons, y en realidad no acoplan su código al resto de log4net.

<rant>
También encontré eso casi siempre cuando alguien crea un contenedor MyCompanyLogger alrededor de log4net, pierde el punto mal y pierde capacidades importantes y útiles del marco, descarta información útil, pierde las ganancias de rendimiento posibles incluso con la interfaz ILog simplificada o con todas las anteriores. En otras palabras, envolviendo log4net para evitar el acoplamiento a él es un anti-patrón.
</rant>

Si sientes la necesidad de inyectarlo, haz que tu instancia de registro esté accesible a través de una propiedad para habilitar la inyección, pero crea una instancia predeterminada de la manera tradicional.

En cuanto a incluir el estado contextual en cada mensaje de registro, debe agregar una propiedad global cuya ToString() resuelve lo que estás buscando. Como ejemplo, para el tamaño del montón actual:

public class TotalMemoryProperty
{
    public override string ToString()
    {
        return GC.GetTotalMemory(false).ToString();
    }
}

Luego, para enchufarlo durante el inicio:

GlobalContext.Properties["TotalMemory"] = new TotalMemoryProperty();

60
2018-06-02 17:37



Cómo lo hice fue crear mi propia interfaz, e hice que una clase implementara esa interfaz que usaba Log4Net e inyecté esa clase. De esa forma no estás vinculado a Log4Net ... puedes simplemente crear otra clase para un nuevo registrador e inyectar esa clase.


2
2018-06-02 17:36



Si realmente le preocupa tener varios registradores, siempre puede declarar un registrador global y llamarlo para todas sus necesidades de registro. La desventaja de esto es que se pierde la capacidad incorporada para identificar la clase desde la cual se emitió el registro, pero también se puede insertar su propio mensaje personalizado en el registro para identificar la clase.

Depende de cuán robusto quieras que sea el registro. También puede simplemente colocar el registrador en la clase principal y permitir que todas las excepciones no controladas se activen para iniciar sesión.


2
2018-06-02 17:41



Otra forma de hacerlo sería conectar la secuencia de registro del contenedor de la interfaz LOG4net ILog de la siguiente manera: (usando Castle en un contexto ASP.NET a continuación como ejemplo)

container.Register(Component.For<ILog>().LifeStyle
    .PerWebRequest.UsingFactoryMethod(() => LogManager.GetLogger(
    MethodBase.GetCurrentMethod().DeclaringType)));

y solo constructor-inyectar ILog cada vez que lo necesite.


2
2018-03-12 15:42