web-dev-qa-db-ja.com

スタンドアロンクラスライブラリアセンブリのlog4netをどのように構成して有効にしますか?

バックグラウンド

私はC#.NET 3.5でクラスライブラリアセンブリを書いています。これは、サードパーティの市販品(COTS)ツールを含む他のアプリケーションとの統合に使用されます。したがって、このクラスライブラリは、私が制御するアプリケーション(EXE)によって呼び出されることもあれば、他のDLLまたはnot制御を行うアプリケーションによって呼び出されることもあります。

仮定

  • C#3.0、.NET 3.5 SP1、およびVisual Studio 2008 SP1を使用しています
  • Log4net 1.2.10.0以降を使用しています

制約

解決策は次のとおりです。

  • 呼び出し元のアプリケーションがlog4netを構成していない場合は、クラスライブラリが独自の構成ファイルを介してロギングを有効にして構成できるようにします。
  • クラスライブラリがlog4net情報を指定している場合は、呼び出し側のアプリケーション構成を介してロギングを有効にして構成できるようにする

OR

  • クラスライブラリが独自の構成ファイルを常に使用してログを有効にして構成できるようにします。

問題

スタンドアロンクラスライブラリがDLLまたは制御していないアプリケーション(サードパーティのCOTSツールなど)によって呼び出され、log4net構成情報を指定していない場合、私のクラスはライブラリはログの記録を行うことができません。


質問

スタンドアロンクラスライブラリアセンブリのlog4netを構成して有効にして、呼び出し元のアプリケーションがlog4net構成を提供しているかどうかに関係なくログを記録できるようにする方法

31
Zach Burlingame

解決策1

最初の制約セットの解決策は、基本的に log4net.LogManagerJacobJeroen 、および-のような独自のカスタムLogManagerクラスにラップすることです McWafflestix が提案しています(以下のコードを参照)。

残念ながら、 log4net.LogManager クラスは静的であり、C#は静的継承をサポートしていないため、単純に継承してGetLoggerメソッドをオーバーライドすることはできません。 log4net.LogManager クラスにはあまり多くのメソッドがないため、これは確かに可能性があります。

このソリューションのもう1つの欠点は、既存のコードベース(私はこれを行う)がある場合、log4net.LogManagerへの既存の呼び出しをすべてラッパークラスに置き換える必要があることです。しかし、今日のリファクタリングツールではそれほど重要ではありません。

私のプロジェクトでは、これらの欠点が、呼び出し元のアプリケーションによって提供されるロギング構成を使用する利点を上回っていたため、ソリューション2を採用しました。

コード

まず、LogManagerラッパークラスが必要です。

using System;
using System.IO;
using log4net;
using log4net.Config;

namespace MyApplication.Logging
{
    //// TODO: Implement the additional GetLogger method signatures and log4net.LogManager methods that are not seen below.
    public static class LogManagerWrapper
    {
        private static readonly string LOG_CONFIG_FILE= @"path\to\log4net.config";

        public static ILog GetLogger(Type type)
        {
            // If no loggers have been created, load our own.
            if(LogManager.GetCurrentLoggers().Length == 0)
            {
                LoadConfig();
            }
            return LogManager.GetLogger(type);
        }

        private void LoadConfig()
        {
           //// TODO: Do exception handling for File access issues and supply sane defaults if it's unavailable.   
           XmlConfigurator.ConfigureAndWatch(new FileInfo(LOG_CONFIG_FILE));
        }              
}

次に、クラスの代わりに:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

使用する:

private static readonly ILog log = LogManagerWrapper.GetLogger(typeof(MyApp));

解決策2

私の目的のために、2番目の制約のセットを満たすソリューションを選択することにしました。私の解決策については、以下のコードを参照してください。

Apache log4netドキュメント から:

"アセンブリは、デフォルトのリポジトリではなく、名前付きのログリポジトリを使用することを選択できます。これにより、アセンブリのログがアプリケーションの残りの部分から完全に分離されます。これは、コンポーネント開発者にとって非常に役立ちます。コンポーネントにlog4netを使用したいが、コンポーネントを使用するすべてのアプリケーションがlog4netを認識している必要はありません。これは、デバッグ構成がアプリケーション構成から分離されていることも意味します。リポジトリ。」

コード

クラスライブラリのAssemblyInfo.csファイルに次の行を配置しました。

// Log4Net configuration file location
[Assembly: log4net.Config.Repository("CompanyName.IntegrationLibName")]
[Assembly: log4net.Config.XmlConfigurator(ConfigFile = "CompanyName.IntegrationLibName.config", Watch = true)]

参考文献

12
Zach Burlingame

あなたはおそらく XmlConfigurator クラスの周りに何かをコーディングすることができます:

public static class MyLogManager
{
    // for illustration, you should configure this somewhere else...
    private static string configFile = @"path\to\log4net.config";

