问题
I'm trying to log requests for my REST API application. I'm using rails notifications for this , like here http://railscasts.com/episodes/249-notifications-in-rails-3
I can't understand how to solve one problem with rails notifications.
my initializer code
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, finish, id, payload|
p name
p start
p finish
p id
p payload
end
Controller respond section
class PostsController < ApplicationController
# GET /posts
# GET /posts.json
respond_to :json, :html
....
end
Controller create action
def create
@post = Post.new(params[:post])
@post.save!
respond_with(@post, :location => nil)
end
Console output
"process_action.action_controller"
2013-02-02 20:13:11 +0200
2013-02-02 20:13:11 +0200
"951b8999e9b71d4a8949"
{:controller=>"PostsController", :action=>"create", :params=>{"utf8"=>"✓", "authenticity_token"=>"1WugY9gh6ZCRXjfBTuckye3c9XDvtCqMQ2JdBpCo88s=", "post"=>{"name"=>"post3", "title"=>"post3", "content"=>"post3"}, "commit"=>"Create Post", "action"=>"create", "controller"=>"posts"}, :format=>:html, :method=>"POST", :path=>"/posts", :status=>302, :view_runtime=>nil, :db_runtime=>0}
As you can see :db_runtime=>0
However if I change controller action code to default scaffold
def create
@post = Post.new(params[:post])
#@post.save!
#respond_with(@post)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render json: @post, status: :created, location: @post }
else
format.html { render action: "new" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
I can see
"process_action.action_controller"
2013-02-02 20:22:51 +0200
2013-02-02 20:22:51 +0200
"bf2a3173c08a0fd9008e"
{:controller=>"PostsController", :action=>"create", :params=>{"utf8"=>"✓", "authenticity_token"=>"1WugY9gh6ZCRXjfBTuckye3c9XDvtCqMQ2JdBpCo88s=", "post"=>{"name"=>"post3", "title"=>"post3", "content"=>"post3"}, "commit"=>"Create Post", "action"=>"create", "controller"=>"posts"}, :format=>:html, :method=>"POST", :path=>"/posts", :status=>302, :view_runtime=>nil, :db_runtime=>4.727}
:db_runtime=>4.727
What is the reason of that and how I can fix it to make it work in first example ? Thanks !
UPD
bundle show rails
/Users/admin/.rvm/gems/ruby-1.9.3-p125/gems/rails-3.2.11
rvm current
ruby-1.9.3-p125
UPD2
It seems like it doesn't work when I use respond_with! Can someone tell me why? Thanks
回答1:
Ok, it seems like a bug. Let see, what's going on:
First of all, we have AR railtie for controller action and its implementation for setting db_runtime by using cleanup_view_runtime hook
def cleanup_view_runtime
if ActiveRecord::Base.connected?
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
runtime = super
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
self.db_runtime = db_rt_before_render + db_rt_after_render
runtime - db_rt_after_render
else
super
end
end
App calls controller action -> action doing some db queries and renders some stuff -> before and after rendering AR Logger saves runtime data. Good.
Let see how works respond_with
def respond_with(*resources, &block)
raise "In order to use respond_with, first you need to declare the formats your " <<
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
options[:default_response] = collector.response
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
end
def self.call(*args)
new(*args).respond
end
def to_format
if get? || !has_errors? || response_overridden?
default_render
else
display_errors
end
rescue ActionView::MissingTemplate => e
api_behavior(e)
end
It seems like too much code here, but you should see the callstack for this issue: respond_with -> self.class.responder.respond -> self.class.responder.to_format -> default_render -> default_renderer raise ActionView::MissingTemplate(because we don't have any). At this moment we can see implementation for rendering :json and :xml(api_behaviour) through catching ActionView::MissingTemplate.
Now we know how respond_with works, but AR Logger does'nt know.. cleanup_view_runtime hook is called twice: for default_renderer(at that time template data was prepared and some db queries were called, but we catch ActionView::MissingTemplate in rendering proccess)
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
runtime = super # <-- here
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
and for api_behavour(at that time all template data was ready for rendering and no db queries)
Some messy explanation, but I hope it will be helpful :)
回答2:
The Rails instrumentation works by wrapping the request at the Rack metal level and recording events for the different metrics. In both cases, the entire block should be wrapped and tracked by the standard instrumentation.
The only difference I can see after digging into the source is calling save() vs. save!(). It looks like the underlying method event subscriptions in ActiveRecord may behave differently.
Try changing your respond_with() example to use save vs. save! and see if it records the db_runtime properly? If so, that's a Rails bug, and you can work around it by mimicking the save! features using save.
回答3:
UPDATE:
It's not straightforward and needs some tweak around..
Not unless you write your own Railtie to hook into this like Active Record
, but it's a bit more complex than copying what Active Record
has...
But here's how it does it:
1) Log Subscriber
2) Railtie
3) Controller Runtime
I hope that you got the hint of how to start getting into it.
Cheers!
来源:https://stackoverflow.com/questions/14665016/rails-activesupport-notifications-wrong-db-runtime-value