Pregunta

Tengo una clase de registro que requiere que se la llame desde casi cualquier lugar de la aplicación.

Sin embargo, requiere configuración al principio de la aplicación con "qué ruta escribir", "nivel de registro" y si está "habilitado" o no.

No quiero dar estos parámetros cada vez ni pasar la clase Logging como parámetro a cada objeto en mi aplicación, por lo que uso un patrón singleton para iniciar sesión.

Recientemente he sufrido mucho por clases estrechamente acopladas. No quiero volver a cometer el mismo error, pero después de pensarlo parece que esta es la única buena solución.

ACTUALIZAR :

Realmente no me importa registrar lo que me importa es resolver problemas de diseño similares, Tengo el mismo dilema con otro objeto de configuración global que debe usarse en tantas clases.Pero inyectarlo en cada uno de ellos genera una sobrecarga horrible y un código menos legible.

¿Qué opinas sobre esta implementación y qué haces cuando te encuentras con decisiones de diseño similares?

PDNo sugiera algo como "usar la biblioteca Log4X", etc.

¿Fue útil?

Solución

En primer lugar - se puede escribir la escritura del registro como un escucha de seguimiento, y el uso de Trace.Write etc de los métodos

?

¿Usted realmente necesita una instancia aquí? Eso sería útil, por ejemplo, si se quería abstracta como un TextWriter o similar - pero si va a ser un producto único independiente, pueden los métodos no utilizar métodos estáticos directamente, es decir Log.Write(...) (en lugar de pasar en una instancia de registro )?

Re el problema general - que depende de los tipos que están haciendo el registro. Para las clases (etc) "manager", es posible considerar el uso de la inyección de dependencias (Unidad, StructureMap, etc) para automatizar esto. No me suelen utilizar inyección con dtos, sin embargo.

Otros consejos

Incluso si usted no quiere sugerencias de "usar Log4X" (aunque no se dice exactamente ¿Por qué que quieren reinventar la rueda), parecería sensato buscar en las decisiones de diseño realizados por diversas bibliotecas de registro.

En mi experiencia los problemas de acoplamiento fuerte no son tan relevantes cuando se aplica a la explotación forestal - en particular, rara vez quiero probar el lado de la tala de mi aplicación, y no me importa si se registra a la consola durante la unidad pruebas.

En resumen, el patrón "normal" de:

private static readonly Logger log = LogManager.GetLogger(...);

(con cambios de nombres deseados, etc.) es estéticamente poco atractiva en el uso de métodos estáticos, pero funciona bastante bien en la práctica. Al menos, esa ha sido mi experiencia.

Puede utilizar probablemente un producto único aquí. Vas a tener una estrecha conexión entre todas las clases de la aplicación y de la clase de logger pero si la clase de logger y la clase configuración mundial realmente se necesitan en cada clase esto puede ser aceptable.

Yo personalmente uso clase estática en tal caso. La clase tiene campos estáticos de configuración (para experimentar el manual), además de algunas funciones para poblar usando configurtion de la sección de archivos .config adecuado.

Este es, en efecto, bastante regular mucho más cerca de lo que usted tiene con DI como se puede "inyectar" nueva configuración. Para cambiar la configuración para el nuevo modelo, acabo de cambio en el campo archivo .config que mantiene la sección de configuración "activa".

Esto es fácil de usar, fácil de mantener, y todo el mundo lo entiende ... No veo ningún inconveniente particular de ella ...

En realidad, el registro y la configuración se manejan de dos maneras diferentes, por lo que, si entendí correctamente, su pregunta real estaba más relacionada con el manejo de la configuración global entre ensamblajes.

Con respecto al registro, las cosas están bastante claras: usar un Singleton global para eso es común, aunque vincula estrechamente sus bibliotecas con la biblioteca de registros.Usar oyentes Trace es una solución aún mejor en mi humilde opinión.

Pero cuando se habla de la configuración de la aplicación, sin duda deberías evitar haciéndolos globales.Mantenga todas las configuraciones relacionadas con la aplicación ubicadas en un solo lugar (aquellas que deben persistir), pero no disponibles estáticamente para otras bibliotecas.Por lo tanto, pasar la configuración adecuada a otros ensamblados tendrá que ser responsabilidad de quien llama, no al revés.

Es esto ASP.Net? Si es así, puede utilizar el evento de error en el Global.asax.

En sus muchas dependencias, ¿ha considerado el uso de un marco de inyección de dependencias?

Actualizar

No estoy seguro de la implicación de rendimiento ni la forma de ejecución correspondiente es a su aplicación, pero este marco se ve interesante: PostSharp , Un blog acerca que .

También puede ser capaz de aprovechar la atributo condicional .

Si utiliza PostSharp, estaría interesado cómo funciona.

Una cosa que podría investigar es el paquete por característica. Se afirma que, después de esta técnica Alivia algunos problemas que resultan en alto acoplamiento entre clases. Más específicamente significaría que habría solamente una clase en todas las características de su aplicación con la responsabilidad de hablar con el proveedor de configuración (que bien podría ser parte de una configuración / configuración característica en sí / instalación). El nivel de acoplamiento está todavía en el lado de alta ish, sino porque está bien definida que debe ser manejable.

