ぺーぺーSEのブログ

備忘録・メモ用サイト。

log4jメモ

log4j

commons-loggingとlog4jを使用する場合は下記の設定ファイルをクラスパス配下に置いておく。
■commons-logging.properties

org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger

log4jの設定は「log4j.properties」に行う。
※もしくは「log4j.xml」にも可能だが、ここでは省略。

log4jの設定は、

  • Category(かつてはLoggerだったらしい)
  • Appender
  • Layout

からなる。

Category

Category名はソースの中で下記のように指定する。(かつてはLoggerだったらしい)

// commons-logging
Log log = LogFactory.getLog("[Category名]"); 

// log4j
Logger log = Logger.getLogger("[Category名]"); 

ソースの中で指定したCategory名に対して、ログレベルとAppenderを下記のようにlog4j.propertiesで設定する。

# RootCategory(全てのCategory対する設定)(かつてはlog4j.rootLoggerという書きっぷりだったらしい)
log4j.rootCategory=[ログレベル],[appenderName1],[appenderName2], ...

# Category(かつてはlog4j.loggerという書きっぷりだったらしい)
log4j.category.[Category名]=[ログレベル|INHERITED], [appenderName1], [appenderName2], ...
# INHERITED と書くと、RootCategoryと同じレベルになる。

また、Categoryには下記のように階層構造を持つことができる。

  • Root
    • jp
      • co
        • hogehoge
          • ClassA
          • ClassB
      • hoge
        • ClassC
    • org
      • fuge
        • ClassD

このため、Category名の指定を下記のように行うと、パッケージ単位でログレベル、Appenderの設定が可能となる。

// commons-logging
Log log = LogFactory.getLog(ClassA.class); 

// log4j
Logger log = Logger.getLogger(ClassA.class); 

例えば、上記のCategory階層(パッケージ階層)の例だと、下記のように設定するとClassAとClassBはログレベルにDEBUG、AppenderにA1とA2が設定される。

# Logger
log4j.category.jp.co.hogehoge=DEBUG, A1, A2
はまりポイント

下記のようにrootCategoryと下位の階層のCategoryに同じAppenderを定義した場合、同じログが複数回出力されてしまう。

log4j.rootCategory=DEBUG, A1
log4j.category.jp.co.hogehoge=DEBUG, A1
log4j.category.jp.co.hogehoge.ClassA=DEBUG, A1

上記のように定義するとrootCategoryはjp.co.hogehogeもjp.co.hogehoge.ClassAの両方のCategoryを、jp.co.hogehogeはjp.co.hogehoge.ClassAのCategoryも含むため、ClassAで出力されるログはA1のAppenderに対して同じログを3回出力することになる。
なので、上位のCategoryのログ、Appenderの定義を引き継がないように下記のように記述を追加する必要がある。

log4j.additivity.jp.co.hogehoge=false
log4j.additivity.jp.co.hogehoge.ClassA=false

上記の定義を追加することで、jp.co.hogehogeとjp.co.hogehoge.ClassAのCategoryは上位のCategoryのログ、Appdenderの定義を引き継がず、ClassAで出力されるログは1つとなる。

Appender

Categoryの設定のところで名前だけ設定したAppenderの設定を行う。
使用するAppenderとそのオプションの値を設定することができる。

log4j.appender.[Appender名]=[Appenderクラス]
log4j.appender.[Appender名].[オプション]=[設定値]

■例(Appender名A1をコンソール出力で設定)

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.Target=System.out

■Appenderクラス

  • org.apache.log4j.ConsoleAppender
    • コンソールにログ出力する
  • org.apache.log4j.FileAppender
    • ファイルにログ出力する
  • org.apache.log4j.DailyRollingFileAppender
    • ファイルにログ出力する。日次でログローテーションする
  • org.apache.log4j.RollingFileAppender
    • ファイルにログ出力する。一定サイズでログローテーションする。
  • org.apache.log4j.net.JMSAppender JMSにログ出力する。
  • org.apache.log4j.nt.NTEventLogAppender
    • Windowsイベントログにログ出力する
  • org.apache.log4j.net.SMTPAppender
    • メールにログ出力する。
  • org.apache.log4j.net.SocketAppender
    • ソケットにログ出力する。
  • org.apache.log4j.net.SyslogAppender
    • Syslogにログ出力する。
  • org.apache.log4j.AsyncAppender
    • 非同期ログ出力
  • org.apache.log4j.jdbc.JDBCAppender
    • DBへログを出力する

■設定項目

  • DatePattern
    • 日付出力パターンの設定
    • 例)'.'yyyy-MM-dd
  • Append
    • 追加でファイル書き込みする設定
    • デフォルトfalse
  • File
    • 出力ファイルパス
  • BufferedIO
    • trueを指定した場合、バッファリング
    • デフォルトfalse
  • BufferSize
    • バッファリングする場合のバッファサイズを指定
  • Encoding
  • Threshold
    • ”INFO”を指定すると、INFO以下のログは出力せずに無視する

Layout

Appenderに対して出力フォーマットの設定を行う。

log4j.appender.[Appender名].layout=[Layoutクラス]
log4j.appender.[Appender名].layout.[オプション]=[設定値]

■例(Appender名A1のレイアウトを設定)

log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%d{yyyy/MM/dd HH:mm:ss.SSS}][%-5p][%t][%c{1}] %m%n

