波打際のブログさん

主に、プログラミング備忘録など。

Ruby on RailsのI18nで使用する名前空間に関してのまとめと、ベストプラクティスの検討。

はじめに

 Ruby on RailsにはI18nというgemが標準で同梱されており、特に何もせずとも文章の国際化を行うことができる仕組みになっています。ちなみに、I18nとはinternationalizationを表しており、iとnの間に18文字あるから `I18n` だそうです。
 最近関わっているRailsのプロジェクトで国際化を行うタスクが発生し、I18nの辞書ファイルを定義する際に、定義ファイル中の名前空間(ロケール情報を定義する場所)について、どのような構造で配置するべきか調べる機会があったのでまとめておきます。

  • 2013年12月14日 Rubyの徳が高い同僚が、グローバルなattributesに関しての情報を見つけてくれたので追記しました。
  • 2014年07月04日 ロケールファイルの構成を一部見直しました。

環境

  • Rails4

想定環境

rails generateで name, email, urlを引数に指定してUserをscaffoldした環境を想定してロケールファイルを記述しています。

rails generate scaffold user name:string email:string url:string
Userモデル
User name email url

※idとかcreated_atとかupdated_atは気にしないでください。

app/views/users フォルダ
app/views/users
_form.html.erb
index.html.erb
show.html.erb
edit.html.erb

※他にも生成されますが、これくらいで許してください。

Rails公式ガイドのまとめ。

 Railsの公式ガイドを Rails Internationalization (I18n) API — Ruby on Rails Guides を読み解いた結果、ロケールファイルは次のような名前空間の構造で記述すれば良いことがわかりました。

ja:
  # モデルは全て activerecord 以下に記述する。
  # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
  activerecord:
    models:
      user: 'ユーザ情報'
    attributes:
      user:
        name: '名前'
        mail: 'メールアドレス'
        url:  'ウェブページ'
    errors:
      models:
        user:
          blank: 'urlかmailが空白です。'
          attributes:
            name:
              blank: '名前が空白です。'  # errosの中に同じ定義がある場合、ネストが深いほうが優先順位が高い
    
  
  # ビューはビューを格納しているフォルダ名を起点にし、ビュー名毎に記述する。
  # 対応するビューの中ではツリーを省略できる。<%= t '.title' %>
  users:
    index:
      title: 'ユーザ一覧'
    show:
      title: '%{user_name}さんのユーザ情報'
    edit:
      title: '%{user_name}さんのユーザ情報を編集'
 
 
  # グローバルな感じのエラーはerrorsを起点にする。
  errors:
    nanikano_error: 'よくわからないけど例外です。'

上記の形式で記述することで、Railsのヘルパーメソッドを利用して、容易にロケール情報へアクセスすることができます。

Modelのロケール情報へのアクセス

 モデル関連のロケール情報を、上記の形式で書くことで ActiveRecord::Base に定義されている、model_name.humanとhuman_attribute_name を利用して取得することができます。

p User.model_name.human # => `ユーザ情報` (activerecord.models.userを参照)

p User.human_attribute_name(:name) # => `名前` (activerecord.attributes.user.nameを参照)

Viewからロケール情報へのアクセス

 ビュー関連のロケール情報を、上記の形式で書くことで、対応するビューの中で t ヘルパーメソッドを利用して、キー名を省略した形で取得することができます。

# index.html.erbの中で実行
<% t '.title' %> # => 'ユーザ一覧' (users.index.titleを参照)

# show.html.erbの中で実行
<% t '.title', user_name: user.name %> # => '{ユーザ名}さんのユーザ情報' (users.show.titleを参照)

HTMLをロケールの中に記述する

 通常、ロケールファイルの中にHTMLタグを記述しても、タグがエスケープされて出力されます。しかし、キーの末尾を _html にすることで、ロケールファイル内に書かれたHTMLタグをそのまま出力することが可能です。

