2011-12-13 14 views
5

私たちは多くの異なるアプリケーションからインスタンス化されたプレーンな古いJavaライブラリを持っています。この場合、各アプリケーションは、すべて同じTomcatコンテナーに存在するWebアプリケーションです。ライブラリのインスタンスごとに別々のロガー

各アプリケーションは、独自のロガーを使用して独自のログファイルにログを記録します。特定のアプリケーションに関連するライブラリによって生成されたログは、そのアプリケーションの別のログファイルにも移動します。

library = new library(Logger applicationsVeryOwnLogger); 

をそして、ライブラリ内のすべての文をログに記録する、そのロガーを使用します。このため

は、一つの方法は、アプリケーションがライブラリにそのロガーに渡すことができるようにすることです。しかし、これは、ロガーがライブラリ内のクラス変数になり、ライブラリ内のすべてのクラスが正しいロガーを使用するためにライブラリへの参照を必要とすることを意味します。

これを行うより良い方法はありますか?

答えて

2

これまでのアプリケーションで同様のニーズがありました。私たちが思いついた解決策は、リソース(Logger、Configファイルなど)をClassLoader(コンテキスト)で取得するResourceManagerでした。

通常、EARとしてデプロイされたアプリケーションはすべて独自のClassLoaderを取得し、ライブラリはResourceManager.getLogger()を呼び出して、現在のスレッド/アプリケーションに関連付けられたLoggerを取得できます。そうすれば、ライブラリ内のすべてのメソッド呼び出しで渡す必要はありません(ライブラリを変更する必要があります)。

Logger logger = Logger.getLogger([Application Logger Name]); 
ResourceManager.registerLogger(logger); 

は、ライブラリ内のロガーを取得する(ユーティリティメソッド):

private Logger getLogger() 
    { 
     return ResourceManager.getLogger();  
    } 
EJB/WebAppのの初期化フェーズで

import java.util.*; 
import java.util.logging.*; 

public class ResourceManager 
{ 
    private static final Map<ClassLoader, Map<String, Object>> resources = 
     Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, Object>>()); 
    public static final String LOGGER = Logger.class.getName(); 

    static 
    { 
     // adjust for log4j or other frameworks 
     final Logger logger = Logger.getLogger("logging.default"); 
     logger.setLevel(Level.ALL); 
     logger.addHandler(new ConsoleHandler() 
     { 
      { 
       setOutputStream(System.out); 
       setLevel(Level.ALL); 
      } 
     }); 
     registerResource(null, LOGGER, logger); 
    } 

    private static ClassLoader getApplicationScope() 
    { 
     return Thread.currentThread().getContextClassLoader(); 
    } 

    public static void registerResource(final String name, final Object resource) 
    { 
     registerResource(getApplicationScope(), name, resource); 
    } 

    public static synchronized void registerResource(final ClassLoader scope, final String name, final Object resource) 
    { 
     Map<String, Object> hm = null; 
     hm = resources.get(scope); 
     if (hm == null) 
     { 
      hm = Collections.synchronizedMap(new HashMap<String, Object>()); 
      resources.put(scope, hm); 
     } 
     hm.put(name, resource); 
    } 

    public static Object getResource(final String name) 
    { 
     for(ClassLoader scope = getApplicationScope();;scope = scope.getParent()) 
     { 
      final Map<String, Object> hm = resources.get(scope); 
      if ((hm != null) && hm.containsKey(name)) 
      { 
       return hm.get(name); 
      } 
      if (scope == null) break; 
     } 
     return null; 
    } 

    public static void registerLogger(final Logger logger) 
    { 
     registerResource(LOGGER, logger); 
    } 

    public static Logger getLogger() 
    { 
     return (Logger)getResource(LOGGER); 
    }  
} 

登録ロガー(のgetLoggerへの呼び出しの前に登録する必要があります)

これは、現在のスレッドに関連付けられているアプリケーション(EAR)のロガーを返します。

Loggerに限らず、共有したい他のリソースにも使用できます。

制限:デプロイEAR

  • のResourceManagerとロギングライブラリごとに複数のアプリケーション/ EJBをパッケージ化する場合

    • 文句を言わない仕事は、同じまたはライブラリとアプリケーションのより高いクラスローダにする必要があります。バンドルするオプションがある場合、Alexandersのアプローチはよりクリーンです。 (私たちはjava.util.loggingをデフォルトでサーバレベルで使用しているので、彼のアプローチは機能しません)

  • +0

    注釈として、このソリューションは、ロガーだけでなく、あらゆる種類のリソースを共有するためにも使用できます。 – Stefan

    +0

    あなたの答えにコードの関連セクションを貼り付け、将来リンクが消滅する場合があります。 – rouble

    +0

    このメソッドについてもう1つ注意する点は、ライブラリがインスタンス化される前にResourceManager.registerLogger()を呼び出さなければならないことです。それ以外の場合、クラスロガーはすべてデフォルトロガーを使用して設定されます。これは、いくつかの実装では、ディール・ブレーカになる可能性があります。 – rouble

    3

    log4jタグで質問をマークしました。使用していると思われます。

    あなたのライブラリが一意のパッケージ名を使用していることを希望します。

    この場合、実際にはそのパッケージのロガーをセットアップするだけで済みます。

    log4j.category.my.lib.package = INFO, libFileAppender 
    log4j.rootLogger = INFO, rootFileAppender 
    

    両方libFileAppenderrootFileAppenderにあなたのライブラリーからのメッセージをログに記録されてしまいます。

    あなたはロガーの加法は、このようにすることをオフにしてrootFileAppenderに表示されるように、あなたのライブラリーからのメッセージにしたくない場合は:これで

    log4j.category.my.lib.package = INFO, libFileAppender 
    log4j.additivity.my.lib.package = false 
    log4j.rootLogger = INFO, rootFileAppender 
    

    、あなただけlibFileAppenderにメッセージが表示されます。

    +0

    これは要件を満たしているとは限りません。これにより、ライブラリのすべてのログを1か所にリダイレクトできます。ただし、特定のアプリケーションによって生成されたライブラリーの各ログ(アプリケーションのログ・ファイル)を1つの場所に移動する必要があります。 – rouble

    +0

    @prmatta。このファイルをWEB-INF/classesに投げ込んで、そのアプリケーション固有のセットアップアペンダーを呼び出すと、各アプリケーションはそのアペンダーへのライブラリー呼び出しを記録します。私はそれがあなたの要求を満たしていないかどうかはわかりません。 –

    +0

    JVMレベルで管理されているlog4jのロガー/アペンダーを終了しますか?また、すべてのロガー(ライブラリに名前が必要なために必要なもの)に同じ名前を使用すると、同じロガーを登録したままになります。 – Stefan

    関連する問題