プログラムを作成する際に、地味ですが重要な作業の1つとしてログの出力部分の作成があります。ログの出力が適切でないと、トラブルが発生した場合にその原因をつかむのが難しくなります。ログの出力が適切であれば、問題の原因を特定することが容易になるでしょう。
また、システムの処理時間をログの要所に入れておけば、プロファイラなどを使わなくても、ボトルネックがある程度解析できます。レスポンスタイムなどのシステム状態の統計を取っておけば、問題の把握などにも役立つでしょう。本稿では、そんな縁の下の力持ちともいえるログの出力機能を提供するCommonsプロジェクトのLoggingとlog4jについて紹介します。
Loggingは、log4jやJ2SDK 1.4のLogging APIなど、複数のログAPIへのラッパーとして利用できるライブラリです。Loggingを利用すると、次のようなメリットがあります。
ログ実装には、前述のJ2SDK 1.4から導入されたLogging APIのほかに、Jakartaでもlog4jやLogkitなどを提供しています。どれを利用するかは、一長一短であり悩める問題ですが、取りあえずLoggingを利用して実装しておけば、実際にログを出力する実装は後で別のものに切り替えることができるのが利点です。
ある「アプリケーションA」がlog4jを利用していて、その「アプリケーションA」のログと自分で作成したアプリケーションのログを同じログファイルに統一された形式で出力したいとします。Loggingを利用すれば、ログ出力の実装にlog4jを利用するように設定するだけで、統一された形式のログを出力することが可能となります。また、「アプリケーションB」がJDK 1.4のログAPIを利用している場合でも、簡単にJ2SDK 1.4のAPIに合わせることができます(図1)。
上図では、ライブラリのログをアプリケーションに合わせていますが、もちろん別のファイルに出力することもできます。
さて、早速ですが、Loggingの使い方をサンプルと一緒に実際に見てみましょう。Loggingを利用したサンプルをリスト1に示します。
リスト1 LogTest.java |
import org.apache.commons.logging.*; |
リスト1を実行してみましょう。Loggingをhttp://jakarta.apache.org/site/binindex.htmlからダウンロードします。ここでは、バージョン1.0.3を利用しました。次に、ダウンロードしたファイルからcommons-logging.jarを取り出し、次のようにして実行します。
> javac -classpath
commons-logging.jar:. LogTest.java |
J2SDK 1.4を利用する場合、Loggingによるログ出力の実装は、デフォルトとしてJ2SDK 1.4のログAPIが利用されます。そのため、上記のメッセージはJ2SDK 1.4のログAPIにより出力されたものとなっています。
より具体的に使い方を見ていきましょう。
public class LogTest { |
ここで、LogFactoryのgetLogメソッドにログを埋め込むクラスを指定することに注意してください。これにより、このLogインスタンスはLogTestというクラスに対応づけられ、log4jやJ2SDK 1.4のログAPIを利用する際、ログの出力単位をパッケージ名やクラス名で指定できるようになります。このLogは、staticメンバ変数として取得しておくとよいでしょう。
ログを出力するには、前出のリスト1のように、fatal/error/warn/info/debug/traceを利用します。Exceptionのエラーメッセージとスタックトレースをログに出力したい場合は、ログ出力メソッドの後にExceptionのオブジェクトを指定します。
} catch (Exception e){ |
ログの出力には、fatalからtraceまで6段階のレベルを指定することができますが、実際に現場で開発する場合には、各レベルで何を出力するか表1のように決めておいた方がよいでしょう。
ログレベル | 用途 | |
---|---|---|
fatal | システムがこれ以上動作できない致命的なエラーが発生した場合 | |
error | 予期せぬ動作などにより、正しく処理できない場合 | |
warn | なんらかの問題が発生したが、動作には影響がない場合 | |
info | 設定ファイルを読み込んだときの設定情報、システム開始/停止時の状態出力など | |
debug | デバッグ情報。例えば、外部モジュールから呼ばれるメソッドの入出力情報など | |
trace | 詳細なデバッグ情報。モジュール内部の情報、ループの繰り返しで大量に出力される情報など | |
ログ出力メソッドが頻繁に呼び出されたり、メソッド内で文字列操作を行ったり、ループで何度もログを出力するとオーバーヘッドが発生します。例えば、次のようなコーディングは避けるべきです。
log.debug("パラメータ="+param); for(int i=0; i< args.length;i++){ |
このコーディングでは、ログレベルをinfoなどにした場合でも、"パラメータ="+paramの部分で毎回文字列の結合が発生し、ループ内の処理も毎回実行されるなど、パフォーマンスの低下が懸念されます。isDebugEnable()などのメソッドを利用すると、指定したログレベルが有効かどうか確認することができます。これを利用して、次のようにコーディングするとよいでしょう。
if(log.isDebugEnable()){ if(log.isTraceEnable()){ |
さて、Loggingでログの出力方法が分かったところで、次にlog4jを利用してログの出力を制御してみましょう。ここでは、Loggingとlog4jを一緒に使うことを前提に紹介します。log4jを利用することにより、次のようなことが可能になります。
J2SDK 1.4からはログAPIが標準で含まれているので、log4jの存在を疑問視する人もいますが、安定性を求めるシステムなどで、J2SDK 1.3を利用する場合には、log4jが有用ですし、将来的にJ2SDK 1.4を利用するにしても、log4jを使えばいままでのノウハウをそのまま流用できます。また、log4c(C)/log4cpp(C++)/log4net(.NET)など、ほかの処理系に対する実装も用意されているので、別の言語を利用する場合でも応用できます。
Loggingに含まれるcommons-logging.jarとhttp://jakarta.apache.org/site/binindex.htmlからlog4j(ここではjakarta-log4j-1.2.8.zip)をダウンロードし、中のdest/libディレクトリに含まれるlog4j-1.2.8.jarを取り出しておきます。
次に、log4jの設定ファイルlog4j.xmlを用意します。
リスト2 log4j.xml(文字コードはutf-8で保存) |
<?xml version="1.0" encoding="UTF-8" ?>
<!-- すべてのログをinfo --> |
さて、実行してみましょう。リスト1、リスト2、commons-loggin.jar、log4j-1.2.8.jar、そして先ほどコンパイルしたLogTest.classを同一ディレクトリ上に配置します。実行結果は以下のとおりです。
> java -classpath
commons-logging.jar;log4j-1.2.8.jar;. LogTest |
画面に表示されたログ以外に、daily-log.txtファイルにもログが出力されます。実行時の注意として、commons-logging.jarとlog4j-1.2.8.jarが%JAVA_HOME%\jre\lib\extにインストールされているとカレントディレクトリのlog4j.xmlが読み込まれず正しく動作しません。動作がおかしい場合は、extディレクトリを調べて、これらのjarがインストールされているようであれば、削除してください。
さて、log4jを動かせるようになったところで、log4j.xmlの記述方法を見ていきましょう。log4jでは、次の3つの設定を行うことによりさまざまなログの出力形式を指定することができます。
それでは、アペンダ、レイアウト、カテゴリについて説明しましょう。
<appender>要素で、ログの出力対象を決めるアペンダを指定します。ログの出力対象には、コンソール、ファイル、データベース、メール、JMS、Syslogなどがあります。アペンダの設定は次のように行います。
<appender name="定義名" class="アペンダクラス"
> |
パラメータ名では、出力するファイル名、データベースのURL、メールアドレスなど、ログの設定に関する情報を設定します。すべてのアペンダで共通に利用できるパラメータとしては、thresholdがあります。このパラメータは、debug/info/warn/error/fatalを取り、デフォルトのログの出力レベル(log4jのマニュアルでは優先度(Priority)と呼ばれていますがここではLoggingに合わせてレベルと呼びます)を制御できます。この値は、あくまでデフォルト値であり、後で説明するカテゴリの設定で特定のクラスやパッケージに対する設定を変更することができます。なお、log4jにはTraceのログレベルはなく、LoggingのTraceはDebugに対応づけられています。アペンダにはさまざまなものが用意されていますが、代表的なものを表2に示します。
アペンダ | 機能 | |
---|---|---|
ConsoleAppender | コンソールにログを出力する | |
FileAppender | ファイルにログを出力する | |
DailyRollingFileAppender | ファイルにログを出力するが毎日/毎週といった、一定期間でログのバックアップを取り、新しいファイルにログを出力することができる | |
JDBCAppender | データベースにログを出力する | |
SMTPAppender | メールにログを出力する | |
なお、オリジナルのlog4j(1.2.8以前)では、SMTPAppenderで日本語を正しく扱うことができません。そこで、SMTPAppenderを日本語で扱えるようにしてみました。オリジナルのlog4j-1.2.8.jarの代わりにlog4j-1.2.8-ja.jarを用いて、パラメータに以下を追加してください。
<param name="Charset" value="ISO-2022-JP"
/> |
<appender>要素の中で<layout>要素を指定してログの出力形式を決めるレイアウトを指定します。あらかじめ用意されているPatternLayoutを利用すると、さまざまなフォーマットのログを簡単に出力できます。PatternLayoutは、次のようにAppenderタグの中で指定します。
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"
> |
上記のCoversionPatternパラメータのvalue属性でフォーマットを決めます。上記の例では、以下のような形式になります。
ログのフォーマットに利用できる文字を表3に示します。
変数 | 説明 | |
---|---|---|
%c | カテゴリ名。%c{n}で表示するカテゴリ階層を指定する。例えば、a.b.cというカテゴリに対して、%c{2}はb.cを出力する | |
%d | 日付。HHを時間、mmを分、ssを秒、SSSをミリ秒としたとき、%d{HH:mm:ss,SSS}, %d{dd MMM yyyy HH:mm:ss,SSS}という形式で指定できる。また%d{ISO8601},%d{ABSOLUTE}という形式でも指定可能 | |
%L | 行番号 | |
%m | メッセージ | |
%n | 改行。ログメッセージの最後に挿入 | |
%p | 優先度(fatal/error/warn/info/debug) | |
%r | アプリケーションの実行開始からの経過時間 | |
%t | スレッド名。マルチスレッドのプログラムの場合、利用を勧める | |
また、「%4p」など、数字を入れることで最大表示文字数を4文字に抑制したり、「%-p」とマイナスを入れることで左ぞろえにしたりすることができます。「%.5p」とピリオドを用いると、最小文字数を指定(足りない部分は空白で埋める)こともできます。
カテゴリは、ログの設定単位です。Loggingの解説でログの初期化を次のように行うと書きましたが、以下のコードによってクラス名がカテゴリとして設定されます。
public class LogTest {
|
通常はjp.co.xxx.MyClassのようなパッケージ階層を伴ったクラス名だと考えてください。このカテゴリにより、特定のパッケージや特定のクラスのログの出力レベルの設定をアペンダごとに変更することができます。
すべてのカテゴリに対するログを設定するには、次のように<root>要素で設定します。
<root> |
この例では、<appender>要素で定義したSTDOUTアペンダとDAILYアペンダのすべてのカテゴリに対してinfoレベルでログを出力します。
また、特定のカテゴリとアペンダを指定してログレベルを設定するには、<category>要素を利用します。
なお、<category>要素は、<root>要素の前に定義します。カテゴリの設定例をリスト3に示します。リスト3の設定により、図2のように各アペンダのカテゴリに対してログレベルが設定されます。
リスト3 <category>要素 |
<!-- STDOUTアペンダに対してossbook.log2以下のレベルをdebugに設定 --> |
Loggingとlog4j、2つのログツールの概要を紹介してきましたが、いかがだったでしょうか?これらのツールをうまく利用することにより、ログのハンドリングを快適に行うことができるようになります。log4jについてはすでに利用されている方も多いと思いますが、すでに利用されている方にも参考になるように今回はTips的な内容を加えてみました。本稿が皆さまの参考になれば幸いです。
岡本隆史(おかもと たかし)
岡山大学工学研究科修了後、(株)NTTデータに入社。文字認識ソフトウェアの研究開発を経て、Webサービス関連の研究開発に携わる。個人では、Debian GNU/Linuxの優れたメンテナンス性とほかのディストリビューションを圧倒するパッケージ数に引かれDebianを使い始めたのをきっかけに、Debianプロジェクトの開発者となる。DebianプロジェクトのStefan Gybas、Ola Lundqvistらとともに、Javaサポートの強化を行う。Jakartaに関しては、Tomcat/JMeter/ORO/Luceneなどの国際化/日本語へのローカライズ、AntのKaffe VM対応などを行っている。『Jakartaプロジェクト徹底攻略』(技術評論社)、『WEB+DB PRESS』(技術評論社)、『Java World』(IDGジャパン)、『JAVA Developer』(ソフトバンクパブリッシング)などで執筆活動を行っている。
Ja-Jakarta Projectについて
Ja-Jakartaプロジェクトでは、Jakartaプロジェクトのドキュメントの和訳やプロダクトの国際化/日本語化などを行っている。現在、プロジェクトのメンバーを募集中。Ja-Jakartaプロジェクトの活動に参加しようという方は、「Ja-Jakartaプロジェクトへの参加方法」(http://www.ingrid.org/jajakarta/site/getinvolved.html)を参照。
Copyright © ITmedia, Inc. All Rights Reserved.