[基本機能]ログ

アプリケーション開発において、動作確認、障害検知などの要件でログを出力する必要があります。
ログと言えば、The Apache Jakarta ProjectのCommons LoggingやLog4Jなどが有名です。 それらのロギングの概念は、クライアント側が出力するメッセージを決め、メッセージの出力レベルに合ったメソッドを呼び出すと言うものです。
以下にその実装例を示します。

  1. Logger logger = Logger.getInstance(this.getClass());
  2. logger.info("Information Level Message");
  3. logger.error("Error Level Message");

このロギングのインタフェースは、非常にシンプルで使いやすい事は分かります。
しかし、実際のエンタープライズなアプリケーションにおいて、運用・保守まで考えてロギングを行う場合、このようなインタフェースでは、以下のような問題が生じます。

  • コードにメッセージが埋め込まれるため、メッセージの変更が容易に行えない。
  • 使用されているメッセージがコード中に分散し、管理しにくいため、重複したメッセージを使用したり、無駄なメッセージを出力していたりし易い。
  • メッセージと出力レベルがインタフェースレベルで決定するので、メッセージと出力レベルの関係が強く、変更しにくい。

Nimbusのロギングの概念は、メッセージには、文言や出力レベル、出力先など様々な付加情報があり、それを意識するのは、ロギングを行うアプリケーションではないと言うものです。
そのため、Nimbusのロギングのインタフェースでは、メッセージに一意に付与されるIDを渡して、出力を依頼するというだけです。 そのインタフェースには、出力レベルどころか、メッセージすら現れません。
それらの付加情報は、別の場所で定義するのです。付加情報を外出しにする事で、メッセージの管理や、出力レベル・出力先の変更など、メッセージに付加される情報の変更が、アプリケーションに影響を及ぼさなくなります。
このように、一意なメッセージIDのみでログの出力を依頼する簡潔な機能を抽象化したインタフェースが、jp.ossc.nimbus.service.log.Loggerです。

ログは、[基本機能]メッセージと[基本機能]出力が前提となります。

関連するパッケージは、以下です。

アプリケーション向けインタフェース Logger

アプリケーション向けインタフェースLoggerを使った簡単なアプリケーションのサンプルを示します。

  1. import jp.ossc.nimbus.core.ServiceManagerFactory;
  2. import jp.ossc.nimbus.service.log.Logger;
  3. Logger logger = (Logger)ServiceManagerFactory.getServiceObject("Logger");
  4. // メッセージIDのみで、メッセージをログ出力する
  5. logger.write("MSG_0001");
  6. // メッセージIDと埋め込みパラメータを指定して、埋め込みメッセージをログ出力する
  7. logger.write("MSG_0002", new Object[]{"hoge", new Integer(100)});
  8. // メッセージIDと例外を指定して、メッセージとスタックトレースをログ出力する
  9. logger.write("MSG_0003", new Exception("test"));

実装サービスの一覧は以下のとおりです。

実装サービス実装概要
jp.ossc.nimbus.service.log.LogServiceLoggerのデフォルト実装サービス
jp.ossc.nimbus.service.log.DefaultCommonsLogFactoryServicejp.ossc.nimbus.service.log.LogServiceの拡張サービスで、Jakarta Commons LoggingのLoggerとしても動作するサービス

ログ機能は、Nimbus自体にとっても必須機能であるため、Nimbus自体が内部にデフォルトのLoggerを持っています。
このLoggerは、内部で生成しているため、自由な設定はできません。設定可能なのは、ログレベルの出力設定のみです。その設定は、サービス定義で以下のように設定します。

  1. <?xml version="1.0" encoding="Shift_JIS"?>
  2. <!DOCTYPE server PUBLIC
  3. "-//Nimbus//DTD Nimbus 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/nimbus-service_1_0.dtd">
  5. <server>
  6. <default-log>
  7. <debug output="false"/>
  8. <information output="false"/>
  9. <warning output="true"/>
  10. <error output="true"/>
  11. <fatal output="true"/>
  12. </default-log>

