S-JIS[2013-11-22/2019-06-14] 変更履歴

Asakusa Framework ロギング演算子

Asakusa FrameworkOperator DSLのロギング演算子(Logging)のメモ。


概要

ロギング演算子は、ログを出力する演算子。
性能特性はExtract(旧ドキュメントではMap)。[/2016-02-11]

入力
ポート数
入力データモデル
の制約
イメージ 出力
ポート数
出力データモデル
の制約
入力1レコード
に対する
出力レコード数
1   1 inと同じ
データモデル
1レコード。

ロギング演算子の実装としてログメッセージを返すようにする。
すると、レコード毎にログメッセージが出力される。

ログレベルは以下のものが指定できる。

ロギング演算子はレポートAPIを使ってログ出力しているので、ログの出力先はレポートAPIと同じ。
処理対象の全レコードに対してログ出力するとさすがにログの量が多くなって遅くなりそうなので、ある程度絞ったデータに対してロギング演算子を適用させるべきだろう。

なお、ロギング演算子は特別で、出力をどこにも接続しない場合は自動的に停止演算子(stop)に接続される。とJavadocに書いてある。


AsakusaFW 0.5.1では(テストドライバーに限るが)トレース機能が追加された。
これは、演算子のポートを指定して、そのポートで入出力されるデータモデルの全プロパティーをログ出力するもの。
どのポートでログ出力するのかは(テストドライバーの)実行時に指定するので、Flow DSL本体には手を入れる必要がないのが優れている。
(ロギング演算子の場合は、出力をやめようと思ったらFlow DSLを修正してロギング演算子を取り除かないといけない)


ロギング演算子のコーディング例。
(この図はToad Editorを用いて作っています)

example.dmdl(DMDL):

hoge = {
    value : INT;
};

ExampleOperator.java(Operator DSL):

import java.text.MessageFormat;

import com.asakusafw.vocabulary.operator.Logging;

import com.example.modelgen.dmdl.model.Hoge;
public abstract class ExampleOperator {

	/**
	 * エラー値を出力する。
	 * 
	 * @param in
	 *         データ
	 * @return ログメッセージ
	 */
	@Logging(Logging.Level.ERROR)
	public String error(Hoge in) {
		return MessageFormat.format("hoge.value = {0}", in.getValueOption());
	}
}

データモデルを引数に取り、ログメッセージを返すようにする。

@Loggingアノテーションの引数にはログレベルを指定する。
@Logging(value = Logging.Level.ERROR)」という書き方も可。
ログレベルを指定しなかった場合はINFOになる。

ExampleJob.java(Flow DSL):

import com.example.modelgen.dmdl.model.Hoge;

import com.example.operator.ExampleOperatorFactory;
import com.example.operator.ExampleOperatorFactory.Error;
	private final In<Hoge> in;

	private final Out<Hoge> out;
	@Override
	public void describe() {
		ExampleOperatorFactory operators = new ExampleOperatorFactory();

		// エラー値を出力する
		Error err = operators.error(this.in);

		this.out.add(err.out);
	}

Flow DSLでは、自分が作ったOperatorのFactoryクラス(AsakusaFWのコンパイラーによって生成される)を使用する。
メソッド名はOperatorクラスに書いたメソッド名と同じ。
戻り値の型はAsakusaFWのコンパイラーによって生成されたクラス。(メソッド名を先頭が大文字のキャメルケースに変換したもの)
出力ポートの名前のデフォルトはout。

出力ポート名を変えたい場合は@Loggingアノテーションで指定できる。
この場合、ログレベルは「value =」を使って指定する。

	@Logging(value = Logging.Level.ERROR, outputPort = "out")

単体テスト

ロギング演算子の単体テストの実装例。

ExampleOperatorTest.java:

package com.example.operator;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;

import com.example.modelgen.dmdl.model.Hoge;
/**
 * {@link ExampleOperator}のテスト.
 */
public class ExampleOperatorTest {

	@Test
	public void error() {
		ExampleOperator operator = new ExampleOperatorImpl();

		Hoge hoge = new Hoge();
		hoge.setValue(123);

		String message = operator.error(hoge);

		assertThat(message, is("hoge.value = 123"));
	}
}

Operatorのテストクラスは、通常のJavaのJUnitのテストケースクラスとして作成する。

テスト対象のOperatorクラス自身は抽象クラスだが、Operatorクラス名の末尾に「Impl」の付いた具象クラスがAsakusaFWによって生成されるので、それを使う。


値引数の例

ロギング演算子では、入力のデータモデルの他にも引数を指定することが出来る。
こうすると、引数の内容分だけ異なる同じ演算子(処理内容)をいくつかの場所で使用できるようになる。

先の例に値引数を追加する例。

ExampleOperator.java(Operator DSL):

	@Logging(Logging.Level.ERROR)
	public String error(Hoge in, String title) {
		return MessageFormat.format("{0}: hoge.value = {1}", title, in.getValueOption());
	}

出力ポートを示す引数以降を通常のJavaメソッドと同様に自由な引数とすることが出来る。(ただしプリミティブのみ)

ExampleJob.java(Flow DSL):

import com.example.modelgen.dmdl.model.Hoge;

import com.example.operator.ExampleOperatorFactory;
import com.example.operator.ExampleOperatorFactory.Error;
	private final In<Hoge> in;

	private final Out<Hoge> out;
	@Override
	public void describe() {
		ExampleOperatorFactory operators = new ExampleOperatorFactory();

		// エラー値を出力する
		Error err = operators.error(this.in, "エラー出力");

		this.out.add(err.out);
	}

演算子を使用するFlow DSLで、引数に具体的な値を指定する。


ExampleOperatorTest.java:

	@Test
	public void error() {
		ExampleOperator operator = new ExampleOperatorImpl();

		Hoge hoge = new Hoge();
		hoge.setValue(123);
		String title = "タイトル";

		String message = operator.error(hoge, title);

		assertThat(message, is("タイトル: hoge.value = 123"));
	}

全データモデルに対応する例

AsakusaFW 0.10.3以降では、型引数を指定すると、全データモデルを受け取るロギング演算子を作れる。[2019-06-14]

ExampleOperator.java:

	@Logging
	public <T> String log(T in) {
		return in.toString();
	}

実はAsakusaFWのドキュメントには以前から「例えば上限境界の無い型引数を定義して、引数の型として利用すると、 すべてのデータを受け取れるようなロギング演算子を定義することができます」と書かれていたのだが、実際には出来なかった(バグだった)らしい。
AsakusaFW 0.10.3で書けるようになった。


Operator DSLへ戻る / AsakusaFW目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま