source patterns pattern patron observer making doble diseño despacho design-patterns log4net

design-patterns - patterns - visitor patron java



¿Cómo sería una clase Log4Net Wrapper? (9)

¿Qué beneficios planea dejar de escribir un contenedor para log4net? Recomiendo que te sientas cómodo con las clases de log4net primero antes de escribir un contenedor a su alrededor. cfeduke tiene razón en su respuesta sobre cómo escribir dicha envoltura, pero a menos que necesite agregar funcionalidad real a su ejemplo, una envoltura solo tendría éxito en desacelerar el proceso de registro y agregar complejidad para los futuros mantenedores. Esto es especialmente cierto cuando las herramientas de refacturación disponibles en .Net hacen que estos cambios sean muy fáciles.

He estado buscando un marco de registro para .net (c #) y decidí darle una oportunidad a log4net después de leer algunos hilos de pregunta / respuesta aquí en stackoverflow. Veo a personas mencionar una y otra vez que usan una clase de contenedor para log4net y me pregunto cómo se vería.

Tengo mi código dividido en diferentes proyectos (acceso a datos / negocio / servicio web / ..). ¿Cómo se vería una clase de contenedor log4net? ¿Debería incluirse la clase contenedora en todos los proyectos? ¿Debo construirlo como un proyecto separado todos juntos?

¿Debería ser el envoltorio una clase singleton?


Alconja, me gusta tu idea de usar stacktrace para volver al método de llamada. Estaba pensando en encapsular las llamadas, no solo para recuperar el objeto logger, sino para realizar realmente el registro. Lo que quiero es una clase estática que maneje el registro, al abstraer de la implementación específica utilizada. Es decir

LoggingService.LogError("my error message");

De esa manera solo necesito cambiar las partes internas de la clase estática, si luego decido utilizar otro sistema de registro.

Así que utilicé su idea para obtener el objeto que llama utilizando el seguimiento de la pila:

public static class LoggingService { private static ILog GetLogger() { var stack = new StackTrace(); var frame = stack.GetFrame(2); return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType); } public static void LogError(string message) { ILog logger = GetLogger(); if (logger.IsErrorEnabled) logger.Error(message); } ... }

¿Alguien ve un problema con este enfoque?


Asumiendo que ibas con algo como la respuesta de cfeduke anterior , también podrías agregar una sobrecarga a tu LogManager esta manera:

public static ILogger GetLogger() { var stack = new StackTrace(); var frame = stack.GetFrame(1); return new Log4NetWrapper(frame.GetMethod().DeclaringType); }

De esa manera en su código ahora puede simplemente usar:

private static readonly ILogger _logger = LogManager.GetLogger();

en lugar de cualquiera de estos:

private static readonly ILogger _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILogger _logger = LogManager.GetLogger(typeof(YourTypeName));

Que es efectivamente equivalente a la primera alternativa (es decir, la que usa MethodBase.GetCurrentMethod().DeclaringType ), solo un poco más simple.


Básicamente, usted crea una interfaz y luego una implementación concreta de esa interfaz que envuelve las clases y métodos de Log4net directamente. Los sistemas de registro adicionales pueden ser envueltos creando clases más concretas que envuelven otras clases y métodos de esos sistemas. Finalmente, use una fábrica para crear instancias de sus envolturas según una configuración o una línea de cambio de código. (Nota: puede ser más flexible, y complejo, utilizando un contenedor de Inversión de control como StructureMap ).

