Javaのログ
ライブラリの話
初心者編

Javaツール勉強会@福岡 2016/04

吉村 武志 (@takesi_yosimura)

## 自己紹介 吉村 武志 福岡周辺の勉強会に
ちょくちょく参加して
、 togetterまとめたりしてる人 Javaの勉強会やってますが、
仕事はClassic ASP & VB.Net 趣味は音ゲーとかアナログゲーム(ボードゲーム・TRPG)
# 先にお断り
### まともに使ってない人の発表です。 # ツッコミ求む!
## で、ログの中身の前に
## なぜログを使うんだっけ?

障害解析 ?

ズンドコキヨシ

あるかも・・・

監査対応 ?

個人情報漏洩ログ?

あるかも・・・

当たり前の事ですが

ログは起きた事の記録なので、
後から追跡する用途のためのもの

ログ = システムで起きた事の記録

これを踏まえて・・・

ログには何が欲しいか?

日時

発生箇所(ファイル・行・クラス・メソッドなど)

出力抑制機能(本番でデバッグログ出さないなど)

・・・・・・

ログのライブラリがこれらに対応する

では実際にJavaのログライブラリを見て行く事にします

Javaのログライブラリ

名称 登場時期 備考
log4j 1999年 2015年にEOL
java.util.logging 2002年(java1.4) 残念なJava標準
Apache Commons Logging 2002年 ログファサードライブラリ
SLF4J / LOGBack 2006年 今のデファクト?
Log4j2 2014年 1系と互換性の無いLog4j

Log4J


private final Logger logger = Logger.getLogger(getClass());

public void log4jTest() {
    this.logger.info("情報ログテキストを出力");
}

当時のデファクトスタンダード

ロガーで出力先(アペンダ)制御

アペンダで多様な出力先を実現(ファイル以外も可)

ログレベルで出力抑制制御

ロガーの階層化

Log4J

この後のログライブラリもだいたいLog4J互換らしいので、 Log4Jについてもうちょい詳しく見ていきます。

Log4Jのロガー


private final Logger logger = Logger.getLogger(getClass());

だいたい上の例のように、Logger.getLogger(String name)の 引数にクラス名を渡して取得します。

このloggerに生えたメソッドを使ってログを出力します。

Log4Jのロガーのメソッドと
ログレベル


public void log4jTest() {
    this.logger.fatal("致命的エラー");
    this.logger.error("エラー");
    this.logger.warn("警告");
    this.logger.info("情報");
    this.logger.debug("デバッグ");
    this.logger.trace("トレース");
}

上記の6つはいずれもログ出力用メソッドです。
上の方から順に出力優先度が高いです。

余談ですが...


public void log4jTest() {
    for(int i=0; i < 10000; i++) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("デバッグ:" + i);
        }
    }
}

Log4J登場当時は可変長引数が無かったので、
文字列結合コスト削減でこんなメソッドも使いました。

Log4Jの設定(properties版)


### ログメッセージを標準出力に出力するアペンダ「stdout」 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p %c{1} - %m%n

### ログメッセージを test.log に出力するアペンダ「logfile」 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=test.log
log4j.appender.logfile.Append=true
log4j.appender.logfile.MaxFileSize=1MB
log4j.appender.logfile.MaxBackupIndex=3
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %5p %c{1} - %m%n

### 名前が「logtest.***」であるロガーからのアペンダは「stdout」と「logfile」 ###
log4j.logger.logtest=info, stdout, logfile

上の例のように「アペンダ」を定義し「ロガー」に設定。
上の例はlog4j.propertiesですが、XML形式でも書けます。

Log4Jのその他

アペンダを変えればいろいろ出力可

出力書式も好きに書ける

スレッド情報なども書式指定だけ

スタック形式のNDC、Map形式のMDCというものがあり、文脈情報を出力可

log4j1.3系は行方不明

java.util.logging


private final Logger logger = Logger.getLogger(getClass().getName());

public void loggingTest() {
    this.logger.info("情報ログテキストを出力");
}

Java1.4から導入された標準API

ぱっと見Log4Jと似てる

java.util.logging


public void loggingTest() {
    this.logger.severe("重大");
    this.logger.warning("警告");
    this.logger.info("情報");
    this.logger.config("設定?");
    this.logger.fine("デバッグ?");
    this.logger.finer("デバッグ?");
    this.logger.finest("デバッグ?");
}

ログレベルが謎

標準書式が使いにくい

Java1.3以前で使えない

これは・・・流行らない

Apache Commons Logging


private final Log logger = LogFactory.getLog(getClass());

public void loggingTest() {
    this.logger.info("情報ログテキストを出力");
}

ログ実装に依存しないログファサードライブラリ

やっぱりLog4Jと似てる

Apache Commons Logging


public void loggingTest() {
    this.logger.fatal("致命的エラー");
    this.logger.error("エラー");
    this.logger.warn("警告");
    this.logger.info("情報");
    this.logger.debug("デバッグ");
    this.logger.trace("トレース");
}

あちこちで使われてるらしい

ログレベルはlog4Jといっしょ

動的に探索して、commons-loggingの設定があれば使う

無ければlog4Jやjava.util.loggingを使う

どれも無ければSimpleLog

Apache Commons Logging

動的探索が罠

クラスローダ使って探そうとするので、EE環境等でまともに動かないらしい
動的探索するという構想に無理があったとのこと。

SLF4J / LOGBack


private final Logger logger = LoggerFactory.getLogger(getClass());

public void logbackTest() {
    this.logger.info("情報ログ{}を出力", "てきすと");
}

SLF4Jがログファサードライブラリ

LOGBackがログ実装

やっぱりLog4Jと似てる

プレースホルダーが使えてる嬉しい

Log4jを作った人がLog4jに愛想を尽かして作ったとか…

SLF4J


public void logbackTest() {
    this.logger.error("エラー");
    this.logger.warn("警告");
    this.logger.info("情報");
    this.logger.debug("デバッグ");
    this.logger.trace("トレース");
}

SLF4Jのログレベルはfatalがない

SLF4Jはクラス実体の差し替えとやらで実装との紐付けを実現しているらしい

使うときは正しくjarファイルの配置が必須。
マニュアルを見よう。

LOGBack



  
    
      %d{HH:mm:ss.SSS} %-5level %logger{10} %msg%n
    
  
  
    logback.log
    
      %date %level [%thread] %logger{10} [%file:%line] %msg%n
    
  
  
    
  
  
    
  

LOGBackは単独では使えず、SLF4J経由で呼び出す

XMLまたはgroobyで設定を書く

Log4j2


private final Logger logger = LoggerManager.getLogger(getClass());

public void log4j2Test() {
    this.logger.info("情報ログ{}を出力", "てきすと");
}

Log4jの1系と互換性の無いLog4j

いろいろ間に合いませんでしたorz

まとめ

だいたい宮川さんの資料の焼き直しでした!
http://www.slideshare.net/miyakawataku/concepts-and-tools-of-logging-in-java

こだわりが無ければ、SLF4J / LOGBackを使っていれば良さそう

おわり

事情がいろいろあったんだなと思ってもらえれば幸いです