■レイアウトクラス

  • org.apache.log4j.TTCCLayout
    • 時間、スレッド名、カテゴリ名、メッセージを出力
  • org.apache.log4j.HTMLLayout
    • HTML形式で出力
  • org.apache.log4j.XMLLayout
    • XML形式で出力
  • org.apache.log4j.PatternLayout
    • ユーザでレイアウトを指定

設定例

log4j.properties

log4j.rootCategory=INFO, CONSOLE, FILE
log4j.category.jp.co.hogehoge=DEBUG, CONSOLE
log4j.category.jp.co.hogehoge.ClassA=DEBUG, FILE
log4j.additivity.jp.co.hogehoge=false
log4j.additivity.jp.co.hogehoge.ClassA=false

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n

log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd
log4j.appender.FILE.Append=true
log4j.appender.FILE.File=/log/hoge.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n

log4j.xml(上記log4j.propertiesと同じ設定)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n" />
    </layout>
  </appender>

  <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="DatePattern" value="'.'yyyy-MM-dd" />
    <param name="Append" value="true" />
    <param name="File" value="/log/hoge.log" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n" />
    </layout>
  </appender>
  
  <category name="jp.co.hogehoge" additivity="false">
    <priority value="DEBUG" />
    <appender-ref ref="CONSOLE" />
  </category>
  
  <category name="jp.co.hogehoge.ClassA" additivity="false">
    <priority value="DEBUG" />
    <appender-ref ref="FILE" />
  </category>
  
  <root additivity="false">
    <priority value="INFO" />
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
  </root>

</log4j:configuration>

log4j.xml独自設定

■org.apache.log4j.AsyncAppender
通常のアペンダクラスは、ログ出力は完了復帰タイプで、ログ出力指示をして処理が完了するまで、後続処理を実行しない。
しかし、アペンダクラス「org.apache.log4j.AsyncAppender」を使用すると、ログ出力指示だけして、処理終了を待たずに、後続処理が実行できるようになる。
つまり非同期でログ出力処理ができるようになる。
使い方は通常通り設定したAppenderに対してさらにappender-refでラッピングする形で書く。
■例

<appender name="FILE" class="rg.apache.log4j.DailyRollingFileAppender">
  <param name="DatePattern" value="'.'yyyy-MM-dd" />
  <param name="Append" value="true" />
  <param name="File" value="/log/hoge.log" />
  <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n" />
  </layout>
</appender>

<appender name="AsyncFILE">
  <appender-ref ref="FILE" />
</appender>

ソースの書き方

Log log = LogFactory.getLog(ClassA.class); 
if(log.isDebugEnabled()){
  log.debug("DEBUGレベルのログメッセージ");
}
if(log.isInfoEnabled()){
  log.info("INFOレベルのログメッセージ");
}

MDC(Mapped Diagnostic Context)

log4jの設定ファイルでlayoutのConversionPatternにて「"[%d{yyyy/MM/dd HH:mm:ss}][%-5p][%t][%c{1}] %m%n"」のようにメッセージのパターンを設定する。
「%X{key}」というパターンがあった場合、MDCを使用してメッセージを埋め込むことができる。
MDCは難しそうだが、ただのMapと考えてしまえばよい。
例えば

%X{hoge}

というパターンがあった場合

MDC.put("hoge", "ほげ");

ソースコードに記述するだけで「%X{hoge}」が「ほげ」に置き換わる。

ログレベル

ログレベル 出力項目 出力内容
TRACE 性能ログ 処理の開始・終了時間(ms)
DEBUG デバッグログ 任意
INFO アクセスログ
通信ログ
サービスへのアクセス時刻、ユーザID、サービスを判別できるID(クラス名、メソッド名等)
WARN 業務エラーログ 入力情報、例外情報
ERROR システムエラーログ
監視ログ
入力情報、システムエラー情報、監視ログは極力一行で

※本番環境ではINFO以上を想定

参考:
http://www.techscore.com/tech/Java/ApacheJakarta/Log4J/index/
http://iihito.dip.jp/docs/java/no15/log-9.html
http://struts.wasureppoi.com/util/05_category.html
http://nobuit.blog56.fc2.com/?mode=m&no=115
http://www.nurs.or.jp/~sug/soft/log4j/log4j10.htm
http://www.nurs.or.jp/~sug/soft/log4j/log4j3.htm
http://d.hatena.ne.jp/torutk/20110429/p1
http://struts.wasureppoi.com/util/05_async.html


log4jに関連する話題

ログをDBへ出力したい場合

AppenderにJDBCAppenderを使用する。
しかし、DBのコネクションプールに対応していないため性能問題に発展するケースが多発すると思われる。
log4j 1.3では、コネクションプールに対応しているかわからないがDBAppenderというものになるようだ。

動的にlog4jの設定を変更したい場合

アプリケーションが起動している間にもlog4jの設定変更は可能だ。
例えば、ログレベルの変更は下記のようにソース中で変更できる。

// ルートカテゴリのログレベルの変更
Logger rootLogger = LogManager.getRootLogger();
rootLogger.setLevel(Level.INFO);

// 他カテゴリのログレベルの変更
Logger logger = LogManager.getLogger(ClassA.class);
logger.setLevel(Level.INFO);

さらにlog4jにはjmxから設定変更が可能のようだ。
「org.apache.log4j.jmx」パッケージにMBeanが提供されている。。。

log4jdbc

発行したSQLをログとして出力したいケースがある。
その場合、log4jdbcが使える。

参考:
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=26852&forum=12