public interface ILogger { void Debug(object message); bool IsDebugEnabled { get; } // continue for all methods like Error, Fatal ... } public class Log4NetWrapper : ILogger { private readonly log4net.ILog _logger; public Log4NetWrapper(Type type) { _logger = log4net.LogManager.GetLogger(type); } public void Debug(object message) { _logger.Debug(message); } public bool IsDebugEnabled { get { return _logger.IsDebugEnabled; } } // complete ILogger interface implementation } public static class LogManager { public static ILogger GetLogger(Type type) { // if configuration file says log4net... return new Log4NetWrapper(type); // if it says Joe''s Logger... // return new JoesLoggerWrapper(type); } }

Y un ejemplo de uso de este código en sus clases (declarado como un campo de solo lectura estático):

private static readonly ILogger _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Puede obtener el mismo efecto ligeramente más agradable para el rendimiento utilizando:

private static readonly ILogger _logger = LogManager.GetLogger(typeof(YourTypeName));

El primer ejemplo se considera más sostenible.

No desea crear un Singleton para manejar todos los registros porque Log4Net registra el tipo de invocación; es mucho más limpio y útil para que cada tipo use su propio registrador en lugar de simplemente ver un solo tipo en el archivo de registro que informa todos los mensajes.

Debido a que su implementación debe ser bastante reutilizable (otros proyectos en su organización), puede hacer que sea su propio ensamblaje o, idealmente, incluirlo con el ensamblaje de su marco / utilidad personal / organizacional. No vuelva a declarar las clases por separado en cada uno de sus ensamblados de negocios / datos / UI, eso no es mantenible.


Existen marcos como la Biblioteca Prism para WPF que promueven el uso de una facade para el marco de trabajo de registro que usted elija.

Este es un ejemplo que usa log4net :

using System; using log4net; using log4net.Core; using Prism.Logging; public class Log4NetLoggerFacade : ILoggerFacade { private static readonly ILog Log4NetLog = LogManager.GetLogger(typeof (Log4NetLoggerFacade)); public void Log(string message, Category category, Priority priority) { switch (category) { case Category.Debug: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Debug, message, null); break; case Category.Exception: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Error, message, null); break; case Category.Info: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Info, message, null); break; case Category.Warn: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Warn, message, null); break; default: throw new ArgumentOutOfRangeException(nameof(category), category, null); } } }

Tenga en cuenta que al especificar el callerStackBoundaryDeclaringType todavía puede obtener el nombre de la clase del llamante que emite la solicitud de registro. Todo lo que necesita hacer es incluir %C %M en su patrón de conversión:

<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %C.%M - %message%newline" /> </layout>

Sin embargo, como lo advierte la documentation , la generación de la información de la clase llamante es lenta, por lo tanto, debe usarse con prudencia.


He aislado con éxito la dependencia de log4net en un solo proyecto. Si tiene la intención de hacer lo mismo, esta es la apariencia de mi clase contenedora:

using System; namespace Framework.Logging { public class Logger { private readonly log4net.ILog _log; public Logger() { _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } public Logger(string name) { _log = log4net.LogManager.GetLogger(name); } public Logger(Type type) { _log = log4net.LogManager.GetLogger(type); } public void Debug(object message, Exception ex = null) { if (_log.IsDebugEnabled) { if (ex == null) { _log.Debug(message); } else { _log.Debug(message, ex); } } } public void Info(object message, Exception ex = null) { if (_log.IsInfoEnabled) { if (ex == null) { _log.Info(message); } else { _log.Info(message, ex); } } } public void Warn(object message, Exception ex = null) { if (_log.IsWarnEnabled) { if (ex == null) { _log.Warn(message); } else { _log.Warn(message, ex); } } } public void Error(object message, Exception ex = null) { if (_log.IsErrorEnabled) { if (ex == null) { _log.Error(message); } else { _log.Error(message, ex); } } } public void Fatal(object message, Exception ex = null) { if (_log.IsFatalEnabled) { if (ex == null) { _log.Fatal(message); } else { _log.Fatal(message, ex); } } } } }

Y no olvide agregar esto en AssemblyInfo.cs del proyecto de interfaz (me tomó unas buenas horas encontrarlo)

[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]

Y ponga su xml de configuración de log4net.config en el archivo log4net.config , log4net.config como Content , Copy Always


Sé que esta respuesta es tardía, pero puede ayudar a alguien en el futuro.

Parece que quieres una API programática que XQuiSoft Logging te brinda. No tiene que especificar qué registrador desea con XQuiSoft. Es tan simple como esto:

Log.Write (Level.Verbose, "fuente", "categoría", "su mensaje aquí");

Luego, a través de la configuración, dirige los mensajes por fuente, categoría, nivel o cualquier otro filtro personalizado a diferentes ubicaciones (archivos, correos electrónicos, etc.).

Vea este artículo para una introducción.


Según tengo entendido, una clase contenedora para log4net sería una clase estática que se encarga de inicializar el objeto de registro desde app.config / web.config o mediante código (por ejemplo, integración con NUnit).


un posible uso para un contenedor de log4net podría ser una clase que obtiene la clase y el método de llamada a través de la reflexión para tener una idea de dónde ocurrió su entrada de registro. al menos yo uso esto con frecuencia.