Algo similar:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using log4net.Config;
using log4net.Appender;
using System.Reflection;
using System.IO;
using System.Globalization;
using log4net.Core;
using System.Web;

namespace GenApp.Utils
{
  ///<summary> Wrapper around log4net with dynamically adjustable verbosity</summary>
  public class Logger
  {

    private static Logger inst = new Logger ();
    public static Logger Inst ()
    {
      inst.ConfigureLogging ();
      return inst;
    }


    public enum DebugLevel : int
    {
      Fatal_Msgs = 0,
      Fatal_Error_Msgs = 1,
      Fatal_Error_Warn_Msgs = 2,
      Fatal_Error_Warn_Info_Msgs = 3,
      Fatal_Error_Warn_Info_Debug_Msgs = 4
    }

    public static void Debug ( GenApp.Bo.User objUser, ILog logger, string msg )
    {
      DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
      string strLogLevel = Logger.GetLogTypeString ( debugLevel );
      inst.SetLogingLevel ( strLogLevel );
      logger.Debug ( msg );

    } //eof method 


    public static void Info ( GenApp.Bo.User objUser, ILog logger, string msg )
    {
      DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
      string strLogLevel = Logger.GetLogTypeString ( debugLevel );
      inst.SetLogingLevel ( strLogLevel );
      logger.Info ( msg );

    } //eof method 


    public static void Warn ( GenApp.Bo.User objUser, ILog logger, string msg )
    {
      DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
      string strLogLevel = Logger.GetLogTypeString ( debugLevel );
      inst.SetLogingLevel ( strLogLevel );
      logger.Warn ( msg );

    } //eof method 


    public static void Error ( GenApp.Bo.User objUser, ILog logger, string msg )
    {
      DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
      string strLogLevel = Logger.GetLogTypeString ( debugLevel );
      inst.SetLogingLevel ( strLogLevel );
      logger.Error ( msg );
    } //eof method 


    public static void Fatal ( GenApp.Bo.User objUser, ILog logger, string msg )
    {
      DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
      string strLogLevel = Logger.GetLogTypeString ( debugLevel );
      inst.SetLogingLevel ( strLogLevel );
      logger.Fatal ( msg );
    } //eof method 


    /// <summary>
    /// Activates debug level 
    /// </summary>
    /// <sourceurl>http://geekswithblogs.net/rakker/archive/2007/08/22/114900.aspx</sourceurl>
    private void SetLogingLevel ( string strLogLevel )
    {

      this.ConfigureLogging ();
      string strChecker = "WARN_INFO_DEBUG_ERROR_FATAL";

      if (String.IsNullOrEmpty ( strLogLevel ) == true || strChecker.Contains ( strLogLevel ) == false)
        throw new ArgumentOutOfRangeException ( " The strLogLevel should be set to WARN , INFO , DEBUG ," );



      log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories ();

      //Configure all loggers to be at the debug level.
      foreach (log4net.Repository.ILoggerRepository repository in repositories)
      {
        repository.Threshold = repository.LevelMap[strLogLevel];
        log4net.Repository.Hierarchy.Hierarchy hier = (log4net.Repository.Hierarchy.Hierarchy)repository;
        log4net.Core.ILogger[] loggers = hier.GetCurrentLoggers ();
        foreach (log4net.Core.ILogger logger in loggers)
        {
          ( (log4net.Repository.Hierarchy.Logger)logger ).Level = hier.LevelMap[strLogLevel];
        }
      }

      //Configure the root logger.
      log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository ();
      log4net.Repository.Hierarchy.Logger rootLogger = h.Root;
      rootLogger.Level = h.LevelMap[strLogLevel];
    }

    ///<summary>
    ///0 -- prints only FATAL messages 
    ///1 -- prints FATAL and ERROR messages 
    ///2 -- prints FATAL , ERROR and WARN messages 
    ///3 -- prints FATAL  , ERROR , WARN and INFO messages 
    ///4 -- prints FATAL  , ERROR , WARN , INFO and DEBUG messages 
    ///</summary>
    private static string GetLogTypeString ( DebugLevel debugLevel )
    {

      string srtLogLevel = String.Empty;
      switch (debugLevel)
      {
        case DebugLevel.Fatal_Msgs:
          srtLogLevel = "FATAL";
          break;
        case DebugLevel.Fatal_Error_Msgs:
          srtLogLevel = "ERROR";
          break;
        case DebugLevel.Fatal_Error_Warn_Msgs:
          srtLogLevel = "WARN";
          break;
        case DebugLevel.Fatal_Error_Warn_Info_Msgs:
          srtLogLevel = "INFO";
          break;
        case DebugLevel.Fatal_Error_Warn_Info_Debug_Msgs:
          srtLogLevel = "DEBUG";
          break;
        default:
          srtLogLevel = "FATAL";
          break;
      } //eof switch
      return srtLogLevel;

    } //eof GetLogTypeString


