Skip to content

Commit

Permalink
More refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Feb 12, 2010
1 parent 33c68c1 commit 12b3d85
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 98 deletions.
61 changes: 30 additions & 31 deletions lib/rails_metrics.rb
Expand Up @@ -4,7 +4,6 @@
module RailsMetrics
autoload :AsyncConsumer, 'rails_metrics/async_consumer'
autoload :Middleware, 'rails_metrics/middleware'
autoload :Mute, 'rails_metrics/mute'
autoload :PayloadParser, 'rails_metrics/payload_parser'
autoload :Store, 'rails_metrics/store'
autoload :VERSION, 'rails_metrics/version'
Expand All @@ -14,10 +13,6 @@ module ORM
autoload :ActiveRecord, 'rails_metrics/orm/active_record'
end

class << self
delegate :mute!, :mute_instance_method!, :mute_class_method!, :to => RailsMetrics::Mute
end

# Set which store to use in RailsMetrics.
#
# RailsMetrics.set_store { Metric }
Expand All @@ -26,15 +21,33 @@ def self.set_store(&block)
metaclass.send :define_method, :store, &block
end

# Place holder for the store
# Place holder for the store.
def self.store; end

def self.request_root_node
Thread.current[:rails_metrics_request_root_node]
# Holds the events for a specific thread.
def self.events
Thread.current[:rails_metrics_events] ||= []
end

def self.request_root_node=(value)
Thread.current[:rails_metrics_request_root_node] = value
# Turn RailsMetrics on, i.e. make it listen to notifications during the block.
# At the end, it pushes notifications to the async consumer.
def self.listen
events = RailsMetrics.events
events.clear

Thread.current[:rails_metrics_listening] = true
result = yield

RailsMetrics.async_consumer.push(events.dup)
result
ensure
Thread.current[:rails_metrics_listening] = false
RailsMetrics.events.clear
end

# Returns if events are being registered or not.
def self.listening?
Thread.current[:rails_metrics_listening] || false
end

class Node
Expand Down Expand Up @@ -70,18 +83,6 @@ def child_of?(node)
end
end

class RootNode < Node
alias :set_attributes! :initialize
public :set_attributes!

def initialize
end

def root?
true
end
end

# Allow you to specify a condition to ignore a notification based
# on its name and/or payload. For example, if you want to ignore
# all notifications with empty payload, one can do:
Expand Down Expand Up @@ -112,18 +113,17 @@ def self.ignore_patterns

# Holds the queue which store stuff in the database.
def self.async_consumer
@@async_consumer ||= AsyncConsumer.new do |root_node|
root_node.children.map! { |i| RailsMetrics::Node.new(*i) }
@@async_consumer ||= AsyncConsumer.new do |nodes|
next if nodes.empty?

nodes = root_node.children.dup
root_node.children.clear
nodes.push root_node
nodes.map! { |i| RailsMetrics::Node.new(*i) }
root_node = nil

while node = nodes.shift
if parent = nodes.find { |n| n.parent_of?(node) }
parent.children << node
else
raise "OMG, Node without parent #{node.inspect}" unless nodes.empty?
root_node = node
end
end

Expand Down Expand Up @@ -155,10 +155,9 @@ def self.wait
# 2) If the notification name does not match any ignored pattern;
#
def self.valid_for_storing?(args) #:nodoc:
name, instrumenter_id, payload = args[0].to_s, args[3], args[4]
name, payload = args[0].to_s, args[4]

RailsMetrics.store && RailsMetrics.request_root_node &&
!RailsMetrics::Mute.blacklist.include?(instrumenter_id) &&
RailsMetrics.store && RailsMetrics.listening? &&
!self.ignore_patterns.find { |p| String === p ? name == p : name =~ p } &&
!self.ignore_lambdas.values.any? { |b| b.call(name, payload) }
end
Expand Down
10 changes: 1 addition & 9 deletions lib/rails_metrics/engine.rb
Expand Up @@ -22,15 +22,7 @@ class Engine < ::Rails::Engine

initializer "rails_metrics.start_subscriber" do
ActiveSupport::Notifications.subscribe do |*args|
if args[0] == "rails_metrics.request"
# TODO Commenting this line makes stuff fail, discover why
RailsMetrics.store.inspect
root_node = RailsMetrics.request_root_node
root_node.set_attributes!(*args)
RailsMetrics.async_consumer.push(root_node)
elsif RailsMetrics.valid_for_storing?(args)
RailsMetrics.request_root_node.children.push(args)
end
RailsMetrics.events.push(args) if RailsMetrics.valid_for_storing?(args)
end
end