ja:
  users:
    index:
      hello_html: 'Hello!<strong>Ruby</strong>World!'

 ただし、ロケールにHTMLタグを持たせることには抵抗があります。言語ファイルがビューのスタイルに影響を及ぼしてしまうことはロケール情報の範疇を超えていると個人的には考えています。なので、他に綺麗に記述することができない、止む終えない場合の最終手段程度に留めておくことをお勧めします。

オレオレベストプラクティス

 Rails公式ガイドでは ヘルパー や コントローラ、共通のワード等の名前空間の構造には触れられていませんでした。そこで、stackoverflowや既存のロケールファイルを参考に、オレオレベストプラクティス的な物を考えてみました。

ja:
  # 共通の使いまわす可能性のあるワードは dictionary を起点にする。
  dictionary:
    messages:
      hello_user: 'こんにちは%{user_name}さん'
    words:
      user: &user 'ユーザ情報'        # &user はエイリアスで *user で参照できる。(同一ファイル内のみ有効)
      user_copy: *user                # => 'ユーザ情報' となる。
      site_name: '波打際のブログさん'
 
  # モデルは全て activerecord を起点にする。
  # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
  activerecord:
    models:
      user: 'ユーザー情報'
    attributes:
      user:
        name: '名前'
        mail: 'メールアドレス'
        url:  'ウェブページ'
    errors:
      nanikano_model_error: 'モデルで何かのエラー'  # 特定のモデルに属さないエラーはこの階層に書く。
      models:
        user:
          blank: 'urlかmailが空白です。' # モデル内で使いまわすエラーはこの階層に書く。
          attributes:
            name:
              blank: '名前が空白です。'  # errosの中に同じ定義がある場合、ネストが深いほうが優先順位が高い
  
  # 全モデルで共通のアトリビュートはattributesを起点にattribute名を直下に記述する。
  # validation errorやhuman_attribute_nameで取得できる。
  # (ネストの深いものが優先されるため、activerecord.attributes内の優先度が最も高い。)
  attributes:
    user_id: 'ユーザー'

  # ヘルパー関数はhelpersを起点にする。
  # ヘルパー関数内で tメソッドを使用すると、呼び出し元のビューに基づいたパスが呼び出される。
  # 例えばusersのshowから呼ばれたヘルパー関数内で t('.hoge') を実行した場合 users.show.hoge が参照される。
  # 呼び出し元によって文言を変えたい場合はビュー側に記述する。(そんなことあるかわからないけど。)
  helpers:
    user:
      welcome: 'ようこそ!'
  
  # ビューはビューを格納しているフォルダ名を起点にし、ビュー名毎に記述する。
  # 対応するビューの中ではツリーを省略できる。<%= t '.title' %>
  users:
    index:
      title: 'ユーザ一覧'
    show:
      title: '%{user_name}さんのユーザ情報'
    edit:
      title: '%{user_name}さんのユーザ情報を編集'

  # ActionMailerを継承したMailerはクラス名を起点にする。
  nanikano_mailer:
    alert_email:
      subject: 'タイトル'
      body:    '本文'

  # グローバルな感じのエラーはerrorsを起点にする。
  errors:
    nanikano_error: 'よくわからないけど例外です。'

 コントローラに関する記述が無いと思いますが、コントローラで固有の言語情報を持つ設計は、そもそも設計が誤りである可能性があります。もしコントローラにロケールしなければならない文字列がある場合には、設計を見直してみてください。
(redirect_toメソッド呼び出しの際の、noticeやalertメッセージに関しては、文量がさほど多くなく割と使い回しているため、dictionary.messages以下に突っ込んでしまっています。100行200行にでもなったら考えましょ。)

 あくまでも オレオレベストプラクティス なので、おかしいところ、もっとこうする方がいいんじゃね?とかあると思います。もしそう思ったら指摘してくれると嬉しいです。

RubyでInteger値を持つEnum、Inumを作ってみた。

この記事はバージョン2.x系の記事で、現行バージョンの3.xとは互換性がありません。

