Saturday, October 26, 2013

log4j2にログを集める

log4j, slf4j, logback, commons-logging... こんがらがりませんか?僕はこんがらがりました。

ある実験的なWebアプリの開発の過程では、ログを最終的に何に集約するか?logbackか?それとも...?と、迷い、 結局log4j2に集約しました。これはそのメモみたいなものです。

なお、「slf4j+logbackのほうがナウでヤングだろ」といったご意見はスルーです。好みとかポリシーの問題です。 また、いにしえより伝わるlog4j1.xではなく、log4j2.xっていうところもポイントです。 java.util.logging?えーっ?!却下。

1. 前提とか方針とか

  • 新規のJava-Webアプリを開発する。そのコード上で使うLoggerにはlog4j2を使う。
  • ベースはSpringMVCフレームワーク。spring自身は内部でcommons-loggingを使っているので、それもlog4j2に集められるように設定する。
  • Spring以外にもいろいろOSSライブラリを使うが、それが吐くログもできる限りlog4j2に集める。例えば、内部でslf4j-apiを使ってるOSSが最近はとても多い。
  • 設定はlog4j2.xmlに集める。とりあえずWebアプリ内部に同梱する。
  • ビルドツールはmavenを使う

2. mavenのpom.xml

dependenciesタグ内で先に書いてあるほうが優先されるので、ログ出力ライブラリは上のほうに書いておくのが無難です。log4j-coreがもちろんlog4j2の本体。 log4j-jclがcommons-loggingによる出力をlog4j2に中継させるやつ。log4j-slf4j-implはslf4jによる出力を(以下略)

    
        
            org.apache.logging.log4j
            log4j-core
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-jcl
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-slf4j-impl
            2.0-beta8
        

        ... other dependency...
    

maven使いじゃない人のために、これで実際どんなjarがビルドパスに入ってくれるのか、書いておきます。こんだけ。

log4j-core-2.0-beta8.jar
log4j-api-2.0-beta8.jar
log4j-jcl-2.0-beta8.jar
commons-logging-1.1.1.jar
log4j-slf4j-impl-2.0-beta8.jar
slf4j-api-1.7.5.jar
slf4j-ext-1.7.5.jar
cal10n-api-0.7.4.jar

3. log4j2.xml

log4j2.xmlをsrc/main/resourcesに置く。root(要するにデフォルト値)を設定するほか、 ログ出力を制御したいクラス名、パッケージ名に対して出力レベルや出力先(appender)を設定します。

4. コードを書く

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Foo {
    private static Logger logger = LogManager.getLogger();
    public void bar(String a, String b) {
        logger.debug("a={},b={}", a, b);
    }
}

ログに変数をいれたいときに、logger.debug("a=" + a + ",b=" + b ) などとやってしまいがちですが、 実はこれ、ログ出力メソッドよりも先に+演算子による文字列連結の方が先に実行されますので、性能影響が出ます。 logger.debug()なんて本番環境ではオフにすると思いますが、それでも文字列連結は無駄に実行されてしまうのです。 どの程度の性能差が出るかは Performance - Apache Log4j 2 に詳しく解説されています。 せっかくlog4j2を使うのですから、{}記号によるプレースホルダ機能を使うようにしましょう。

また、log4j1.xのときは、
provate static Logger logger = Logger.getLogger(Foo.class);
のように、ロガー名として自クラスを明示的に与えてloggerを作る必要がありましたが、 log4j2のLogManagerでは省略できます。 log4j2のほうが好みな理由はこれが地味に便利だからかも。

Tuesday, October 1, 2013

Mixer2 as XHTML snippet generator

Mixer2 is the xhtml dedicated template engine. Because Mixer2 use JAXB (Java Architecture for XML Binding) internally, the usage is not only as a template engine, but also, Mixer2 can be used in another way.

Mixer2 has some different functionality.

  1. as a alternative to JSP, Velocity, and other template engine technology
  2. as a XHTML parser
  3. as as XHTML tag snippet generator