    public static ILog GetLogger(Type type)
    {
        if(log4net.LogManager.GetCurrentLoggers().Length == 0)
        {
            // load logger config with XmlConfigurator
            log4net.Config.XmlConfigurator.Configure(configFile);
        }
        return LogManager.GetLogger(type);
    }
}

次に、クラスの代わりに:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

使用する:

private static readonly ILog log = MyLogManager.GetLogger(typeof(MyApp));

もちろん、このクラスをサービスにして、選択したIoCコンテナーで動的に構成するのが望ましいでしょうが、そのアイデアはわかりますか?

EDIT:修正されたCount()の問題がコメントで指摘されました。

12
Jacob

あなたのコードでは、ロガーが存在するかどうかを確認できます

log4net.LogManager.GetCurrentLoggers().Count()

次に、たとえばXmlConfiguratorを使用して、ファイルからデフォルト構成をロードします。

log4net.Config.XmlConfigurator.Configure(configFile)

静的または通常のコンストラクターで初期化を行うことができます。

class Sample
{
    private static readonly log4net.ILog LOG;

    static Sample()
    {
        if (log4net.LogManager.GetCurrentLoggers().Count() == 0)
        {
            loadConfig();
        }
        LOG = log4net.LogManager.GetLogger(typeof(Sample));

    }

    private static void loadConfig()
    {
        /* Load your config file here */
    }

    public void YourMethod()
    {
       LOG.Info("Your messages");
    }
}
7
Jeroen Huinink

スタンドアロンクラスライブラリに、log4netを使用してlog4net.Config.XmlConfigurator構成ファイルをロードするシングルトンがあります。

具体的には、独自のカスタムロギングクラスを使用するようにすべてのコードを定義できます。このクラスは、log4netロギング呼び出しの単純なラッパーであり、1つ追加されています。記録したいログ情報を含む静的メンバーを作成します。そのクラスの静的コンストラクターでXmlConfiguratorを呼び出してそれを初期化します。それだけです。

1
Paul Sonier

あなたはここで良い説明を見つけることができます: log4net:クイックスタートガイド

記事で説明しているように、各アセンブリを個別に構成するには、_AssemblyName.dll.log4net_という名前のアセンブリ用のXMLファイルを作成し、次のXMLコードを配置します。

_<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
   <appender name="XmlSchemaFileAppender" type="log4net.Appender.FileAppender">
      <file value="AppLog.xml" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.XmlLayout" />
   </appender>

   <root>
      <level value="WARN" />
      <appender-ref ref="XmlSchemaFileAppender" />
   </root>
</log4net>
_

さらに、新しいロガーをインスタンス化するために、クラス全体の変数として次のように宣言することも説明しています。

_public class LogSample
{
    private static readonly log4net.ILog Log 
            = log4net.LogManager.GetLogger(typeof(LogSample));
    // Class Methods Go Here...
}
_

次に、クラス内で次のようにプライベート変数Logを使用できます。

_Log.Info("Sample message");
_

同様に、Log.Error("Error occurred while running myMethod", ex)を使用して、例外の詳細とともにエラーをログに記録できます。

私が見つけたものは次のとおりです:

  • 設定を有効にするためにlog4net.Config.XmlConfigurator.Configure();を呼び出すことを忘れないでください

  • 書き込まれたファイルのパスを知る必要がある場合、 ここ Log4Netから取得するためのコード

これがお役に立てば幸いです。

1
Matt