share facebook facebook2 twitter menu hatena pocket slack

2015.02.24 TUE

PlayFramework の application.log をよしなにローテーションする方法

川原 洋平

WRITTEN BY川原 洋平

ども、知らないことばかりの cloudpackかっぱ (@inokara) です。

はじめに

アプリケーションのエラーログも見逃したくない

PlayFramework では特に設定を行わない限りは ${application.home}/logs/application.log にログを書き込むが、放っておくとローテーションも行われない…orz。後々泣かない為にもローテーションを行いつつ、ログの監視を行うにあたり監視対象のログファイル名は固定したい場合にどうすっかなーと思って試したメモ。

キモは LogBack

初耳の LogBack だが、PlayFramework ではデフォルトのロガーとして利用されている。

log4j の後継という位置付けで log4j よりも柔軟な設定が出来るとのこと。今回は Logback の設定を修正した上で…冒頭と繰り返しになるが、

  • ログの定期的なローテーション
  • 最新のログファイル名は固定

を試してみたい。

参考

Logback Project
Logback Document / Chapter 4: Appenders / FileAppender
Logback Document / Chapter 4: Appenders / RollingFileAppender

logger.xml

logger.xml を全文掲載

とりあえず Logback の設定は以下のような logger.xml に記述して ${application.home}/conf/ 以下に置くことでやりたいことを実現してみる。

<configuration>

  <conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${application.home}/logs/application.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${application.home}/logs/application.%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10</maxFileSize>
    </triggeringPolicy>

     <encoder>
       <pattern>%date - [%level] - from %logger in %thread %n%message%n%xException%n</pattern>
     </encoder>
   </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%coloredLevel %logger{15} - %message%n%xException{5}</pattern>
    </encoder>
  </appender>

  <logger name="play" level="INFO" />
  <logger name="application" level="DEBUG" />

  <!-- Off these ones as they are annoying, and anyway we manage configuration ourself -->
  <logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
  <logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
  <logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
  <logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />

  <root level="ERROR">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="FILE" />
  </root>

</configuration>

ローテーション内容の詳細

  • 最新のログは ${application.home}/logs/application.log に書き込む
  • 一定容量を超えたら ${application.home}/logs/application.1.log のようにローテーション
  • 容量の制限は 1000 バイト/1 ファイル(テストの為)
  • 過去 3 ファイルまで保存

ポイント

今回のポイントとなる部分は以下の通り。

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${application.home}/logs/application.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${application.home}/logs/application.%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>1000</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%date - [%level] - from %logger in %thread %n%message%n%xException%n</pattern>
    </encoder>
   </appender>

ch.qos.logback.core.rolling.RollingFileAppender というクラスを利用することで…

RollingFileAppender extends FileAppender with the capability to rollover log files. For example, RollingFileAppender can log to a file named log.txt file and, once a certain condition is met, change its logging target to another file.

とあるように従来の FileAppender を拡張することが出来る。例えば、ファイル名を log.txt に変更したり、一定条件を満たされるとローテーションさせたりすることが出来るようになる。

以下は各種プロパティを整理したもの。(ドキュメントより抜粋)

プロパティ名 タイプ 詳細
file string ファイル名を指定
apend boolean デフォルトは有効で有効な場合には既存ファイルにログイベントが追記される
encoder Encoder ※ちょい特殊
rollingPolicy RollingPolicy ログのローテーションポリシーで TimeBasedRollingPolicy / FixedWindowRollingPolicy から選択
triggeringPolicy triggeringPolicy ローテーションを行う際のトリガーを指定で SizeBasedTriggeringPolicy を指定する
prudent boolean FixedWindowRollingPolicy では利用しない

なるほど、なるほど。

上記の設定例を見ると…

  • ファイル名は ${application.home}/logs/application.log
  • ローテーションされるファイル名は ${application.home}/logs/application.%i.log
  • ローテーションは三世代(1 〜 3)
  • ファイルサイズが 1000 bytes を超えたらローテーションされる(デフォルトは 10MB となる)

ファイルサイズの指定については以下のように書かれている。

SizeBasedTriggeringPolicy accepts only one parameter, namely maxFileSize, with a default value of 10 MB.

The maxFileSize option can be specified in bytes, kilobytes, megabytes or gigabytes by suffixing a numeric value with KB, MB and respectively GB. For example, 5000000, 5000KB, 5MB and 2GB are all valid values, with the first three being equivalent.

KBMBGB で指定することが出来る。

デモ

アプリケーションの用意

import org.fluentd.logger.FluentLogger;
import play.Logger;

public class Application extends Controller {
  private static final FluentLogger LOG = FluentLogger.getLogger("app","127.0.0.1", 24224);

  public static Result index() {
      Logger.debug("--------------------------------------------------------------");
      return ok(index.render("Your new application is ready."));

(snip)

上記のようなアプリケーションを用意。ちなみに fluent-logger-java との同居も可能。とりあえず Logger に仕事をさせたいのでアプリケーションのルート(/)にアクセスさせる度に Logger がデバッグログを吐く。

アプリケーションの起動

アプリケーションを play dist 状態で、冒頭に貼った logger.xml を以下のように指定してアプリケーションを起動する。

${application.home}/bin/play-test-app -Dhttp.port=9002 -Dlogger.file=${application.home}/conf/logger.xml &

起動後の application.log は以下のような状態。

$ pwd
${application.home}/logs
$ ls -l
合計 4
-rw-rw-r-- 1 pool pool 181  2月 19 07:37 application.log

とりあえずローテーションさせてみる

ひたすら curl -s localhost:9000/ip を実行することで application.log には以下のようなログが吐かれ続ける。

2015-02-19 07:40:32,777 - [DEBUG] - from application in play-akka.actor.default-dispatcher-4
--------------------------------------------------------------

以下のようにログが分割されている。

$ ls -ltr
合計 12
-rw-rw-r-- 1 pool pool 2393  2月 19 07:40 application.2.log
-rw-rw-r-- 1 pool pool 1106  2月 19 07:42 application.1.log
-rw-rw-r-- 1 pool pool  158  2月 19 07:42 application.log

おお。ちゃんと最新のファイルは application.log になってるし 1000 bytes で(チョッキリではないけど)ローテーションされている!

ということで…

今日の知見

  • Play Framework では標準で Logback というログフレームワークが利用されている
  • 特に指定しなければ ${application.home}/logs/application.log にログが吐かれ続ける
  • ローテーションを行う場合には ch.qos.logback.core.rolling.RollingFileAppender クラスを利用する
  • RollingFileAppender クラスではファイルサイズ、時間でのローテーションを行うことが出来る

今回は…

駆け足でサイズベースのローテーションを試してみたが TimeBasedRollingPolicy を利用することで時間でのローテーションも出来るので別途試したいみたい。

元記事はこちらです。
PlayFramework の application.log をよしなにローテーションする方法