In thie post, I introduce "XHTML snippet generator" functionality of Mixer2.

You must fix up your html template as "full html" like "<html>...</html>" in order to use mixer2Engine.loadHtmlTemplate() .

But if you need to output xhtml tag using mixer2Engine.saveToString(), you can get any tag snippet as string. So, you can create some helper class like custom tag of JSP.

For example, If you must use jsp as the base template but also you want to use Mixer2, You can use them combine them.

If you create the new tag instance, use "new" the tag classes. Also you can use TagCreator.

Div div = TagCreator.div();
Span span = TagCreator.span();
span.getContent().add("Hello World !");
span.setStyle("color:red;");
div.getContent().add(span);
String result = mixer2Engine.saveToString(div);
System.out.println(result);

output:

Hello World !

If you create table tag, you can use TableBuilder.

String[][] data = new String[][]{
    // id, name, price
    {"1", "cookie", "2.50"},
    {"2", "candy",  "3.75"}
};

TableBuilder tBuilder = new TableBuilder();
for (String[] row : data) {
    tBuilder
        .addTr()
        .addTd(row[0])
        .addTd(row[1])
        .addTd(row[2]);
}
Table table = tBuilder.build();
String str = mixer2Engine.saveToString(table);
System.out.println(str);

output:

1 cookie 2.50
2 candy 3.75

For more detail of TableBuilder, see Mixer2 official site.

How about it ? Mixer2 and other template technology can exist together. Try Mixer2 !

XHTMLタグスニペット生成器としてのMixer2

Mixer2はXHTML専用のテンプレートエンジンです。 しかし、Mixer2が内部でJAXB(Java Architecture for XML Binding)を使用しているため、 単なるテンプレートエンジンにとどまらない使い方が可能です。
大きく分けて、Mixer2には次のような側面があります。
  1. JSPやVelocityに代わる技術としてのMixer2
  2. XHTMLパーサーとしてのMixer2
  3. XHTMLタグスニペット生成器としてのMixer2
この記事では、3番目の、XHTMLタグスニペット生成器としてのMixer2の使い方を紹介します。
Mixer2は、XHTMLをパース - loadHtmlTemplate() - する場合には フルHTML、つまり<html>...</html>までのHTML全体を与える必要があります。
しかしXHTMLタグを出力 - saveToString() - する場合には 任意のタグのみをString化することが可能です。 この特徴をうまく使えば、JSPのカスタムタグのようなものを 簡単なJavaコーディングのみで作ることができます。
たとえば、ベースとしてJSPを使いつつもMixer2を活用したい場合、下の図のような使い方が可能です。

タグを新規に作る場合は目的のタグ型クラスを new すればよいのですが、 TagCreatorクラスでも可能です。 TagCreatorのほうがeclipse等のIDE上でコード補完が効くので便利です。
Div div = TagCreator.div();
Span span = TagCreator.span();
span.getContent().add("Hello World !");
span.setStyle("color:red;");
div.getContent().add(span);
String result = mixer2Engine.saveToString(div);
System.out.println(result);
output:
Hello World !
table タグを作る場合にはTableBuilderクラスが便利です。
String[][] data = new String[][]{
    // id, name, price
    {"1", "cookie", "2.50"},
    {"2", "candy",  "3.75"}
};

TableBuilder tBuilder = new TableBuilder();
for (String[] row : data) {
    tBuilder
        .addTr()
        .addTd(row[0])
        .addTd(row[1])
        .addTd(row[2]);
}
Table table = tBuilder.build();
String str = mixer2Engine.saveToString(table);
System.out.println(str);
output:
1 cookie 2.50
2 candy 3.75
For more detail of TableBuilder, see Mixer2 official site.
いかがでしたか? できあがったタグオブジェクトをstring化したあとなら jspやvelocityに埋め込むこともできます。 Mixer2と他のテンプレートエンジンの共存は十分に可能です。 ぜひ組み合わせ技も考えてみてください。