RailsのEnumと相性が悪い人向けのInumをバージョンアップさせた話。 - 波打際のブログさん
こちらの記事がバージョン3.x系の記事となります。

はじめに

 最近Rubyを使用する機会が増えてきたのですが、Rubyに列挙型の実装がありませんでした。そこで、Gemを探してみたものの、C言語ライクなInteger値を持つEnumで、思ったとおりの事が可能なGemが存在しなかったので、自前で実装する機会がありました。その実装の仕組みを参考に汎用的に使用可能なEnum(Inum)を作成してGemとして公開しました。


  • 2013年11月28日、機能追加に伴い追記しました。
  • 2013年12月03日、バージョン2.0.0リリースに伴い記事を修正しました。
  • 2013年12月10日、バージョン2.0.5リリースに伴い記事を修正しました。

Inumでやりたいこと(実装したこと)

  • CやC#ライクなInteger値を保持しているEnumで有ること。
  • 列挙型のメンバーを表すインスタンスは、一つしか存在しないこと。(メンバーがシングルトン)
  • 値とメンバー名の相互変換ができること。
  • i18nを使用して列挙型からローカライズした文字列を取得できること。

使い方

Gemのインストール

 いつもどおりです、RailsならGemfileなどにinumの依存関係を追加します。

gem 'inum'

列挙型の定義

 Inum::Baseを継承したクラス内で、define_enumメソッドを呼び出して列挙型を定義します。define_enumの第一引数には列挙型のラベル(名前)をSymbolで、第二引数には列挙型の値をIntegerでそれぞれ渡します。第二引数の列挙型の値は省略可能で、省略された場合0から自動的にインクリメントされます。

class AnimeType < Inum::Base
  define_enum :NyarukoSan,   0
  define_enum :KiniroMosaic, 1
  define_enum :KillMeBaby,   2
end

列挙型の使用方法

Inumを継承した列挙型は次のように使用出来ます。

p AnimeType::NyarukoSan       # => NyarukoSan 
p AnimeType::NyarukoSan.to_i  # => 0

# 数値や、シンボルから列挙型へのパーズが可能です。
type = AnimeType::parse(1)
p type.equal?(AnimeType::KiniroMosaic)  # => true

# eql?メソッドを使用すると、比較対象のオブジェクトをパーズ後比較します。
p AnimeType::KillMeBaby.eql?('KillMeBaby')  # => true

# underscoreメソッドを使用すると、Enumのラベルをアンダースコアつなぎの文字列にします。
p AnimeType::NyarukoSan.underscore  # => nyaruko_san

# +と-の単項演算子も定義されています。
p AnimeType::NyarukoSan + 1  # => KiniroMosaic
p AnimeType::KillMeBaby - 1  # => KiniroMosaic

# eachによる繰り返しもできます。
AnimeType::each do |enum|
  p enum
end

Enumerableモジュールをextendし、Comparableモジュールをincludeしていますので、mapなどのメソッドが利用可能です。

その他のメソッドについてはドキュメント(Class: Inum::Base — Documentation for alfa-jpn/inum (master))を参照してください。

その他の使い方

i18nを利用する

 Enum(Inum)で to_t メソッドを利用することで、i18nローカライズされた文字列を取得することが可能です。

次のような多言語化ファイルが存在した場合

ja:
  inum:
    anime_type:
      nyaruko_san:   "這いよれ!ニャル子さん"
      kiniro_mosaic: "きんいろモザイク"
      kill_me_baby:  "キルミーベイベー"

translateメソッドを用いると次の結果になります。

p AnimeType::NyarukoSan.translate    # => 這いよれ!ニャル子さん
p AnimeType::KiniroMosaic.translate  # => きんいろモザイク
p AnimeType::KillMeBaby.translate    # => キルミーベイベー

i18nの辞書定義はデフォルトでは inum.{名前空間(モジュール名)}.{クラス名}.{メンバー名} を参照しますが、i18n_keyクラスメソッドをオーバーライドすることで参照先を書き換えることも可能です。

