17462


リクエストを受けるごとに、その時点の時刻を保持して、コントローラ、モデル、ビュー、ヘルパー、(ownなlib配置の)ライブラリのどこからも参照できるようにするには、どうすべきでしょうか。

ぱっと思いつくのは、ApplicationControllerのフィルターでグローバル変数で管理する(あるいは管理するクラスを利用する)という方法ですが、本当にそれで良いのでしょうか。

管理するクラスで処理する場合、  
グローバル変数にアクセスすべきか。  
クラス変数にアクセスすべきか。  
安全かわかりませんがThreadにインスタンス変数を入れて保管すべきか。  
そのあたりの良し悪しがわからないので教えて下さい。


CC BY-SA 3.0
2015-10-09T02:07:20.703
17450
2015-10-13T13:05:42.323
2015-10-10T01:21:32.030
5793
8335
question
ruby-on-rails
Railsでリクエストごとのタイムスタンプを取得する方法


以下はStack Overflowに投稿された質問 [How to get the time at which a request is made from
the request object in rails
3?](https://stackoverflow.com/questions/5661303)に対する[Romanの回答](https://stackoverflow.com/a/5690430)を少し改変したものです

Rack Middlewareを作成し、リクエストにタイムスタンプを付加します

app/middleware/timestamp.rb

[code]

    class Timestamp
      def initialize(app)
        @app = app
      end
    
      def call(env)
        env[:timestamp] = Time.now
        @app.call(env)
      end
    end
    
[/code]

config/application.rbに`config.middleware.use`を追加します

[code]

    config.middleware.use "Timestamp"
    
[/code]

これで、`request.env[:timestamp]`でタイムスタンプが取得できます

* * *

練習がてらgemにしてみました

[code]

    gem 'rack-request_timestamp'
    
[/code]

してbundle installでインストールした後、config/application.rbに

[code]

    config.middleware.use Rack::RequestTimestamp
    
[/code]

を追加してください。

[code]

    request.env[:timestamp]
    
[/code]

でタイムスタンプが取れます。


CC BY-SA 3.0
2015-10-09T05:00:58.697
17462
2015-10-13T13:05:42.323
2017-05-23T12:38:56.083
-1
5793
17450
answer


## TL;DR

[steveklabnik/request_store](https://github.com/steveklabnik/request_store)
gemを使い、`ApplicationController`のフィルターで値を設定するとよいのではないでしょうか。

[code]

    class ApplicationController < ActionController::Base
      before_action :set_timestamp
    
      def set_timestamp
       RequestStore.store[:timestamp] = Time.now
      end
    end
    
    # コントローラ/モデル/ビュー/ヘルパーなどで
    RequestStore.store[:timestamp]
    
[/code]

`RequestStore`はグローバルに参照できて、スレッドセーフなどにまつわる問題の面倒を見てくれます。

## 理由

グローバル変数やクラス変数はスレッドセーフではないのでお勧めできません。

[Thread#[]](http://docs.ruby-
lang.org/ja/2.2.0/method/Thread/i/=5b=5d.html)や[Thread#[]=](http://docs.ruby-
lang.org/ja/2.2.0/method/Thread/i/=5b=5d=3d.html)を使うとスレッド固有のデータを格納することが出来ます。

`Thread.current[:foo] = :bar`のように値をいれれば、コントローラ/モデル/ビュー/ヘルパーどこからでも参照できます。

単純にリクエストごとに毎回値を設定/参照するだけなら以下のようなミドルウェアを書くといいと思います。

[code]

    class Timestamp
      def initialize(app)
        @app = app
      end
    
      def call(env)
        Thread.current[:timestamp] = Time.now
        @app.call(env)
      end
    
      def self.current
        Thread.current[:timestamp]
      end
    end
    
    # コントローラ/モデル/ビュー/ヘルパーなどで
    Timestamp.current
    
[/code]

しかし、同じスレッドで何度もリクエストが処理されるような場合、スレッドに設定されたデータが残っていて問題が起こる可能性があるので、以下のようにリクエストごとに設定した値を消す処理が必要になってくると思います。

[code]

      def call(env)
        Thread.current[:timestamp] = Time.now
        @app.call(env)
      ensure
        Thread.current[:timestamp] = nil
      end
    
[/code]

`RequestStore`を使うとそのあたりの面倒を見てくれるので楽です。

参考: [Rails - リクエスト単位でグローバルな参照を持たせてAuditログをスッキリ実装したい -
Qiita](http://qiita.com/ainame/items/823b396d560d82194d48)


CC BY-SA 3.0
2015-10-09T07:50:37.383
17473
2015-10-09T08:12:26.500
2015-10-09T08:12:26.500
2599
2599
17450
answer


apache 等のログをsed,grep,awkといったunixコマンドを使って取得することで、  
rubyやrailsの仕掛けに苦闘する理由を根本的に消す方法もあり得ると思います。

色々状況はあり得るのですが、説明のために一番簡単な状況を仮定しますと、  
例えばwebサーバーがapacheで、railsアプリしか扱わず、  
その状態でホントに最後のリクエストログから時刻をとるには  
以下のようなコマンド展開を利用したやり方が使えます。

[code]

    log = `tail -n 1 /usr/local/apache/logs/access_log | awk '{print substr($4,2)}'`.chop
    #=> "13/Oct/2015:10:04:53"
    
[/code]


CC BY-SA 3.0
2015-10-13T01:13:07.183
17570
2015-10-13T01:13:07.183
9403
17450
answer