こんにちは、河野です。この記事は TECHSCORE Advent Calendar 2014 の 11日目の記事になります。
最近Railsで使用するRack Middlewareを作ってみましたので、その辺りの話を書こうと思います。
そもそもrackって?
rackについて説明すると長くなるので、以下の記事が参考にしてください。
Ruby - Rack解説 - Rackの構造とRack DSL - Qiita
簡単に説明すると、アプリケーションサーバとのインターフェースのことです。
あるクラスにcallメソッドを定義して、
- ステータスコード
- レスポンスヘッダのハッシュ
- レスポンスボディの配列
をまとめた配列を返すようにしてね!というものです。
Rack Middlewareを作成する
今回は、大量のヘルスチェックのログでアプリのログが見にくくなることに悩んでいましたので、専用のログに出力する機能を持ったミドルウェアを作ってみました。
stackoverflowの記事を参考に作りました。
ログを分けるためのmiddlewareなので、Rails::Rack::Loggerを継承したクラスを作ります。
オプションでヘルスチェック用のパスと、ログのタグを渡せるようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# lib/health_logger.rb class HealthLogger < Rails::Rack::Logger def initialize(app, opts={}) @default_log = Rails.logger @health_log = Logger.new(Rails.root.join('log', 'health.log')) @app = app @opts = opts @opts[:health_path] = Array(@opts[:health_path]) super app, @opts[:tags] end def call(env) if @opts[:health_path].include?(env['PATH_INFO']) logfile = @health_log else logfile = @default_log end ActiveRecord::Base.logger = logfile ActionController::Base.logger = logfile ActionView::Base.logger = logfile Rails.logger = logfile @logger = logfile super env end end |
initializeで専用のロガーを作っておいて、アクセス時にPATH_INFOを参照し、ロガーを切り替えるというものです。
作ったmiddlewareを追加する
Railsでは、config.middlwareに
- insert_before
- insert_after
- swap
の3つのメソッドが用意されています。
今回は既存のロガーを入れ替えるので、swapを使用しました。
1 2 3 4 |
# config/application.rb config.middleware.swap( Rails::Rack::Logger, "HealthLogger", {tags: config.log_tags, health_path: "/check/health"} ) |
config/application.rbの中で指定していますが、config/initializersの中で設定しても良いみたいです。
クラス名は文字列でも渡せます。
middlewareが登録されているか確認する
rakeコマンドを実行しましょう。
1 |
bundle exec rake middleware |
以下のように自分が作ったクラスが表示されているはずです。
1 2 3 4 5 6 |
use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use HealthLogger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions |
動作確認する
1 |
bundle exec rails s |
普通にrailsを起動してアクセスして確認します。
ハマったこと
他にもいくつか作ってみてハマったところがあるので、ご紹介します。
auto reload されない
middlewareは起動時にしか読み込まれません。
修正した内容を反映するためには、railsの再起動が必要です。
開発中だと地味にメンドクサイです。
例外を適切に処理する
middlewareの中で例外をざっくり受け取って、500で返すような作りにしてしまうと、他のmiddlewareに例外が伝搬できないことになります。
アプリ本体や他のmiddlewareで発生したエラーも拾ってしまうので、内容を確認した上でraiseするなどの処理が必要です。
以上、簡単に説明してみましたが、Rack Middlewareを作れるようになると、ApplicationControlerで対応していたような処理を切り出すことができそうですね。