observer - pattern design proxy
¿Cuál es el punto de una fachada de tala? (9)
Creo que lo que hace Uno (nivel de abstracción) el número mágico aquí es que Zero es muy pocos y Dos es demasiado.
El intercambio de un registrador detrás de la fachada de un registrador (número de niveles: 1) puede resultar en algún beneficio para el usuario, como que el nuevo registrador puede hacer algo que el registrador anterior no puede hacer. Me puedo imaginar que podría ser el rendimiento, el apoyo a ciertos tipos de appenders, etc.
Es mucho más difícil imaginar que el usuario se beneficie al intercambiar una fachada de registrador (número de niveles: 2).
(Y si el número de niveles es 0, entonces probablemente sea solo un diseño orientado a objetos malo: tendrá miles de lugares en el código donde se hace referencia al registrador y qué pasa si hay un cambio de rotura en la próxima versión del registrador. )
El trato con fachadas de registrador parece ser que tienes que elegir una de las opciones de terceros o crear la tuya propia y prepararte para seguirla por mucho tiempo.
Hay una gran cantidad de bibliotecas de registro diferentes para elegir, cada una con su propio conjunto de peculiaridades y ventajas. (Ejemplos de .Net: log4net, System.Diagnostics.TraceSource, nLog, etc.)
La inclinación natural es abstraer esas peculiaridades y usar una fachada de registro. (ejemplos: Castle.Services.Logging , Common.Logging , Simple Logging Facade ) De esta manera, si un marco de trabajo de registro determinado que está utilizando se queda obsoleto, o uno diferente entra en boga, puede simplemente cambiar la implementación y salir el código intacto
Pero hay varias fachadas de registro para elegir. Dado que la respuesta a muchas implementaciones de registro dispares fue la abstracción, ¿por qué no utilizar una fachada de fachada de registro? Si eso suena ridículo, ¿qué lo hace más ridículo que la fachada de tala original? ¿Qué hace que una capa extra de abstracción sobre el marco de registro sea el número mágico?
De ninguna manera soy un experto, pero en nuestro grupo, el valor de la fachada no nos da la capacidad de cambiar el marco de trabajo. Es cierto, eso es algo que obtenemos, pero estamos en la categoría que es muy poco probable que cambie nuestro marco.
En nuestro caso, estamos utilizando una fachada para adaptar la interfaz de registro a las necesidades de nuestra aplicación. Descubrimos que en todos los marcos semánticos que vimos, todavía estaban demasiado concentrados en lo que llamamos el modelo de registro "forense": alguien que busca entre los registros la búsqueda de alguna línea de salida para analizar algún evento.
Si bien este es un caso de uso para nosotros también, en realidad no es nuestro caso de uso principal. Queremos más un marco de instrumentación que un registro, lo que nos permitirá informar sobre cosas de interés, incluso aquellas en las que no hemos pensado en el momento de la implementación.
Por ejemplo, nuestra aplicación no necesita un "mensaje" para acompañar un evento; en cambio, nuestro "registrador" aceptará enumeraciones que definan el tipo de evento y los objetos de estado que representan detalles específicos (por ejemplo, marcas de tiempo u otros valores) y los serializará para facilitar informes, análisis de rendimiento, métricas de valor empresarial, etc., además de respaldar los forense. (Reconocemos que estamos, de hecho, haciendo que los forenses tradicionales sean un poco más difíciles en beneficio de una interfaz de registro fácil de usar y comprender que aumenta la probabilidad de que realmente la usemos y la usemos con más frecuencia).
Entonces, para darle una respuesta resumida de manera sucinta, aquí hay un orden aproximado de los beneficios que creemos que obtendremos al utilizar una fachada de registro.
- Instrumentación para facilitar la interfaz coherente y construida especialmente (en contraposición al registro tradicional, como se discutió anteriormente)
- Puntos de prueba fingibles
- Implementaciones de registradores inyectables adecuadas para todo, desde desarrollo local hasta conexiones AWS S3 o DB, dependiendo de la implementación (nuestra aplicación utiliza Autofac con inyección de dependencia de constructor)
- Marcos de registro sustituibles que nos permiten cambiar a un marco logger diferente en el futuro si queremos. (Por cierto, no prevemos que esto ocurra, por lo que, por sí solo, no nos habría convencido de usar una fachada).
Y finalmente, 0 fachadas perderían estos beneficios, y 2 fachadas no se agregarían a ninguno de estos beneficios, por eso 1 fachada es el número correcto para nosotros.
Gran pregunta, @brian! :)
En este ejemplo, solo necesita un nivel de abstracción para poder intercambiar su registrador.
¿Qué ventaja adicional sería poder cambiar su fachada de registro?
Hablaré principalmente desde la perspectiva del uso de la abstracción para aislar el código de la aplicación de un marco de registro particular. Hay otros factores que pueden afectar la elección de un marco de trabajo de registro o la elección de uno (y los requisitos para) una abstracción.
He pasado mucho tiempo evaluando recientemente varios marcos de trabajo de registro, así como abstracciones de registro de terceros.
Algunas personas sienten que es valioso aislar su código de aplicación de un marco de registro específico. Encontrará muchos posts aquí en SO como this y this y this (y hay más) donde se discute el registro y muchas personas lo toman como una cuestión de curso que el marco de registro debe ser envuelto / abstraído.
Obviamente, esto le permite no estar atado a un marco específico. ¿Es esto importante? ¿Realmente cambiarás tu marco de trabajo? Bueno, también hay muchas personas que no mencionan envolver o que recomiendan no hacerlo. Si nos fijamos en algunos de los ejemplos del código de envoltura del marco de registro que se ha publicado aquí, también puede ver muchos ejemplos de por qué algunas personas no deberían envolver su marco de registro.
Si hubiera comenzado recientemente un proyecto, es posible que haya examinado los marcos de registro y, tal vez, lo haya reducido a dos finalistas: log4net y NLog. Cada uno tiene argumentos a su favor. log4net es claramente un favorito, probablemente EL favorito de aquellos que han expresado una opinión. NLog proporciona capacidades muy similares. A juzgar por la popularidad, log4net podría ser la opción más clara. Según las capacidades, parecen muy similares. En función de la "actividad reciente" (como lo indican los registros de sus repositorios de código fuente por actividad del blog o la falta de ellos), NLog será la opción más clara. Si tuvo que elegir hace un año, puede ir con log4net ya que sería la opción "segura". No estaba claro cuándo se lanzaría NLog. En el año transcurrido desde entonces, NLog ha pasado por un ciclo de desarrollo bastante significativo, lanzando una versión beta hace unos días.
¿Qué elegir hace un año? ¿Qué elegir ahora? ¿Fue una opción claramente mejor entonces? ¿Una es la mejor opción ahora?
Una cosa que le proporciona una abstracción es la capacidad de posponer la decisión de cuál elegir (ni siquiera necesariamente TIENE que elegir SIEMPRE, aunque es probable que desee hacerlo si planea entregar el marco de trabajo de registro con su producto). Puede probar una unidad y luego la otra y obtener una idea de cómo funcionan con su aplicación, con su equipo, en su entorno. El uso de algo como Common.Logging o SLF permite comenzar a escribir código ahora, codificarlo en alguna API / interfaz de registro y obtener su código de registro en su lugar. Si cree que la interfaz / API es provista por la abstracción es suficiente para su trabajo (y, por qué no sería, ya que es esencialmente lo mismo que la interfaz / API proporcionada por log4net y NLog), entonces no hay mucho peligro al usar la abstracción. A medida que avanza en el ciclo de desarrollo, puede encontrar que un marco u otro se adapta mejor a sus necesidades. Después de haber codificado la abstracción, puede elegir libremente en cualquier momento, hasta que su producto salga por la puerta.
Incluso podría pensar, en el fondo de su mente, que posiblemente podría escribir una biblioteca de registro desde cero. Nuevamente, si usted cree que la interfaz / API de log4net y / o NLog es suficiente, puede implementar su biblioteca de registro con una API similar. Si crees eso, esa podría ser otra razón para usar una abstracción. Nuevamente, puede comenzar a escribir código (para su producto, no su biblioteca de registro) hoy, iniciando sesión con algún otro marco de registro hasta el momento en que su biblioteca de registro "desde cero" esté lista. Quizás desee usar System.Diagnostics.TraceSource y Ukadc.Diagnostics (para obtener capacidades de formato de salida similares a log4net o NLog) para que pueda obtener una "mejor" integración con el registro que Microsoft ha implementado en algunas de sus plataformas utilizando TraceSources . Podría ser muy fácil escribir un "registrador" en términos de TraceSources y luego escribir la abstracción para que pueda conectarlo a Common.Logging o SLF. (Si la interfaz / API es suficiente, puede simplemente escribir su "registrador" en términos de la interfaz de la biblioteca de abstracción y no tener que escribir una capa de abstracción adicional).
Con argumentos tan persuasivos como estos, ¿por qué alguien nunca usaría una abstracción? ¡Jaja solo bromeo!
Si una abstracción es buena, ¿debería escribir la suya o usar una existente? Si escribes uno por tu cuenta, obviamente tienes que escribirlo. ¿Cómo hace uno esto? Bueno, puedes definir una interfaz y ajustar un marco (¡ten cuidado y envuélvelo correctamente!). Más tarde, si decides que quieres cambiar, ajusta ese marco. Si tiene cuidado, no tiene que cambiar ningún código de aplicación, excepto tal vez el lugar donde realmente crea los objetos del marco subyacente. Quizás esto es bueno. Ha evitado una dependencia en la abstracción de un tercero por el precio "pequeño" de implementar un único contenedor en un marco único. Sin embargo, hay un costo. Hasta que haya escrito su abstracción, no podrá escribir mucho código de aplicación que tenga un inicio de sesión, a menos que tenga una buena estrategia para cambiarlo a su abstracción. También se vuelve más difícil probar dos o más marcos para decidir cuál funciona mejor para usted. Cada marco que desee "probar" requiere otro trabajo de ajuste. Si desea cambiar entre marcos fácilmente (al menos durante el ciclo de desarrollo), tiene trabajo que hacer para hacerlo más fácil. Los marcos de terceros proporcionan esto fuera de la caja.
¡Guauu! ¡Ahora estoy vendido! Dame la extracción de registros, o dame la muerte!
¿Están todas las extracciones de la extracción de registros? ¿Hay algún inconveniente? No pueden ESO genial, ¿verdad?
Bueno, como siempre, cuando "compras" algo o cuando obtienes algo gratis, obtienes lo que está disponible. Las abstracciones de extracción no son diferentes. Ni Common.Logging ni SLF exponen al menos un conjunto muy importante de capacidades de log4net / NLog - las capacidades de contexto de registro (GDC, MDC, NDC). Estos pueden ser clave para obtener la información adecuada registrada y formateada para que pueda obtener el máximo valor de su. SLF no proporciona una abstracción de TraceSource. Tampoco proporciona funciones habilitadas para IsXXX. Common.Logging proporciona una abstracción de TraceSource. Castle.Logging DOES expone GDC / MDC / NDC para log4net y NLog. También proporciona una abstracción de TraceSource. La abstracción TraceSource de Castle también mejora el registro de TraceSource al proporcionar una capacidad de nomenclatura "jerárquica", similar a la proporcionada por log4net y NLog. ¡Se ve muy bien!
Además, estos proyectos son todos de código abierto de una forma u otra. Entonces, dependiendo de la abstracción, los desarrolladores podrían tener más o menos un interés personal en mantenerlo actualizado y agregar nuevas características. Common.Logging ha pasado por algunas versiones y se utiliza, AFAIK, en Spring.Net. Parece razonablemente activo, al menos históricamente. Castle.Logging se usa en el marco Castle. Por lo tanto, aparentemente tienen clientes "reales" y están obteniendo un uso del "mundo real", lo que con suerte impulsará la implementación de más funciones. SLF, por lo que puedo decir, no se usa como parte de una plataforma de desarrollo "real", por lo que es difícil decir cuánto se ejerce.
No está claro cuál es la hoja de ruta para estas plataformas. Common.Logging tiene algunas características próximas enumeradas en su sitio web, pero no indica claramente cuándo estarán disponibles. El sitio web dice "junio", pero ¿de qué año? ¿Con qué frecuencia se monitorea la lista de correo? Para SLF, ¿con qué frecuencia se monitorea su codeplex? ¿Dónde se clasifica la prioridad de estos proyectos "gratuitos" en comparación con los empleos remunerados de los desarrolladores? ¿Puede permitirse una abstracción de terceros para implementar una característica que necesita? ¿Serán receptivos si implementan algo y luego lo envían nuevamente para que se incluya en el producto?
En el lado positivo, está disponible toda la fuente de todas estas abstracciones, por lo que puedes asumir la responsabilidad y hacer correcciones o agregar mejoras que, sin tener que pasar por el tiempo y la energía de crear una abstracción de rasguño. ¿Le gusta Common.Logging pero realmente quiere log4net / NLog GDC / MDC / NDC? Obtenga la implementación de Castle y agréguela a Common.Logging. Voila! Una abstracción de registro que contiene casi el 100% de la API de registro log4net / NLog. ¿Prefiere SLF pero desearía tener IsXXXEnabled? No hay mucho trabajo para implementar eso. Continúe y vire en el GDC / MDC / NDC mientras lo hace. ¿Te gusta Castle? (No estoy tan familiarizado con eso, no estoy seguro de lo fácil que es usarlo fuera de Castle, si eso es importante) Tenga cuidado, no lo he usado, pero mirando la fuente en git, parece que el registrador NLog la abstracción puede no retener la información del sitio de llamada.
¿Es ético tomar partes de múltiples proyectos de código abierto y combinarlos para hacer un proyecto "super" (para su propio uso o el de su compañía)? ¿Es malo tomar Common.Logging y aumentarlo con la implementación de Castle GDC / MDC / NDC? No lo sé. Dejaré que alguien más responda eso.
Estoy casi terminado ...
Algunas abstracciones de registro de terceros proporcionan otras capacidades. Puede usar una biblioteca que se implemente en términos de, digamos log4net. Es posible que no desee usar log4net, o al menos no desee estar vinculado a él. Common.Logging (y tal vez SLF) hace que sea relativamente fácil para usted capturar los mensajes de log4net logging y redirigirlos a través de la abstracción para que sean capturados en la corriente de registro subyacente del marco de registro de la abstracción. SLF podría proporcionar algo similar. Por supuesto, es posible que pueda hacer algo similar con los marcos de registro existentes, ya sea de manera inmediata o escribiendo un apéndice log4net personalizado, NLog Target o System.Diagnostics TraceListener. Estas características no han brotado muy alto en mi evaluación particular de si usar o no una abstracción de registro de terceros en mi proyecto porque estoy interesado principalmente simplemente en el aspecto de la abstracción.
Entonces, ¿dónde estoy parado? Creo que tiene sentido mantener el código de su aplicación aislado de un marco de registro específico. Para mí, Common.Logging parece una opción sólida de abstracción, aunque faltan algunas características importantes (GDC / MDC / NDC) y no es compatible con Silverlight. Sería genial de esas características disponibles pronto. Me siento cómodo con la implementación de GDC / MDC / NDC si es necesario. Hacerlo compatible con Silverlight probablemente requeriría más esfuerzo, principalmente porque no tengo mucha experiencia con C # /. NET / Silverlight. Hasta que se solucionen esos problemas, podríamos escribir un montón de código de aplicación con Common.Logging en su lugar. Podemos dedicar nuestro tiempo a desarrollar nuestra aplicación en lugar de desarrollar otra biblioteca de registro o biblioteca de abstracción. Si terminamos teniendo que agregar esas características faltantes nosotros mismos, bueno, habríamos tenido que hacer mucho de eso si hubiésemos implementado una biblioteca de registro o una biblioteca de abstracción nosotros mismos.
Hasta que NLog y log4net proporcionen una interfaz que se pueda usar en lugar de las clases concretas, siempre las resumí detrás de mi propia interfaz y clase contenedora.
¿Por qué?
Para obtener la mayor cobertura de prueba de todos los requisitos del usuario, especialmente cuando esos requisitos cubren el registro.
Cuando tiene todo su registro en funcionamiento a través de una interfaz en lugar de una clase concreta, resulta muy fácil proporcionar un objeto simulado con la interfaz de registro (seleccione su elección de cómo hacerlo, inyección de dependencia, etc.) para registrar todos las llamadas de registro realizadas durante un escenario de prueba particular.
Estos pueden ser afirmados y si un cambio de código rompe el registro, las pruebas de su unidad lo cubrirán.
Si NLog o log4Net fueran a proporcionar una interfaz para sus registradores, entonces no tendría que hacer el esfuerzo de proporcionar una interfaz y una clase contenedora, ya que podría burlarme de su interfaz para las pruebas.
No es el número mágico, depende de lo flexible que quieras ser. Si piensa cambiar la fachada del registro un día, debe escribir una fachada. Si piensa cambiar solo el registro, necesita una fachada. Si no piensas en nada, no uses fachada.
La desventaja como dijiste son las habilidades especiales. Si los está usando, escriba su propia fachada solamente.
Se puede usar en el protocolo para el sistema de complementos. En lugar de decir que use Some3rdParty.dll and MyPlugingApi.dll
, solo documentaría MyPlugingApi.dll
. La interfaz de la fachada del registro sugerirá y documentará algún uso que probablemente genere registros legibles y un rendimiento logístico suficientemente bueno. Y, por supuesto, hacer cambios no generará cambios en la API de complementos.
¿Por qué es necesario cambiar la implementación? Esto puede suceder si la corriente parece ralentizarse para comenzar a salir de la configuración o ralentizar la escritura de entradas, desea expandirse para reducir la versión de .NET que no necesita una tercera parte, integrándola con otra base de código que utiliza otros grabadores de registro.
También he escrito otra facade que es hija de pensamientos en esta respuesta.
Un uso importante de una fachada de registro es cuando estás escribiendo una biblioteca . Incrustar dependencias en una biblioteca siempre es algo que requiere un poco de cuidado, y aún más.
En resumen, no desea forzar su implementación de registro a los usuarios de su biblioteca . Usar una fachada bien elegida significa que podrán manejar los registros de su biblioteca con su propio marco de trabajo y no tendrán que pasar por algunas extrañas exclusiones de dependencia y lagunas para que coexistan tanto su marco de trabajo como el de ellos.
En mi proyecto utilizo System.Diagnostics.Trace.TraceError(...)
, System.Diagnostics.Debug.Print(...)
como fachada para el registro. Para organizar registros (de escritura) utilizo NLog, es decir, en app.config tengo la configuración para NLog y la redirección del seguimiento de .net a NLog.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File"
layout="${longdate:universalTime=true}Z [${threadid}] ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}"
fileName="${basedir}/App_Data/logfile.txt"...
</targets>
</nlog>
<system.diagnostics>
<trace>
<listeners>
<add name="nlog" type="NLog.NLogTraceListener, NLog" />
</listeners>
</trace>
</system.diagnostics>
Esto no me une a ningún registrador. Cuando envío mis componentes a los clientes, pueden usar cualquier registrador que quieran. Usar un registrador particular en la aplicación podría causar problemas, es decir, puede usar nlog pero sus clientes usan log4net.