class AnimeType < Inum::Base
  define_enum :NyarukoSan,   0
  define_enum :KiniroMosaic, 1
  define_enum :KillMeBaby,   2

  # i18n_keyをオーバーライド(内容はデフォルトの定義です。)
  def self.i18n_key(label)
    # underscoreメソッドでキャメルをアンダースコアつなぎにした上で、::を.に置き換えています。
    Inum::Utils::underscore("inum::#{self.name}::#{label}")
  end
end
Enumをクラスの変数に結びつける

 DefineCheckMethodモジュールを利用すると、クラスの変数とEnumを関連付けし、判別用メソッドを自動的に定義することが可能です。

class Anime
  extend Inum::DefineCheckMethod

  attr_accessor :type

  define_check_method :type, AnimeType
end

define_check_methodにカラム名と関連付けさせるInumを継承したEnumクラスを渡して判別用メソッドを自動的に定義しています。

anime = Anime.new
anime.type = AnimeType::NyarukoSan

p anime.nyaruko_san?    # => true
p anime.kiniro_mosaic?  # => false


# typeにはパーズ可能なメソッドなら何でも入ります。
anime.type = 1
p anime.kiniro_mosaic? # => true

また、define_check_methodの第三引数は省略可能オプションになっていて、prefixの設定が可能です。prefixを指定すると定義されるメソッドの先頭にprefixが付与されます。

class Anime
  extend Inum::DefineCheckMethod

  attr_accessor :type

  define_check_method :type, AnimeType, 'anime'
end

prefixにanimeを指定すると、定義されるメソッドは次のようになります。

anime = Anime.new
anime.type = AnimeType::NyarukoSan

p anime.anime_nyaruko_san?    # => true

おわりに

 何か改善案あればpullrequest、issue気軽に投げてください、githubアカウントお持ちでない方はこのブログのコメント欄にもお気軽にどうぞ。

関連リンク

rbenv-railsを使用して、バージョン指定したrailsプロジェクトを30秒で生成する。

はじめに

 以前、Bundlerを使ってローカルなRailsから新規プロジェクトを作る 方法を紹介しましたが、プロジェクトを生成するたびに実行するのは結構面倒です。そこでこの作業を自動的に行うシェルスクリプトを書いたので紹介します。 → 追記:せっかくなので rbenv のプラグインにしました。


想定環境

  • rbenvがインストールと設定が完了している事。 → こちらを参考に設定&インストール。
  • gemのbundler(bundleコマンド)がインストールされている事。 → こちらのBundlerの項目を参照(コマンド3行です。)

インストール

rbenvのpluginsフォルダにrbenv-railsをgit cloneします。
# rbenvのプラグインフォルダにクローンする。
$ git clone https://github.com/alfa-jpn/rbenv-rails.git ~/.rbenv/plugins/rbenv-rails

インストールは以上です。

使い方

引数

rbenv rails <プロジェクト名> <railsのバージョン>

実行例

# rails 4.0.0 を使用して example_project を作成する。
rbenv rails example_project 4.0.0

もし、途中でsqlite3関連のエラーが出た際は libsqlite3-dev をパッケージマネージャからインストールしてください。

補足

 rbenv-railsは引数で指定されたバージョンのrailsを、ローカル環境(カレントディレクトリ以下のvendorフォルダ)にインストールします。そのrailsを利用して新規にrailsプロジェクトを生成し、生成されたrailsプロジェクトの中で bundle install コマンドを実行し、プロジェクトディレクトリの vendor/bundle に gemをインストールします。

 そのため、rbenv-railsを使用して生成されたrailsプロジェクトは、グローバル環境に干渉しないrailsプロジェクトになります。

補足2

 おそらく、rbenv rails コマンド実行後パッケージのダウンロードに時間がかかると思いますが、コマンドを入力する時間は30秒以内だったはずです。誇大タイトルでしたゆるしてくだ・・・おや誰か来たようだ、うわなにをするやめくぁwせdrftgyふじこlp;

Playframework2.1のCSRF対策