このデフォルトのLoggerは、以下のように取得できます。

  1. import jp.ossc.nimbus.core.ServiceManagerFactory;
  2. import jp.ossc.nimbus.service.log.Logger;
  3. Logger logger = ServiceManagerFactory.getLogger();

デフォルトLogger自体に設定を行う事は、前述の通りログレベルの出力設定のみですが、デフォルトLoggerを差し替える事で、任意のLoggerを使用する事ができます。
デフォルトLoggerを差し替えるには、サービス定義で以下のように設定します。

  1. <?xml version="1.0" encoding="Shift_JIS"?>
  2. <!DOCTYPE server PUBLIC
  3. "-//Nimbus//DTD Nimbus 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/nimbus-service_1_0.dtd">
  5. <server>
  6. <log>Logger</log>
  7. <manager>
  8. <!-- ユーザ定義の任意のLoggerサービス -->
  9. <service name="Logger"
  10. code="jp.ossc.nimbus.service.log.LogService">

ログ機能は、サービスの実装においても必須機能となります。
サービスを実装する場合に、jp.ossc.nimbus.core.ServiceBaseを継承すると、デフォルトLoggerを以下のように使用する事ができます。

  1. import jp.ossc.nimbus.core.ServiceBase;
  2. import jp.ossc.nimbus.service.log.Logger;
  3. public class SampleService extends ServiceBase{
  4. public void startService() throws Exception{
  5. Logger logger = getLogger();

上記のServiceBaseを継承したサービスから取得できるLoggerは、サービス毎に差し替える事もでき、以下のように定義します。

  1. <?xml version="1.0" encoding="Shift_JIS"?>
  2. <!DOCTYPE server PUBLIC
  3. "-//Nimbus//DTD Nimbus 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/nimbus-service_1_0.dtd">
  5. <server>
  6. <manager>
  7. <!-- ServiceBaseを継承したサービス -->
  8. <service name="Sample"
  9. code="SampleService">
  10. <!-- Loggerサービスのサービス名を指定する。 -->
  11. <attribute name="SystemLoggerServiceName">#Logger</attribute>
  12. <depends>Logger</depends>
  13. </service>
  14. <!-- ユーザ定義の任意のLoggerサービス -->
  15. <service name="Logger"
  16. code="jp.ossc.nimbus.service.log.LogService">

また、特定のサービスマネージャ配下の全てのServiceBaseを継承したサービスのLoggerを差し替える事もでき、以下のように定義します。

  1. <?xml version="1.0" encoding="Shift_JIS"?>
  2. <!DOCTYPE server PUBLIC
  3. "-//Nimbus//DTD Nimbus 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/nimbus-service_1_0.dtd">
  5. <server>
  6. <manager>
  7. <log>Logger</log>
  8. <!-- ServiceBaseを継承したサービス -->
  9. <service name="Sample1"
  10. code="SampleService"/>
  11. <!-- ServiceBaseを継承したサービス -->
  12. <service name="Sample2"
  13. code="SampleService"/>
  14. <!-- ユーザ定義の任意のLoggerサービス -->
  15. <service name="Logger"
  16. code="jp.ossc.nimbus.service.log.LogService">

Logger向けインタフェース LogMessageRecord

インタフェースLogMessageRecordは、LoggerがメッセージIDから、メッセージ、出力レベル、出力カテゴリ、スタックトレースの出力有無を決定するために必要な、MessageRecordの拡張インタフェースです。
実装クラスはjp.ossc.nimbus.service.message.MessageRecordImplのサブクラスjp.ossc.nimbus.service.log.LogMessageRecordImplで、メッセージID、メッセージに加え、出力レベル、出力カテゴリ、スタックトレースの出力有無を定義できます。

出力レベルは、数値で表現します。LogCategoryにて、出力する出力レベルの範囲を設定して、出力を制御します。また、出力レベルを出力する際に、出力レベル数値の範囲に対して出力レベルのラベル(DEBUG、INFOといった文字列)をマッピングして出力します。出力レベル数値に範囲を持たせているのは、出力レベルを後で細分化したくなった場合の変更容易性を確保するためです。
出力カテゴリは、文字列で表現します。LogCategoryに、出力カテゴリ名を持たせる事で、特定のメッセージをどのカテゴリに出力するかを制御する事ができます。
スタックトレースの出力有無は、true/falseで表現します。Logger.write(java.lang.String, java.lang.Throwable)などで例外を渡して出力依頼された場合でも、falseを設定すると、スタックトレースが出力されなくなります。この項目は、省略可能で、デフォルトはtrueです。

ログメッセージ定義は、以下のようなフォーマットです。

メッセージID,メッセージ,ログレベル,ログカテゴリ,スタックトレース出力フラグ

以下に簡単なログメッセージ定義を示します。

MyMessage.def

APL_001,Good morning!,0,jp.ossc.nimbus.service.log.SYSTEM_DEBUG_CATEGORY
APL_002,Good afternoon\\, @0!,50,jp.ossc.nimbus.service.log.SYSTEM_INFO_CATEGORY
APL_003,Good evening\\, @0 and @1!,100,jp.ossc.nimbus.service.log.SYSTEM_WARN_CATEGORY
APL_004,Good night\\, #0 and @1!,150,jp.ossc.nimbus.service.log.SYSTEM_ERROR_CATEGORY
APL_005,Bye @0!,200,jp.ossc.nimbus.service.log.SYSTEM_FATAL_CATEGORY
APL_006,Bye @0!,200,jp.ossc.nimbus.service.log.SYSTEM_FATAL_CATEGORY,false

MessageRecordFactoryServiceに、LogMessageRecordImplを適用させるには、その属性setMessageRecordClassName(java.lang.String)に、LogMessageRecordImplのクラス名を指定します。
以下に簡単なサービス定義を示します。

  1. <?xml version="1.0" encoding="Shift_JIS"?>
  2. <!DOCTYPE server PUBLIC
  3. "-//Nimbus//DTD Nimbus 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/nimbus-service_1_0.dtd">
  5. <server>
  6. <manager>
  7. <!-- メッセージを提供するMessageRecordFactoryサービス -->
  8. <service name="MessageRecordFactory"
  9. code="jp.ossc.nimbus.service.message.MessageRecordFactoryService">
  10. <!-- MessageRecordインタフェースの実装クラス名を指定する -->
  11. <attribute name="MessageRecordClassName">jp.ossc.nimbus.service.log.LogMessageRecordImpl</attribute>
  12. <!-- メッセージ定義ファイルを指定する -->
  13. <attribute name="MessageFiles">MyMessage</attribute>
  14. </service>
  15. </manager>
  16. </server>

Logger向けインタフェース LogCategory

インタフェースLogCategoryは、LoggerがメッセージIDから、LogMessageRecordを特定し、そこからどのカテゴリにどのような出力レベルで出力するかを判別し、出力を依頼するためのCategoryの拡張インタフェースです。

カテゴリ名、出力レベルの有効範囲、出力レベルとラベルのマッピングを持ち、入力をフォーマットして出力する機能を持ちます。

実装サービスの一覧は以下のとおりです。

実装サービス実装概要
jp.ossc.nimbus.service.log.SimpleCategoryServicejp.ossc.nimbus.service.writer.SimpleCategoryServiceの拡張サービス。カテゴリ名、出力レベルとラベルのマッピング、出力レベルの有効範囲を定義できる。
jp.ossc.nimbus.service.log.GroupCategoryServicejp.ossc.nimbus.service.writer.GroupCategoryServiceの拡張サービス。カテゴリ名、出力レベルとラベルのマッピングを定義できる。出力レベルの有効範囲は、グルーピングするそれぞれのLogCategoryで定義する。

LogCategoryは、インタフェースWritableRecordFactoryの実装サービスを必要としますが、ログの出力項目に沿ったWritableRecordへの変換を行うjp.ossc.nimbus.service.log.LogWritableRecordFactoryServiceを使用します。

サンプルは、以下。