rails_metrics 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +3 -0
- data/Gemfile +9 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +58 -0
- data/Rakefile +58 -0
- data/TODO.rdoc +1 -0
- data/app/controllers/rails_metrics_controller.rb +78 -0
- data/app/helpers/rails_metrics_helper.rb +161 -0
- data/app/views/layouts/rails_metrics.html.erb +21 -0
- data/app/views/rails_metrics/_request.html.erb +21 -0
- data/app/views/rails_metrics/_row.html.erb +23 -0
- data/app/views/rails_metrics/all.html.erb +25 -0
- data/app/views/rails_metrics/chart.html.erb +49 -0
- data/app/views/rails_metrics/index.html.erb +21 -0
- data/app/views/rails_metrics/show.html.erb +41 -0
- data/config/routes.rb +7 -0
- data/lib/generators/rails_metrics_generator.rb +40 -0
- data/lib/rails_metrics.rb +112 -0
- data/lib/rails_metrics/async_consumer.rb +54 -0
- data/lib/rails_metrics/engine.rb +29 -0
- data/lib/rails_metrics/middleware.rb +27 -0
- data/lib/rails_metrics/orm/active_record.rb +66 -0
- data/lib/rails_metrics/payload_parser.rb +131 -0
- data/lib/rails_metrics/store.rb +132 -0
- data/lib/rails_metrics/version.rb +3 -0
- data/public/images/rails_metrics/arrow_down.png +0 -0
- data/public/images/rails_metrics/arrow_up.png +0 -0
- data/public/images/rails_metrics/cancel.png +0 -0
- data/public/images/rails_metrics/chart_pie.png +0 -0
- data/public/images/rails_metrics/page_white_delete.png +0 -0
- data/public/images/rails_metrics/page_white_go.png +0 -0
- data/public/images/rails_metrics/tick.png +0 -0
- data/public/javascripts/rails_metrics/g.pie-min.js +6 -0
- data/public/javascripts/rails_metrics/g.raphael-min.js +5 -0
- data/public/javascripts/rails_metrics/raphael-min.js +5 -0
- data/public/stylesheets/rails_metrics.css +135 -0
- data/test/dummy/app/controllers/application_controller.rb +4 -0
- data/test/dummy/app/controllers/users_controller.rb +43 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/metric.rb +3 -0
- data/test/dummy/app/models/notification.rb +7 -0
- data/test/dummy/app/models/user.rb +2 -0
- data/test/dummy/config/application.rb +52 -0
- data/test/dummy/config/boot.rb +9 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +19 -0
- data/test/dummy/config/environments/production.rb +33 -0
- data/test/dummy/config/environments/test.rb +29 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookie_verification_secret.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +15 -0
- data/test/dummy/config/routes.rb +60 -0
- data/test/dummy/db/migrate/20100106152343_create_metrics.rb +17 -0
- data/test/dummy/db/migrate/20100108120821_create_users.rb +13 -0
- data/test/integration/instrumentation_test.rb +100 -0
- data/test/integration/navigation_test.rb +103 -0
- data/test/orm/active_record_test.rb +51 -0
- data/test/payload_parser_test.rb +36 -0
- data/test/rails_metrics_test.rb +43 -0
- data/test/store_test.rb +81 -0
- data/test/support/helpers.rb +16 -0
- data/test/support/instrumentation.rb +18 -0
- data/test/support/mock_store.rb +34 -0
- data/test/support/webrat/integrations/rails.rb +31 -0
- data/test/test_helper.rb +32 -0
- metadata +118 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
<% if @metrics.empty? %>
|
2
|
+
<h2>No metrics so far, navigate on your app and come back.</h2>
|
3
|
+
<% else %>
|
4
|
+
<% content_for(:rails_metrics_header) do %>
|
5
|
+
<%= pagination_and_scopes_info(:metrics) %>
|
6
|
+
|
7
|
+
<% form_tag url_for(params.merge(:action => "destroy_all")), :method => :delete do %>
|
8
|
+
<%= submit_tag "Delete all", :onclick => "return confirm('Are you sure you want to delete those #{@metrics_count} metrics?')" %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<table id="rails_metrics_table" class="all">
|
13
|
+
<tr>
|
14
|
+
<th>When<br /><%= link_to_order_by_scopes(:earliest, :latest) %></th>
|
15
|
+
<th>Name<br /><%= link_to_clear_by_scope(:name) %></th>
|
16
|
+
<th>Duration<br /><%= link_to_order_by_scopes(:slowest, :fastest) %></th>
|
17
|
+
<th>Payload</th>
|
18
|
+
<th></th>
|
19
|
+
</tr>
|
20
|
+
|
21
|
+
<%= render :partial => "row", :collection => @metrics, :as => :metric %>
|
22
|
+
</table>
|
23
|
+
|
24
|
+
<% paginate! %>
|
25
|
+
<% end %>
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<% content_for(:rails_metrics_header) do %>
|
2
|
+
Showing request #<%= @request.id %>
|
3
|
+
<div class="actions"><% add_action_links!(@request) %></div>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<div id="chart_container">
|
7
|
+
<div id="chart"></div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<script type="text/javascript" charset="utf-8">
|
11
|
+
var r = Raphael("chart");
|
12
|
+
r.g.text(400, 30, "<%= @request.payload[:method] %> <%= @request.payload[:path] %> at <%= @request.started_at.strftime("%d %b %H:%M:%S") %>").attr({"font-size": 20});
|
13
|
+
|
14
|
+
var pie = r.g.piechart(250, 150, 100,
|
15
|
+
<%=raw @metrics.map { |m| m.exclusive_duration_in_ms }.inspect %>, {
|
16
|
+
legend: <%=raw @metrics.map { |m| "##.# ms - #{m.name}" }.inspect %>,
|
17
|
+
href: <%=raw @metrics.map { |m| "#rails_metric_#{m.id}" }.inspect %>,
|
18
|
+
cut: 0
|
19
|
+
}
|
20
|
+
);
|
21
|
+
|
22
|
+
pie.hover(function () {
|
23
|
+
this.sector.stop();
|
24
|
+
this.sector.scale(1.1, 1.1, this.cx, this.cy);
|
25
|
+
if (this.label) {
|
26
|
+
this.label[0].stop();
|
27
|
+
this.label[0].scale(1.5);
|
28
|
+
this.label[1].attr({"font-weight": 800});
|
29
|
+
}
|
30
|
+
}, function () {
|
31
|
+
this.sector.animate({scale: [1, 1, this.cx, this.cy]}, 500, "bounce");
|
32
|
+
if (this.label) {
|
33
|
+
this.label[0].animate({scale: 1}, 500, "bounce");
|
34
|
+
this.label[1].attr({"font-weight": 400});
|
35
|
+
}
|
36
|
+
});
|
37
|
+
</script>
|
38
|
+
|
39
|
+
<table id="rails_metrics_table" class="chart">
|
40
|
+
<tr>
|
41
|
+
<th>Name</th>
|
42
|
+
<th>Duration (exclusive)</th>
|
43
|
+
<th>Payload</th>
|
44
|
+
<th></th>
|
45
|
+
</tr>
|
46
|
+
|
47
|
+
<%= render :partial => "row", :collection => @metrics, :as => :metric,
|
48
|
+
:locals => { :skip_timestamps => true } %>
|
49
|
+
</table>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% if @metrics.empty? %>
|
2
|
+
<h2>No requests so far, navigate on your app and come back.</h2>
|
3
|
+
<% else %>
|
4
|
+
<% content_for(:rails_metrics_header) do %>
|
5
|
+
<%= pagination_and_scopes_info(:requests) %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<table id="rails_metrics_table" class="requests">
|
9
|
+
<tr>
|
10
|
+
<th>When<br /><%= link_to_order_by_scopes(:earliest, :latest) %></th>
|
11
|
+
<th>Method</th>
|
12
|
+
<th>Path</th>
|
13
|
+
<th>Duration<br /><%= link_to_order_by_scopes(:slowest, :fastest) %></th>
|
14
|
+
<th></th>
|
15
|
+
</tr>
|
16
|
+
|
17
|
+
<%= render :partial => "request", :collection => @metrics, :as => :metric %>
|
18
|
+
</table>
|
19
|
+
|
20
|
+
<% paginate! %>
|
21
|
+
<% end %>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<% content_for(:rails_metrics_header) do %>
|
2
|
+
Showing metric #<%= @metric.id %>
|
3
|
+
<div class="actions"><% add_action_links!(@metric) %></div>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<table id="rails_metrics_table" class="show">
|
7
|
+
<tr>
|
8
|
+
<th>Key</th>
|
9
|
+
<th>Value</th>
|
10
|
+
</tr>
|
11
|
+
|
12
|
+
<tr class="odd">
|
13
|
+
<td>Name</td>
|
14
|
+
<td><%= link_to_set_by_scope @metric, :name %></td>
|
15
|
+
</tr>
|
16
|
+
|
17
|
+
<tr class="even">
|
18
|
+
<td>Request</td>
|
19
|
+
<td><%= link_to @metric.request_id, chart_rails_metric_path(@metric.request_id) %></td>
|
20
|
+
</tr>
|
21
|
+
|
22
|
+
<tr class="odd">
|
23
|
+
<td>Duration</td>
|
24
|
+
<td><%= @metric.duration_in_ms %> ms</td>
|
25
|
+
</tr>
|
26
|
+
|
27
|
+
<tr class="even">
|
28
|
+
<td>Payload</td>
|
29
|
+
<td class="payload"><%= payload_inspect(@metric.payload) %></td>
|
30
|
+
</tr>
|
31
|
+
|
32
|
+
<tr class="odd">
|
33
|
+
<td>Started at</td>
|
34
|
+
<td><%= @metric.started_at %></td>
|
35
|
+
</tr>
|
36
|
+
|
37
|
+
<tr class="even">
|
38
|
+
<td>Created at</td>
|
39
|
+
<td><%= @metric.created_at %></td>
|
40
|
+
</tr>
|
41
|
+
</table>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
class RailsMetricsGenerator < Rails::Generators::NamedBase
|
2
|
+
class_option :migration, :type => :boolean, :default => true
|
3
|
+
|
4
|
+
class_option :update, :type => :boolean, :default => false,
|
5
|
+
:desc => "Just update public files, do not create a model"
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
@_metrics_source_root ||= File.dirname(__FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_public_files
|
12
|
+
directory "../../public", "public", :recursive => true
|
13
|
+
exit(0) if options.update?
|
14
|
+
end
|
15
|
+
|
16
|
+
def invoke_model
|
17
|
+
invoke "model", [name].concat(migration_columns),
|
18
|
+
:timestamps => false, :test_framework => false, :migration => options.migration?
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_model_config
|
22
|
+
inject_into_class "app/models/#{file_name}.rb", class_name, <<-CONTENT
|
23
|
+
include RailsMetrics::ORM::#{Rails::Generators.options[:rails][:orm].to_s.camelize}
|
24
|
+
CONTENT
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_application_config
|
28
|
+
inject_into_class "config/application.rb", "Application", <<-CONTENT
|
29
|
+
# Set rails metrics store
|
30
|
+
config.rails_metrics.set_store = lambda { ::#{class_name} }
|
31
|
+
|
32
|
+
CONTENT
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def migration_columns
|
38
|
+
%w(name:string duration:integer request_id:integer parent_id:integer payload:text started_at:datetime created_at:datetime)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
Thread.abort_on_exception = Rails.env.development? || Rails.env.test?
|
3
|
+
|
4
|
+
module RailsMetrics
|
5
|
+
autoload :AsyncConsumer, 'rails_metrics/async_consumer'
|
6
|
+
autoload :Middleware, 'rails_metrics/middleware'
|
7
|
+
autoload :PayloadParser, 'rails_metrics/payload_parser'
|
8
|
+
autoload :Store, 'rails_metrics/store'
|
9
|
+
autoload :VERSION, 'rails_metrics/version'
|
10
|
+
autoload :VoidInstrumenter, 'rails_metrics/async_consumer'
|
11
|
+
|
12
|
+
module ORM
|
13
|
+
autoload :ActiveRecord, 'rails_metrics/orm/active_record'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set which store to use in RailsMetrics.
|
17
|
+
#
|
18
|
+
# RailsMetrics.set_store { Metric }
|
19
|
+
#
|
20
|
+
def self.set_store(&block)
|
21
|
+
metaclass.send :define_method, :store, &block
|
22
|
+
end
|
23
|
+
|
24
|
+
# Place holder for the store.
|
25
|
+
def self.store; end
|
26
|
+
|
27
|
+
# Holds the events for a specific thread.
|
28
|
+
def self.events
|
29
|
+
Thread.current[:rails_metrics_events] ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
# Turn RailsMetrics on, i.e. make it listen to notifications during the block.
|
33
|
+
# At the end, it pushes notifications to the async consumer.
|
34
|
+
def self.listen_request
|
35
|
+
events = RailsMetrics.events
|
36
|
+
events.clear
|
37
|
+
|
38
|
+
Thread.current[:rails_metrics_listening] = true
|
39
|
+
result = yield
|
40
|
+
|
41
|
+
RailsMetrics.async_consumer.push(events.dup)
|
42
|
+
result
|
43
|
+
ensure
|
44
|
+
Thread.current[:rails_metrics_listening] = false
|
45
|
+
RailsMetrics.events.clear
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns if events are being registered or not.
|
49
|
+
def self.listening?
|
50
|
+
Thread.current[:rails_metrics_listening] || false
|
51
|
+
end
|
52
|
+
|
53
|
+
# Allow you to specify a condition to ignore a notification based
|
54
|
+
# on its name and/or payload. For example, if you want to ignore
|
55
|
+
# all notifications with empty payload, one can do:
|
56
|
+
#
|
57
|
+
# RailsMetrics.ignore :with_empty_payload do |name, payload|
|
58
|
+
# payload.empty?
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# However, if you want to ignore something based solely on its
|
62
|
+
# name, you can use ignore_patterns instead:
|
63
|
+
#
|
64
|
+
# RailsMetrics.ignore_patterns << /^some_noise_plugin/
|
65
|
+
#
|
66
|
+
def self.ignore(name, &block)
|
67
|
+
raise ArgumentError, "ignore expects a block" unless block_given?
|
68
|
+
ignore_lambdas[name] = block
|
69
|
+
end
|
70
|
+
|
71
|
+
# Stores the blocks given to ignore with their respective identifier in a hash.
|
72
|
+
def self.ignore_lambdas
|
73
|
+
@@ignore_lambdas ||= {}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Stores ignore patterns that can be given as strings or regexps.
|
77
|
+
def self.ignore_patterns
|
78
|
+
@@ignore_patterns ||= []
|
79
|
+
end
|
80
|
+
|
81
|
+
# Holds the queue which store stuff in the database.
|
82
|
+
def self.async_consumer
|
83
|
+
@@async_consumer ||= AsyncConsumer.new do |events|
|
84
|
+
next if events.empty?
|
85
|
+
root = RailsMetrics.store.events_to_metrics_tree(events)
|
86
|
+
root.save_metrics!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Wait until the async queue is consumed.
|
91
|
+
def self.wait
|
92
|
+
sleep(0.01) until async_consumer.finished?
|
93
|
+
end
|
94
|
+
|
95
|
+
# A notification is valid for storing if two conditions are met:
|
96
|
+
#
|
97
|
+
# 1) The instrumenter id which created the notification is not the same
|
98
|
+
# instrumenter id of this thread. This means that notifications generated
|
99
|
+
# inside this thread are stored in the database;
|
100
|
+
#
|
101
|
+
# 2) If the notification name does not match any ignored pattern;
|
102
|
+
#
|
103
|
+
def self.valid_for_storing?(args) #:nodoc:
|
104
|
+
name, payload = args[0].to_s, args[4]
|
105
|
+
|
106
|
+
RailsMetrics.listening? && RailsMetrics.store &&
|
107
|
+
!self.ignore_patterns.find { |p| String === p ? name == p : name =~ p } &&
|
108
|
+
!self.ignore_lambdas.values.any? { |b| b.call(name, payload) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
require 'rails_metrics/engine'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module RailsMetrics
|
4
|
+
# An instrumenter that does not send notifications. This is used in the
|
5
|
+
# AsyncQueue so saving events does not send any notifications, not even
|
6
|
+
# for logging.
|
7
|
+
class VoidInstrumenter < ::ActiveSupport::Notifications::Instrumenter
|
8
|
+
def instrument(name, payload={})
|
9
|
+
yield(payload) if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AsyncConsumer
|
14
|
+
attr_reader :thread
|
15
|
+
|
16
|
+
def initialize(queue=Queue.new, &block)
|
17
|
+
@off = true
|
18
|
+
@block = block
|
19
|
+
@queue = queue
|
20
|
+
@mutex = Mutex.new
|
21
|
+
|
22
|
+
@thread = Thread.new do
|
23
|
+
set_void_instrumenter
|
24
|
+
consume
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def push(*args)
|
29
|
+
@mutex.synchronize { @off = false }
|
30
|
+
@queue.push(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def finished?
|
34
|
+
@off
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def set_void_instrumenter #:nodoc:
|
40
|
+
Thread.current[:"instrumentation_#{notifier.object_id}"] = VoidInstrumenter.new(notifier)
|
41
|
+
end
|
42
|
+
|
43
|
+
def notifier #:nodoc:
|
44
|
+
ActiveSupport::Notifications.notifier
|
45
|
+
end
|
46
|
+
|
47
|
+
def consume #:nodoc:
|
48
|
+
while args = @queue.shift
|
49
|
+
@block.call(args)
|
50
|
+
@mutex.synchronize { @off = @queue.empty? }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RailsMetrics
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
engine_name :rails_metrics
|
4
|
+
|
5
|
+
# Add middleware
|
6
|
+
config.middleware.use RailsMetrics::Middleware
|
7
|
+
|
8
|
+
# Initialize configure parameters
|
9
|
+
config.rails_metrics.ignore_lambdas = {}
|
10
|
+
config.rails_metrics.ignore_patterns = [ "action_controller.start_processing" ]
|
11
|
+
|
12
|
+
initializer "rails_metrics.set_ignores" do |app|
|
13
|
+
RailsMetrics.ignore_lambdas.merge!(app.config.rails_metrics.ignore_lambdas)
|
14
|
+
RailsMetrics.ignore_patterns.concat(app.config.rails_metrics.ignore_patterns)
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer "rails_metrics.set_store" do |app|
|
18
|
+
if app.config.rails_metrics.set_store
|
19
|
+
RailsMetrics.set_store(&app.config.rails_metrics.set_store)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
initializer "rails_metrics.start_subscriber" do
|
24
|
+
ActiveSupport::Notifications.subscribe do |*args|
|
25
|
+
RailsMetrics.events.push(args) if RailsMetrics.valid_for_storing?(args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RailsMetrics
|
2
|
+
class Middleware
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
if env["PATH_INFO"] =~ /^\/rails_metrics/
|
9
|
+
@app.call(env)
|
10
|
+
else
|
11
|
+
RailsMetrics.listen_request do
|
12
|
+
response = notifications.instrument "rack.request",
|
13
|
+
:path => env["PATH_INFO"], :method => env["REQUEST_METHOD"],
|
14
|
+
:instrumenter_id => notifications.instrumenter.id do
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def notifications
|
24
|
+
ActiveSupport::Notifications
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Setup to ignore any query which is not a SELECT, INSERT, UPDATE
|
2
|
+
# or DELETE and queries made by the own store.
|
3
|
+
RailsMetrics.ignore :invalid_queries do |name, payload|
|
4
|
+
name == "active_record.sql" &&
|
5
|
+
(payload[:sql] !~ /^(SELECT|INSERT|UPDATE|DELETE)/ ||
|
6
|
+
RailsMetrics.store.connections_ids.include?(payload[:connection_id]))
|
7
|
+
end
|
8
|
+
|
9
|
+
module RailsMetrics
|
10
|
+
module ORM
|
11
|
+
# Include in your model to store metrics. For ActiveRecord, you need the
|
12
|
+
# following setup:
|
13
|
+
#
|
14
|
+
# script/generate model Metric script/generate name:string duration:integer
|
15
|
+
# request_id:integer parent_id:integer payload:text started_at:datetime created_at:datetime --skip-timestamps
|
16
|
+
#
|
17
|
+
# You can use any model name you wish. Next, you need to include
|
18
|
+
# RailsMetrics::ORM::ActiveRecord:
|
19
|
+
#
|
20
|
+
# class Metric < ActiveRecord::Base
|
21
|
+
# include RailsMetrics::ORM::ActiveRecord
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
module ActiveRecord
|
25
|
+
extend ActiveSupport::Concern
|
26
|
+
include RailsMetrics::Store
|
27
|
+
|
28
|
+
included do
|
29
|
+
# Create a new connection pool just for the given resource
|
30
|
+
establish_connection(Rails.env)
|
31
|
+
|
32
|
+
# Set required validations
|
33
|
+
validates_presence_of :name, :started_at, :duration
|
34
|
+
|
35
|
+
# Serialize payload data
|
36
|
+
serialize :payload
|
37
|
+
|
38
|
+
# Select scopes
|
39
|
+
scope :requests, where(:name => "rack.request")
|
40
|
+
scope :by_name, lambda { |name| where(:name => name) }
|
41
|
+
scope :by_request_id, lambda { |request_id| where(:request_id => request_id) }
|
42
|
+
|
43
|
+
# Order scopes
|
44
|
+
# We need to add the id in the earliest and latest scope since the database
|
45
|
+
# does not store miliseconds. The id then comes as second criteria, since
|
46
|
+
# the ones started first are first saved in the database.
|
47
|
+
scope :earliest, order("started_at ASC, id ASC")
|
48
|
+
scope :latest, order("started_at DESC, id DESC")
|
49
|
+
scope :slowest, order("duration DESC")
|
50
|
+
scope :fastest, order("duration ASC")
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def connections_ids
|
55
|
+
self.connection_pool.connections.map(&:object_id)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def save_metric!
|
62
|
+
save!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|