はじめに

 Playframework2.1では新機能としてCSRF対策が追加されたそうです。そもそもCSRF対策の機能はPlayframework1.x系には存在した機能ですが、2.0では機能そのものが消えていました。当然といえば当然の復活です。

 公式の新機能紹介( Highlights )にも New Filter API and CSRF protection と大きく取り上げています。

新機能紹介のページを読み進めていくと、

The filters project that is part of the standard Play distribution contain a set of standard filter, such as the CSRF providing automatic token management against the CSRF security issue.

http://www.playframework.com/documentation/2.1.x/Highlights

 素晴らしい!これでCSRF問題の苦悩から解放される・・・!と、感動すると同時にCSRFに関する説明はここで終わっていることに気づきます。

そうです、肝心なhow toや関連するリンクが一切ないのです!!

と、言うことで必死にぐぐって使えるようになったので結果をまとめておきます。

編集するファイル

project/build.scala

 filtersを依存関係に追加します。appDependenciesの部分を下記のように編集します。

val appDependencies = Seq(
	// Add your project dependencies here,
	javaCore,
	javaJdbc,
	javaEbean,
	filters //←この行を追加
)

app/Global.java

 アプリケーション全体の設定などを記述するGlobalクラスに、次のメソッドを追加します。Globalクラスは初期状態では存在しないので生成して、app直下(eclipse等では、default packages)に配置します。 Global.javaに関する詳しい記述は JavaGlobal を参照してください。

import play.*;
import play.api.mvc.EssentialFilter;
import play.filters.csrf.CSRFFilter;

public class Global extends GlobalSettings {

	@Override
	@SuppressWarnings({"rawtypes", "unchecked"})
	public <T extends EssentialFilter> Class<T>[] filters() {
		Class[] filters = {CSRFFilter.class};
		return filters;
	}

}

 filters()でCSRFFilterのクラスを返すことで、リクエストをインターセプトした際に、フィルターが呼び出されて処理が行われるみたいです。

使い方

 設定が終わったので、あとはフォームに設定を適用するだけで自動的にCSRF対策が行われます。具体的には helper関数を利用してフォームの宣言を行う際のactionに helper.CSRF関数を使用します。

@helper.form(action = helper.CSRF(routes.NyarukoController.postSanti)) {

}

こんなかんじです。


Bundlerを使ってローカルなRailsから新規プロジェクトを作る

はじめに

 本項では、ローカルなRailsから新規プロジェクトを作成し、gitで管理を行います。グローバルな環境にRailsをインストールしないことで、複数のバージョンのRailsを使い分けしやすくなります。

環境

Bundlerのインストール

 Bundlerとは、RubyGemsのラッパーで、Gemのバージョン管理ツールです。Bundlerを使うことで複数の環境下で依存関係の解決が容易にできます。

# rbenvを使用してBundlerをインストール。
$ rbenv exec gem install bundler
 
# rehashする。
$ rbenv rehash
 
# インストールの確認。
$ rbenv exec bundle --version
Bundler version 1.3.5

Bundlerを使ってローカルなRailsからプロジェクトを作る。

これ以降の手順を自動化するシェルスクリプトを書きました。詳細は バージョンを指定したRailsプロジェクトを自動で生成する。 - 波打際のブログさん を参照してください。


 Bundlerを使ってローカルにRailsをインストールし、ローカルなrailsからプロジェクトを生成します。

プロジェクトを生成したいディレクトリにGemfileを生成する。

$ bundle init

Gemfileの編集

 initすると、カレントディレクトリにGemfileが生成されるので、それを編集してGemfileの依存関係にRailsを追加します。

$ echo 'gem "rails", "4.0.0"' >> Gemfile

Bundlerを利用してローカルにRailsをインストール

 bundle installコマンドで、Gemfileの依存関係が解決されるのでRailsがインストールされます。

$ bundle install --path vendor/bundle

Railsプロジェクトを生成する。

 インストールされたRailsを利用して、Railsのプロジェクトを生成します。下記コマンドを実行するとカレントディレクトリの下に Nyaruko プロジェクトが生成されます。

