こんにちは、鈴木です。
Rails ではデフォルトで以下のような形式のログが出力されますね。
1 2 3 4 5 |
Started GET "/articles" for 192.168.0.4 at 2013-05-05 15:14:37 +0900 Processing by ArticlesController#index as HTML Article Load (0.3ms) SELECT "articles".* FROM "articles" ORDER BY id Rendered articles/index.html.erb within layouts/application (2.6ms) Completed 200 OK in 9ms (Views: 7.9ms | ActiveRecord: 0.3ms) |
これにクライアント情報も含まれていたら便利かなと思いました。
具体的には、以下のようなグロが出力される想定です。(横に長いのでスクロースしないと違いが分からないかもしれませんが、一行目の最後に「from 〜〜〜」が増えています。)
1 2 3 4 5 |
Started GET "/articles" for 192.168.0.4 at 2013-05-05 15:20:46 +0900 from Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:20.0) Gecko/20100101 Firefox/20.0 Processing by ArticlesController#index as HTML Article Load (0.6ms) SELECT "articles".* FROM "articles" ORDER BY id Rendered articles/index.html.erb within layouts/application (27.0ms) Completed 200 OK in 90ms (Views: 69.9ms | ActiveRecord: 6.0ms) |
この 1 行目は、Rails::Rack::Logger#started_request_message(request) が生成しています。
モンキーパッチで。。
ということで、このメソッドを修正するモンキーパッチを書きました。
1 2 3 4 5 6 7 8 9 |
class Rails::Rack::Logger < ActiveSupport::LogSubscriber def started_request_message_with_client_info(request) client_info = "#{request.user_agent}" "#{started_request_message_without_client_info(request)} from #{client_info}" end alias_method_chain :started_request_message, :client_info end |
書いてから気付いたのですが、Ruby2.0 で登場した Module#prepend を使ったほうが行儀が良いですね。
Module#prepend を使う。
Module#prepend を使うように書き換えてみました。
(ご存知ではない方は、「Ruby2.0のModule#prependは如何にしてalias_method_chainを撲滅するのか!?」をご覧ください。)
1 2 3 4 5 6 7 8 9 10 |
class Rails::Rack::Logger < ActiveSupport::LogSubscriber prepend Module.new { def started_request_message(request) client_info = "#{request.user_agent}" "#{super(request)} from #{client_info}" end } end |
先程よりもコードがすっきりしましたね。
面倒だったのでモジュールを定義してから prepend に渡さずに、その場で Module.new しました。
Module#prepend の引数に Module.new ってどうよ?
Module#prepend を使うパターンでは、prepend の引数に名前の無いモジュール(Module.new で動的に生成したモジュール)を渡しました。
モジュールの継承関係を確認すると「#<Module:0x9940c24>」のようなものが含まれてしまい、なんとなく見た目がよろしくありません。
1 2 |
> Rails::Rack::Logger.ancestors => [#<Module:0x9940c24>, Rails::Rack::Logger, ActiveSupport::LogSubscriber, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, Kernel, BasicObject] |
これってどうよ?という話なのですが、どう思われますか?
以下のように名前のついたモジュールを定義してから prepend すれば、継承関係の中にもその名前が含まれるので使う側に優しいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module StartRequestMessageWithClientInfo def started_request_message(request) client_info = "#{request.user_agent}" "#{super(request)} from #{client_info}" end end class Rails::Rack::Logger < ActiveSupport::LogSubscriber prepend StartRequestMessageWithClientInfo end # Rails::Rack::Logger.ancestors # => [StartRequestMessageWithClientInfo, Rails::Rack::Logger, ...] |
きちんとしたライブラリを作る場合は、多少面倒でも名前のあるモジュールを定義した上で prepend した方が良いかもしれません。