Expand Down
20 changes: 8 additions & 12 deletions lib/rails_metrics/middleware.rb
@@ -1,7 +1,5 @@
module RailsMetrics
class Middleware
include Mute

def initialize(app)
@app = app
end
Expand All @@ -10,22 +8,20 @@ def call(env)
if env["PATH_INFO"] =~ /^\/rails_metrics/
@app.call(env)
else
RailsMetrics.request_root_node = RailsMetrics::RootNode.new

instrumenter.instrument "rails_metrics.request",
:path => env["PATH_INFO"], :method => env["REQUEST_METHOD"],
:instrumenter_id => instrumenter.id do
@app.call(env)
RailsMetrics.listen do
response = notifications.instrument "rack.middlewares",
:path => env["PATH_INFO"], :method => env["REQUEST_METHOD"],
:instrumenter_id => notifications.instrumenter.id do
@app.call(env)
end
end
end
ensure
RailsMetrics.request_root_node = nil
end

protected

def instrumenter
ActiveSupport::Notifications.instrumenter
def notifications
ActiveSupport::Notifications
end
end
end
38 changes: 0 additions & 38 deletions lib/rails_metrics/mute.rb

This file was deleted.

3 changes: 0 additions & 3 deletions lib/rails_metrics/orm/active_record.rb
@@ -1,6 +1,3 @@
# Mute migration notifications.
RailsMetrics.mute_class_method!(ActiveRecord::Migrator, :migrate)

# Setup to ignore any query which is not a SELECT, INSERT, UPDATE
# or DELETE and queries made by the own store.
RailsMetrics.ignore :invalid_queries do |name, payload|
Expand Down
16 changes: 14 additions & 2 deletions test/integration/instrumentation_test.rb
Expand Up @@ -11,10 +11,9 @@ class InstrumentationTest < ActionController::IntegrationTest

request = Metric.first

assert_equal "rails_metrics.request", request.name
assert_equal "rack.middlewares", request.name
assert (request.duration >= 0)
assert_kind_of Time, request.started_at
assert_nil request.instrumenter_id
assert_equal Hash[:path => "/users", :method => "GET",
:instrumenter_id => ActiveSupport::Notifications.instrumenter.id], request.payload
end
Expand Down Expand Up @@ -48,6 +47,19 @@ class InstrumentationTest < ActionController::IntegrationTest
:layout => "RAILS_ROOT/app/views/layouts/users.html.erb"], template.payload
end

test "instrumentations are saved nested in the database" do
get "/users"
wait!

assert_equal 4, Metric.count
request, action, sql, template = Metric.all

assert_nil request.instrumenter_id
assert_equal action.instrumenter_id, request.id
assert_equal sql.instrumenter_id, action.id
assert_equal template.instrumenter_id, action.id
end

test "does not create metrics when accessing /rails_metrics" do
assert_no_difference "Metric.count" do
get "/rails_metrics"
Expand Down
2 changes: 1 addition & 1 deletion test/integration/navigation_test.rb
Expand Up @@ -80,7 +80,7 @@ class NagivationTest < ActionController::IntegrationTest
assert_contain "ordered by earliest"

click_link "Show"
assert_contain "rails_metrics.request"
assert_contain "rack.middlewares"

get "/rails_metrics"
click_link "Order by fastest"
Expand Down
4 changes: 2 additions & 2 deletions test/store_test.rb
Expand Up @@ -3,7 +3,7 @@
class StoreTest < ActiveSupport::TestCase
def sample_args
time = Time.now
["rails_metrics.example", time, time + 10, "i" * 20, { :some => :info }]
["rails_metrics.example", time, time + 10, 1, { :some => :info }]
end

# We need to mute RailsMetrics, otherwise we get Sqlite3 database lock errors
Expand All @@ -24,7 +24,7 @@ def store!(args=sample_args)
end

test "sets the instrumenter id" do
assert_equal ("i" * 20), store!.instrumenter_id
assert_equal 1, store!.instrumenter_id
end

test "sets the payload" do
Expand Down

0 comments on commit 12b3d85

Please sign in to comment.