$ bundle exec rails new Nyaruko --skip-bundle

 この時に --skip-bundle を指定しないとグローバルな環境が汚染されます。

不要なファイルの削除

 ローカルなRailsはもう使用しないので削除します。

rm -f Gemfile*
rm -rf .bundle
rm -rf vendor

 以上でローカルなRailsから新規Railsプロジェクトの生成が完了しました。

Railsプロジェクトをgitで管理できるようにする。

 生成したRailsプロジェクトをgitで管理できるようにします。

gitリポジトリ化する

 まずは、プロジェクトをgitリポジトリ化します。

Nyaruko $ git init
Nyaruko $ git add .
Nyaruko $ git commit

依存関係の解決

 生成されたRailsプロジェクトを使用可能にするために、bundle installを実行して、依存関係を解決します。

Nyaruko $ bundle install --path vendor/bundle

.gitignoreの設定

 依存関係の解決のために生成されたファイル類は、gitにpushすべきものではないので、無視するように.gitignoreを修正します。

Nyaruko $ echo '/vendor/bundle' >> .gitignore

 後はaddしてcommitすれば、gitで管理されたRailsプロジェクトの生成が完了です。

Railsの起動

 同梱されているWEBRickRailsを起動します。

Nyaruko $ bundle exec rails server

起動できなかったときは、Gemfile中の

gem 'therubyracer', :platforms => :ruby

をコメントアウトしてもう一回 bundle installを実行すると動くかも。。

rbenvを使って3分でRubyをインストールする方法まとめ。

はじめに

 最近モダンなrbenvを使って、3分の作業時間でRubyをインストールできたのでその方法をまとめておきます。

rbenv

 rbenvはrvmと同様にRubyのバージョン管理ソフトです。rvmよりも黒魔術を使わないため、副作用が少なく、最近モダンらしいです。乗るしかないこのビッグウェーブに。

環境

  • CentOS6.4
  • SSHでgit(git-hub)が利用できる環境(本項では解説しません。)

rbenv

rbenvのインストール

 インストールは非常に簡単で、公式リポジトリ(GitHub - rbenv/rbenv: Groom your app’s Ruby environment)をgitクローンしてbashにパスを通して、bash起動時に初期化されるように設定するだけで完了です。

# gitでローカルの ~/.rbenv にクローンを作る。
$ git clone git@github.com:rbenv/rbenv.git ~/.rbenv

# bashにパスを通す。(ubuntuの場合は .bash_profile ではなく .profile)
$ echo 'export RBENV_ROOT="$HOME/.rbenv"' >> ~/.bash_profile
$ echo 'export PATH="$RBENV_ROOT/bin:$PATH"' >> ~/.bash_profile

# bash起動時にrbenvの初期化を行うように設定。
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

# シェルの再起動。
$ exec $SHELL -l

# インストールできているか確認。
$ rbenv --version
rbenv x.x.x-xx-xxxxxx # バージョンは環境によって異なります。

ruby-buildのインストール

 ruby-buildのインストールは、rbenvよりも更に簡単で、先ほどのrbenvのプラグインフォルダに、公式リポジトリ(GitHub - rbenv/ruby-build: Compile and install Ruby)をgitクローンするだけで終わりです。

# gitでローカルにクローンする。
$ git clone git@github.com:rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

 以上でRubyをインストールするためのrbenv環境が整いました。

Rubyのインストール

 rbenvを利用してRubyをインストールします。インストール可能なパッケージを install -l オプションで調べてインストールを行います。

# インストール可能なパッケージ一覧を調べる。
$ rbenv install -l
 
# 実行時に一番新しいRubyをインストールしてみた。
$ rbenv install 2.0.0-p247

# デフォルトのRubyに設定する。
$ rbenv global 2.0.0-p247

 rbenvがソースコードコンパイルを行うため、Rubyのインストールには時間がかかりますが、作業時間では無いのでノーカンで・・・

