8

アプリケーションのほぼすべての場所から呼び出す必要があるロギング クラスがあります。

ただし、アプリケーションの最初に、「書き込むパス」、「ログ レベル」、および「有効」かどうかを設定する必要があります。

毎回このパラメーターを指定したり、Logging クラスをパラメーターとしてアプリケーション内のすべてのオブジェクトに渡したりしたくないので、ロギングにはシングルトン パターンを使用します。

最近、密結合クラスに悩まされています。同じ間違いを繰り返したくありませんが、これについて考えると、これが唯一の良い解決策のように思えます。

アップデート :

同様の設計上の問題を解決することを気にしているものをログに記録することはあまり気にしません。非常に多くのクラスから使用する必要がある別のグローバル設定オブジェクトで同じジレンマを抱えています。しかし、それらのすべてにそれを注入すると、オーバーヘッドがひどくなり、コードが読みにくくなります。

この実装についてどう思いますか? また、同様の設計上の決定に遭遇した場合はどうしますか?

PS「Log4Xライブラリを使用する」などの提案はしないでください。

4

9 に答える 9

4

まず、ログ ライターをトレース リスナーとして記述Trace.Writeし、メソッドから etc を使用できますか?

ここで実際にインスタンスが必要ですか? たとえば、または同様のものとして抽象化したい場合に便利です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

ログと設定は実際には 2 つの異なる方法で処理されるため、正しく理解できていれば、実際の質問はアセンブリ間のグローバル設定の処理に関連していました。

ロギングに関しては、物事は非常に明確です。そのためにグローバルなシングルトンを使用するのが一般的ですが、ライブラリをログ ライブラリに緊密に結合します。Traceリスナーを使用することは、IMHOのさらに優れたソリューションです。

ただし、アプリケーションの設定について話すときは、それらをグローバルにすることは絶対に避けてください。すべてのアプリケーション関連の設定を 1 か所 (永続化する必要がある場所) のみに配置し、他のライブラリで静的に使用できないようにします。したがって、適切な設定を他のアセンブリに渡すことは、呼び出し元の責任であり、その逆ではありません。

于 2009-04-28T07:51:49.443 に答える
1

そのような場合、私は個人的に静的クラスを使用します。このクラスには、静的構成フィールド (手動実験用) と、適切な .config ファイル セクションからの構成を使用してそれらを設定するためのいくつかの関数があります。

これは、新しい構成を「注入」できるため、実際にはDIで得られるものに非常に近いものです。構成を新しいモデルに変更するには、「アクティブな」構成セクションを保持する .config ファイル フィールドを変更するだけです。

これは使いやすく、維持しやすく、誰もがそれを理解しています...特に欠点は見当たりません...

于 2009-04-28T08:23:47.977 に答える
0

調査できることの1つは、機能ごとのパッケージです。この手法に従うと、クラス間の高い結合をもたらすいくつかの問題が軽減されると主張されています。より具体的には、構成プロバイダー(構成/セットアップ/インストール機能自体の一部である可能性があります)と通信する責任を持つ、アプリケーションのすべての機能に1つのクラスしかないことを意味します。結合のレベルはまだ高い側にありますが、明確に定義されているため、管理可能である必要があります。

于 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

常に同じソースに書き込む場合は、Singleton パターンを使用できます。

ただし、ファイルやイベント ログなど、別のソースに情報を記録する場合は、別の構成に対して別のログ クラスのインスタンスを作成します。

于 2012-09-11T21:09:30.560 に答える
0

これは ASP.Net ですか? その場合は、Global.asax でエラー イベントを使用できます。

多くの依存関係について、依存性注入フレームワークの使用を検討しましたか?

アップデート

パフォーマンスへの影響やパフォーマンスがアプリにどの程度関連しているかはわかりませんが、このフレームワークは興味深いようです: PostSharpそれに関するブログ投稿.

また、 Conditional 属性を活用できる場合もあります。

PostSharp を使用している場合は、どのように機能するかに興味があります。

于 2009-04-28T07:22:45.567 に答える