design patterns - Cómo usar log4net con Dependency Injection
design-patterns dependency-injection (4)
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);
}
}
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.
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 descubrí que, casi siempre, cuando alguien crea un contenedor MyCompanyLogger alrededor de log4net, pierde el rumbo 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 todo lo anterior. En otras palabras, ajustar log4net para evitar el acoplamiento es un antipatró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 cuyo ToString()
resuelva lo que está 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();
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.
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.