8

我有一个日志类,需要从应用程序中的几乎所有地方调用。

但是,它需要在应用程序的开头设置“要写入的路径”、“日志级别”以及是否“启用”。

我不想每次都给这个参数或将 Logging 类作为参数传递给我的应用程序中的每个对象,所以我确实使用单例模式进行日志记录。

最近我从紧密耦合的类中遭受了很多痛苦,我不想再犯同样的错误,但考虑到这听起来像是唯一的好解决方案。

更新 :

我并不真正关心记录我关心的是解决类似的设计问题,我与另一个需要从这么多类中使用的全局设置对象有同样的困境。但是将它注入到它们中的每一个只会产生可怕的开销和可读性较差的代码。

你如何看待这个实现,当你遇到类似的设计决策时你会怎么做?

PS请不要提出“使用Log4X库”之类的建议。

4

9 回答 9

4

首先-您可以将日志编写器编写为跟踪侦听器,并Trace.Write从方法中使用等吗?

你真的需要一个实例吗?这将很有用,例如,如果你想将它抽象为一个TextWriter或类似的 - 但如果它是一个独立的单例,这些方法可以不直接使用静态方法,即Log.Write(...)(而不是传递一个日志实例)?

关于一般问题 - 它取决于正在执行日志记录的类型。对于“管理器”(等)类,您可以考虑使用依赖注入(Unity、StructureMap 等)来自动执行此操作。不过,我通常不会将注入与 DTO 一起使用。

于 2009-04-28T07:18:15.297 回答
3

即使您不想要“使用 Log4X”的建议(尽管您没有确切说明为什么要重新发明轮子),查看各种日志库做出的设计决策似乎是明智的。

以我的经验,紧密耦合的问题在应用于日志记录时并不那么相关——特别是,我很少想测试我的应用程序的日志记录端,而且我不介意它是否在单元测试期间记录到控制台。

简而言之,“正常”模式:

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

(通过适当的名称更改等)在使用静态方法时在美学上没有吸引力,但在实践中效果很好。至少,这是我的经验。

于 2009-04-28T07:21:58.827 回答
2

您可能可以在这里使用单例。您将在应用程序中的每个类和记录器类之间紧密耦合,但是如果每个类都确实需要记录器类和全局设置类,这是可以接受的。

于 2009-04-28T07:30:02.723 回答
1

日志记录和设置实际上以两种不同的方式处理,所以如果我理解正确,您的实际问题与处理程序集之间的全局设置更相关。

关于日志记录,事情很清楚 - 使用全局单例是很常见的,尽管它确实将您的库与日志库紧密耦合。恕我直言,使用跟踪侦听器是一个更好的解决方案。

但是在谈论应用程序设置时,您当然应该避免将它们设为全局。将所有与应用程序相关的设置仅保存在一个地方(那些应该被持久化的地方),而不是静态地可供其他库使用。因此,将适当的设置传递给其他程序集必须是调用者的责任,反之亦然。

于 2009-04-28T07:51:49.443 回答
1

在这种情况下,我个人使用静态类。该类具有静态配置字段(用于手动试验)以及一些使用相应 .config 文件部分的配置来填充它们的功能。

这实际上与 DI 非常接近,因为您可以“注入”新配置。要将配置更改为新模型,我只需更改保持“活动”配置部分的 .config 文件字段。

这易于使用,易于维护,并且每个人都理解它......我看不出它有什么特别的缺点......

于 2009-04-28T08:23:47.977 回答
0

这是 ASP.Net 吗?如果是这样,您可以使用 Global.asax 中的错误事件。

对于您的许多依赖项,您是否考虑过使用依赖注入框架?

更新

我不确定性能影响或性能与您的应用程序的相关性,但这个框架看起来很有趣:PostSharp一篇关于它的博客文章

您还可以利用Conditional 属性

如果您使用 PostSharp,我会对它的工作原理感兴趣。

于 2009-04-28T07:22:45.567 回答
0

您可以调查的一件事是按功能打包。据称,遵循这种技术可以缓解一些导致类之间高度耦合的问题。更具体地说,这意味着在您的应用程序的每个功能中只有一个类负责与配置提供者进行对话(这很可能是配置/设置/安装功能本身的一部分)。耦合程度仍然偏高,但由于它定义明确,它应该是可管理的。

于 2009-04-28T10:15:13.997 回答
0

相似的东西:

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 
于 2009-05-20T07:42:03.490 回答
0

如果你总是写到同一个源,那么你可以使用单例模式。

但是,如果您在不同的源上记录信息,例如在文件或事件日志中,则为不同的配置创建不同的日志记录类实例。

于 2012-09-11T21:09:30.560 回答