Apacheのログ設定まとめ

はじめに

 apacheのログについて、調べる機会があったので備忘録としてまとめておきます。

環境

  • CentOS6.4
  • apache2.4.4

バーチャルホスト毎にログを記録する

 バーチャルホストで複数のサービスを運営している際に、それぞれのホスト毎にログファイルを分けると管理が容易になります。

バーチャルホスト設定の編集

 設定はとても簡単で、バーチャルホストの設定(conf/extra/httpd-vhosts.conf)にCustomLog(アクセスログ)とErrorLog(エラーログ)の設定を記述するだけです。

<VirtualHost *:80>
    DocumentRoot "/htdocs/nyaruko"
    ServerName www.nyaruko.cqc
    CustomLog logs/nyaruko_server_access_log common
    ErrorLog  logs/nyaruko_server_error_log
</VirtualHost>

ログのローテートを行う

 日別や週別、月別などにログを分けて保存することをログのローテートと言います。ログのローテート方法はいくつか有りますが、今回はapacheに付属しているrotatelogsを利用する方法と、logrotateを利用する方法の2つを紹介します。

rotatelogsを利用する

 apacheに付属しているログのローテートツールです。ログの出力先としてパイプすることでログを分けて管理することができます。

CustomLog "|/usr/local/apache2/bin/rotatelogs /usr/local/apache2/logs/nyaruko_access_log_%Y%m%d 86400 540" common
第一引数

 ログの出力先を記述します。今回は /usr/local/apache2/logs/nyaruko_access_log_%Y%m%d と記述しているので、logs/nyaruko_access_log_20130814 という形式で出力されます。

第二引数

 ログの周期を記述します。86400sec=24h

第三引数

 時差を記述します。540min = 9h GMT(日本標準時)

logrotateを利用する

 CentOS6.4に標準で搭載されているlogrotateを利用すると、apacheに限らず様々なログを容易にローテートできます。 /etc/logrotate.d ディレクトリ以下に、新たにファイルを作成して設定を記述することでローテートが可能です。今回は /etc/logrotate.d/httpd-nyaruko という名前でファイルを作成して設定を記述しました。

# 対象のログファイル
/usr/local/apache2/logs/nyaruko_access_log {
 
	# ローテートの実行方式
	# 移動&作成		: create
	# コピー			: copy
	# コピー&削除		: copytruncate
	create	
 
	# ログファイルが見つからない際の挙動
	# エラーを吐く		: nomissingok
	# エラーを吐かない		: missingok
	missingok
 
	# ログファイルが空の際の動作
	# 空でも実行		: ifempty
	# 空の場合は実行しない	: noifempty
	notifempty
 
	# ローテーションの頻度
	# 日単位			: daily
	# 週単位			: weekly
	# 月単位			: monthly
	daily
 
	# 生成されるログファイル数の上限
	rotate 7
 
	# 圧縮設定
	# 圧縮			: compres
	# 未圧縮			: nocompress
	# 遅延圧縮		: delaycompress
	compres
 
	# ローテート時にスクリプト実行
	sharedscripts
	postrotate
		/sbin/service httpd reload
	endscript
}

 apache+logrotateではローテート実行時に、sharedscripts設定を記述して再起動を行います。再起動しないでローテートする場合はcreateではなくcopytruncateを指定します。しかし、ローテート中にアクセスがあった場合のログが記録されないのでログに漏れが発生します。

 nginx+logrotateの場合は再起動せずに、ログの漏れもなくローテートが実行できますが、その設定はまた次の機会に・・・。

所感

 apacheでログをローテートするならrotatelogsを使ったほうが、サービスを止めたり、ログの紛失がなくローテートができそう・・・ただしrotatelogsはログの削除ができないので、自分でシェルスクリプトを書いてcronで叩かないといけない。。。さらに、ログ一回吐き出すたびにrotatelogsにパイプするのって負荷がかかりそうな・・・よし、俺のVPSにはnginx使おう←イマココ