Tenía la costumbre de pasar el registrador al constructor, como:
public class OrderService : IOrderService {
public OrderService(ILogger logger) {
}
}
Pero eso es bastante molesto, así que lo he usado como una propiedad durante algún tiempo:
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
Esto también se está volviendo molesto, no está seco, necesito repetir esto en todas las clases. Podría usar la clase base, pero, de nuevo, estoy usando la clase Form, por lo que necesitaría FormBase, etc. Así que creo que, ¿cuál sería la desventaja de tener un singleton con ILogger expuesto, para que cualquiera sepa dónde obtener el registrador?
Infrastructure.Logger.Info("blabla");
ACTUALIZACIÓN: Como Merlyn notó correctamente, debo mencionar que en el primer y segundo ejemplo estoy usando DI.
Respuestas:
Es verdad. Pero hay mucho que puede hacer por una preocupación transversal que impregna todos los tipos que tiene. Usted tiene que utilizar el registrador en todas partes, por lo que debe tener la propiedad de esos tipos.
Así que veamos qué podemos hacer al respecto.
único
Los singleton son terribles
<flame-suit-on>
.Recomiendo seguir con la inyección de propiedades como lo hizo con su segundo ejemplo. Este es el mejor factoring que puede hacer sin recurrir a la magia. Es mejor tener una dependencia explícita que ocultarla mediante un singleton.
Pero si los singleton le ahorran un tiempo significativo, incluida toda la refactorización que tendrá que hacer (¡tiempo de bola de cristal!), Supongo que podría vivir con ellos. Si alguna vez hubo un uso para un Singleton, este podría ser. Tenga en cuenta que el costo si alguna vez desea cambiar de opinión será tan alto como sea posible.
Si hace esto, revise las respuestas de otras personas usando el
Registry
patrón (vea la descripción) y aquellos que registran una fábrica singleton (reiniciable) en lugar de una instancia de registrador singleton.Hay otras alternativas que podrían funcionar igual de bien sin tanto compromiso, por lo que debe verificarlas primero.
Fragmentos de código de Visual Studio
Puede usar fragmentos de código de Visual Studio para acelerar la entrada de ese código repetitivo. Podrás escribir algo como
logger
tab, y el código aparecerá mágicamente para ti.Usando AOP para SECAR
Puede eliminar un poco de ese código de inyección de propiedad utilizando un marco de programación orientada a aspectos (AOP) como PostSharp para generar automáticamente parte de él.
Podría verse algo así cuando haya terminado:
[InjectedLogger] public ILogger Logger { get; set; }
También puede usar su código de muestra de seguimiento de métodos para rastrear automáticamente el código de entrada y salida del método, lo que podría eliminar la necesidad de agregar algunas de las propiedades del registrador todas juntas. Puede aplicar el atributo a nivel de clase o en todo el espacio de nombres:
[Trace] public class MyClass { // ... } // or #if DEBUG [assembly: Trace( AttributeTargetTypes = "MyNamespace.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public )] #endif
fuente
</flame-suit-on>
No sé cómo viviste con un traje de fuego durante 5 años, pero espero que esto te ayude.Pongo una instancia de registrador en mi contenedor de inyección de dependencia, que luego inyecta el registrador en las clases que lo necesitan.
fuente
Buena pregunta. Creo que en la mayoría de los proyectos el registrador es un singleton.
Algunas ideas me vienen a la mente:
Object
tipo de modo que cada clase sería capaz de llamar a los métodos del registrador gustaLogInfo()
,LogDebug()
,LogError()
fuente
public static void LogInfo(this Object instance, string message)
para que cada clase los recoja, con respecto aServiceLocator
: esto le permite tener el registrador como una instancia de clase regular, no como un singleton, por lo que le daría mucha flexibilidadUn singleton es una buena idea. Una idea aún mejor es usar el patrón de registro , que brinda un poco más de control sobre la creación de instancias. En mi opinión, el patrón singleton está demasiado cerca de las variables globales. Con un registro que maneja la creación o reutilización de objetos, hay espacio para cambios futuros en las reglas de creación de instancias.
El registro en sí puede ser una clase estática para proporcionar una sintaxis simple para acceder al registro:
Registry.Logger.Info("blabla");
fuente
Un singleton simple no es una buena idea. Hace que sea difícil reemplazar el registrador. Tiendo a usar filtros para mis registradores (algunas clases "ruidosas" solo pueden registrar advertencias / errores).
Utilizo el patrón singleton combinado con el patrón proxy para la fábrica de registradores:
public class LogFactory { private static LogFactory _instance; public static void Assign(LogFactory instance) { _instance = instance; } public static LogFactory Instance { get { _instance ?? (_instance = new LogFactory()); } } public virtual ILogger GetLogger<T>() { return new SystemDebugLogger(); } }
Esto me permite crear un
FilteringLogFactory
o simplemente unSimpleFileLogFactory
sin cambiar ningún código (y por lo tanto cumplir con el principio Abierto / Cerrado).Extensión de muestra
public class FilteredLogFactory : LogFactory { public override ILogger GetLogger<T>() { if (typeof(ITextParser).IsAssignableFrom(typeof(T))) return new FilteredLogger(typeof(T)); return new FileLogger(@"C:\Logs\MyApp.log"); } }
Y usar la nueva fábrica
// and to use the new log factory (somewhere early in the application): LogFactory.Assign(new FilteredLogFactory());
En su clase que debería registrar:
public class MyUserService : IUserService { ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>(); public void SomeMethod() { _logger.Debug("Welcome world!"); } }
fuente
Instance
en la raíz de la aplicación y no sé por qué lo restablecería)Hay un libro Inyección de dependencias en .NET. Según lo que necesite, debe utilizar la intercepción.
En este libro hay un diagrama que ayuda a decidir si usar la inyección de constructor, inyección de propiedad, inyección de método, contexto ambiental, intercepción.
Así es como se razona usando este diagrama:
Usar intercepción
fuente
Otra solución que personalmente encuentro la más fácil es usar una clase Logger estática. Puede llamarlo desde cualquier método de clase sin tener que cambiar la clase, por ejemplo, agregar una inyección de propiedad, etc. Es bastante simple y fácil de usar.
Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed
fuente
Si desea buscar una buena solución para el registro, le sugiero que mire el motor de aplicaciones de Google con Python, donde el registro es tan simple como
import logging
y luego puede simplementelogging.debug("my message")
ologging.info("my message")
que realmente lo mantiene tan simple como debería.Java no tenía una buena solución para el registro, es decir, debe evitarse log4j, ya que prácticamente lo obliga a usar singletons que, como se responde aquí, es "terrible" y he tenido una experiencia horrible al tratar de hacer que la salida de registro sea la misma declaración de registro solo una vez cuando sospecho que el motivo del doble registro fue que tengo un Singleton del objeto de registro en dos cargadores de clases en la misma máquina virtual (!)
Le pido perdón por no ser tan específico para C #, pero por lo que he visto, las soluciones con C # se ven similares a Java donde teníamos log4j y también deberíamos convertirlo en un singleton.
Por eso me gustó mucho la solución con GAE / python , es tan simple como puede ser y no tiene que preocuparse por los cargadores de clases, obtener una declaración de registro doble o cualquier patrón de diseño en absoluto.
Espero que parte de esta información pueda ser relevante para usted y espero que desee echar un vistazo a la solución de registro que recomiendo, en lugar de eso, intimido sobre la cantidad de problemas que se sospecha de Singleton debido a la imposibilidad de tener un singleton real cuando debe estar instanciando en varios cargadores de clases.
fuente
using Logging; /* ... */ Logging.Info("my message");
logging.info("my message")
en un programa más complicado que hola mundo. Por lo general, se realizan muchas tareas repetidas al inicializar el registrador: configurar el formateador, el nivel, configurar los controladores de archivos y de consola. ¡Nunca jamáslogging.info("my message")
!