    /// <summary>
    /// The path where the configuration is read from.
    /// This value is set upon a call to ConfigureLogging().
    /// </summary>
    private string configurationFilePath;
    public void ConfigureLogging ()
    {
      lock (this)
      {
        bool configured = false;


        #region ConfigureByThePathOfTheEntryAssembly
        // Tells the logging system the correct path.
        Assembly a = Assembly.GetEntryAssembly ();

        if (a != null && a.Location != null)
        {
          string path = a.Location + ".config";

          if (File.Exists ( path ))
          {
            log4net.Config.DOMConfigurator.Configure (
              new FileInfo ( path ) );
            configurationFilePath = path;
            configured = true;
          }
          else
          {
            path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
            if (File.Exists ( path ))
            {
              log4net.Config.DOMConfigurator.Configure (
                new FileInfo ( path ) );
              configurationFilePath = path;
              configured = true;
            }
          }
        }
        #endregion ConfigureByThePathOfTheEntryAssembly


        #region ConfigureByWeb.config
        // Also, try web.config.
        if (!configured)
        {
          if (HttpContext.Current != null &&
            HttpContext.Current.Server != null &&
            HttpContext.Current.Request != null)
          {
            string path = HttpContext.Current.Server.MapPath (
              HttpContext.Current.Request.ApplicationPath );

            path = path.TrimEnd ( '\\' ) + "\\Web.config";

            if (File.Exists ( path ))
            {
              log4net.Config.DOMConfigurator.Configure (
                new FileInfo ( path ) );
              configurationFilePath = path;
              configured = true;
            }
          }
        }
        #endregion ConfigureByWeb.config


        #region ConfigureByThePathOfTheExecutingAssembly
        if (!configured)
        {
          // Tells the logging system the correct path.
          a = Assembly.GetExecutingAssembly ();

          if (a != null && a.Location != null)
          {
            string path = a.Location + ".config";

            if (File.Exists ( path ))
            {
              log4net.Config.DOMConfigurator.Configure (
                new FileInfo ( path ) );
              configurationFilePath = path;
              configured = true;
            }
            else
            {
              path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
              if (File.Exists ( path ))
              {
                log4net.Config.DOMConfigurator.Configure (
                  new FileInfo ( path ) );
                configurationFilePath = path;
                configured = true;
              }
            }
          }
        }
        #endregion ConfigureByThePathOfTheExecutingAssembly


        #region ConfigureByThePathOfTheCallingAssembly
        if (!configured)
        {
          // Tells the logging system the correct path.
          a = Assembly.GetCallingAssembly ();

          if (a != null && a.Location != null)
          {
            string path = a.Location + ".config";

            if (File.Exists ( path ))
            {
              log4net.Config.DOMConfigurator.Configure (
                new FileInfo ( path ) );
              configurationFilePath = path;
              configured = true;
            }
            else
            {
              path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
              if (File.Exists ( path ))
              {
                log4net.Config.DOMConfigurator.Configure (
                  new FileInfo ( path ) );
                configurationFilePath = path;
                configured = true;
              }
            }
          }
        }
        #endregion ConfigureByThePathOfTheCallingAssembly


        #region ConfigureByThePathOfTheLibIsStored
        if (!configured)
        {
          // Look in the path where this library is stored.
          a = Assembly.GetAssembly ( typeof ( Logger ) );

          if (a != null && a.Location != null)
          {
            string path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
            if (File.Exists ( path ))
            {
              log4net.Config.DOMConfigurator.Configure (
                new FileInfo ( path ) );
              configurationFilePath = path;
              configured = true;
            }
          }
        }
        #endregion ConfigureByThePathOfTheLibIsStored



      } //eof lock   
    } //eof method 



    /// <summary>
    /// Searches for a configuration file in the given path.
    /// </summary>
    private string FindConfigInPath (
      string path )
    {
      string[] files = Directory.GetFiles ( path );

      if (files != null && files.Length > 0)
      {
        foreach (string file in files)
        {
          if (Path.GetExtension ( file ).Trim ( '.' ).ToLower (
            CultureInfo.CurrentCulture ) == "config")
          {
            return file;
          }
        }
      }

      // Not found.
      return string.Empty;
    } //eof method 



    /// <summary>
    /// Remove dynamically appenders
    /// </summary>
    /// <param name="appenderName"></param>
    /// <param name="threshold"></param>
    public static void SetThreshold ( string appenderName, Level threshold )
    {
      foreach (AppenderSkeleton appender in LogManager.GetRepository ().GetAppenders ())
      {
        if (appender.Name == appenderName)
        {
          appender.Threshold = threshold;
          appender.ActivateOptions ();

          break;
        }
      }
    } //eof method 



  } //eof class 


} //eof namespace 

Si siempre está escribiendo con la misma fuente, entonces usted puede utilizar el patrón Singleton.

Pero si se registra información en diferentes fuentes para, por ejemplo, en el archivo o registro de eventos a continuación, crear diferentes instancia de la clase de registro de configuración diferente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top