librato-rails 0.9.0 → 0.10.0.pre1

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.
Files changed (51) hide show
  1. data/CHANGELOG.md +14 -0
  2. data/FAQ.md +25 -0
  3. data/README.md +11 -2
  4. data/lib/librato/rails/configuration.rb +26 -29
  5. data/lib/librato/rails/railtie.rb +30 -5
  6. data/lib/librato/rails/subscribers/cache.rb +22 -0
  7. data/lib/librato/rails/subscribers/controller.rb +56 -0
  8. data/lib/librato/rails/subscribers/mail.rb +18 -0
  9. data/lib/librato/rails/subscribers/render.rb +28 -0
  10. data/lib/librato/rails/subscribers/sql.rb +24 -0
  11. data/lib/librato/rails/subscribers.rb +14 -69
  12. data/lib/librato/rails/tracker.rb +13 -0
  13. data/lib/librato/rails/version.rb +1 -1
  14. data/lib/librato/rails.rb +9 -214
  15. data/test/dummy/app/assets/javascripts/application.js +0 -3
  16. data/test/dummy/app/controllers/cache_controller.rb +44 -0
  17. data/test/dummy/app/controllers/render_controller.rb +4 -0
  18. data/test/dummy/app/models/user.rb +7 -5
  19. data/test/dummy/app/views/render/_first.html.erb +1 -0
  20. data/test/dummy/app/views/render/_second.html.erb +1 -0
  21. data/test/dummy/app/views/render/partial.html.erb +2 -0
  22. data/test/dummy/app/views/render/template.html.erb +1 -0
  23. data/test/dummy/config/application.rb +8 -6
  24. data/test/dummy/config/environments/test.rb +1 -1
  25. data/test/dummy/config/routes.rb +12 -3
  26. data/test/integration/cache_test.rb +40 -0
  27. data/test/integration/mail_test.rb +2 -4
  28. data/test/integration/render_test.rb +27 -0
  29. data/test/integration/request_test.rb +15 -11
  30. data/test/integration/sql_test.rb +6 -6
  31. data/test/support/integration_case.rb +11 -7
  32. data/test/unit/configuration_test.rb +63 -73
  33. data/test/unit/tracker_test.rb +15 -0
  34. metadata +36 -53
  35. data/lib/librato/rack/middleware.rb +0 -47
  36. data/lib/librato/rack.rb +0 -4
  37. data/lib/librato/rails/aggregator.rb +0 -95
  38. data/lib/librato/rails/collector.rb +0 -45
  39. data/lib/librato/rails/counter_cache.rb +0 -122
  40. data/lib/librato/rails/group.rb +0 -27
  41. data/lib/librato/rails/logging.rb +0 -77
  42. data/lib/librato/rails/validating_queue.rb +0 -31
  43. data/lib/librato/rails/worker.rb +0 -54
  44. data/lib/tasks/metrics-rails_tasks.rake +0 -4
  45. data/test/librato-rails_test.rb +0 -44
  46. data/test/remote/rails_remote_test.rb +0 -193
  47. data/test/unit/aggregator_test.rb +0 -53
  48. data/test/unit/counter_cache_test.rb +0 -90
  49. data/test/unit/group_test.rb +0 -54
  50. data/test/unit/middleware_test.rb +0 -82
  51. data/test/unit/worker_test.rb +0 -31
data/lib/librato/rails.rb CHANGED
@@ -1,215 +1,10 @@
1
- require 'socket'
2
- require 'thread'
3
-
4
- require 'active_support/core_ext/module/attribute_accessors'
5
- require 'active_support/notifications'
6
- require 'librato/metrics'
7
-
8
1
  require 'librato/rack'
9
- require 'librato/rails/aggregator'
10
- require 'librato/rails/collector'
11
- require 'librato/rails/configuration'
12
- require 'librato/rails/counter_cache'
13
- require 'librato/rails/group'
14
- require 'librato/rails/logging'
15
- require 'librato/rails/validating_queue'
16
- require 'librato/rails/version'
17
- require 'librato/rails/worker'
18
-
19
- module Librato
20
- extend SingleForwardable
21
- def_delegators Librato::Rails, :increment, :measure, :timing, :group
22
-
23
- module Rails
24
- extend SingleForwardable
25
- extend Librato::Rails::Configuration
26
- extend Librato::Rails::Logging
27
-
28
- FORKING_SERVERS = [:unicorn, :passenger]
29
- SOURCE_REGEX = /\A[-:A-Za-z0-9_.]{1,255}\z/
30
-
31
- # config options
32
- mattr_accessor :user
33
- mattr_accessor :token
34
- mattr_accessor :flush_interval
35
- mattr_accessor :source_pids
36
-
37
- # config defaults
38
- self.flush_interval = 60 # seconds
39
- self.source_pids = false # append process id to the source?
40
- # log_level (default :info)
41
- # source (default: your machine's hostname)
42
-
43
- # handy introspection
44
- mattr_accessor :explicit_source
45
-
46
- # a collector instance handles all measurement addition/storage
47
- def_delegators :collector, :aggregate, :counters, :delete_all, :group, :increment,
48
- :measure, :prefix, :prefix=, :timing
49
-
50
- class << self
51
-
52
- # check to see if we've forked into a process where a worker
53
- # isn't running yet, if so start it up!
54
- def check_worker
55
- if @pid != $$
56
- start_worker
57
- # aggregate.clear
58
- # counters.clear
59
- end
60
- end
61
-
62
- # access to client instance
63
- def client
64
- @client ||= prepare_client
65
- end
66
-
67
- # collector instance which is tracking all measurement additions
68
- def collector
69
- @collector ||= Collector.new
70
- end
71
-
72
- # send all current data to Metrics
73
- def flush
74
- log :debug, "flushing pid #{@pid} (#{Time.now}).."
75
- start = Time.now
76
- queue = flush_queue
77
- # thread safety is handled internally for both stores
78
- counters.flush_to(queue)
79
- aggregate.flush_to(queue)
80
- trace_queued(queue.queued) if should_log?(:trace)
81
- queue.submit unless queue.empty?
82
- log :trace, "flushed pid #{@pid} in #{(Time.now - start)*1000.to_f}ms"
83
- rescue Exception => error
84
- log :error, "submission failed permanently: #{error}"
85
- end
86
-
87
- # source including process pid
88
- def qualified_source
89
- self.source_pids ? "#{source}.#{$$}" : source
90
- end
91
-
92
- # run once during Rails startup sequence
93
- def setup(app)
94
- check_config
95
- trace_settings if should_log?(:debug)
96
- return unless should_start?
97
- if app_server == :other
98
- log :info, "starting up..."
99
- else
100
- log :info, "starting up with #{app_server}..."
101
- end
102
- @pid = $$
103
- app.middleware.use Librato::Rack::Middleware
104
- start_worker unless forking_server?
105
- end
106
-
107
- def source
108
- return @source if @source
109
- self.explicit_source = false
110
- @source = Socket.gethostname
111
- end
112
-
113
- # set a custom source
114
- def source=(src)
115
- self.explicit_source = true
116
- @source = src
117
- end
118
-
119
- # start the worker thread, one is needed per process.
120
- # if this process has been forked from an one with an active
121
- # worker thread we don't need to worry about cleanup as only
122
- # the forking thread is copied.
123
- def start_worker
124
- return if @worker # already running
125
- @pid = $$
126
- log :debug, ">> starting up worker for pid #{@pid}..."
127
- @worker = Thread.new do
128
- worker = Worker.new
129
- worker.run_periodically(self.flush_interval) do
130
- flush
131
- end
132
- end
133
- end
134
-
135
- private
136
-
137
- def app_server
138
- if defined?(::Unicorn) && defined?(::Unicorn::HttpServer) && !::Unicorn.listener_names.empty?
139
- :unicorn
140
- elsif defined?(::IN_PHUSION_PASSENGER) || defined?(::PhusionPassenger)
141
- :passenger
142
- elsif defined?(::Thin) && defined?(::Thin::Server)
143
- :thin
144
- else
145
- :other
146
- end
147
- end
148
-
149
- def flush_queue
150
- ValidatingQueue.new(
151
- :client => client,
152
- :source => qualified_source,
153
- :prefix => self.prefix,
154
- :skip_measurement_times => true )
155
- end
156
-
157
- def forking_server?
158
- FORKING_SERVERS.include?(app_server)
159
- end
160
-
161
- def on_heroku
162
- # would be nice to have something more specific here,
163
- # but nothing characteristic in ENV, etc.
164
- @on_heroku ||= source_is_uuid?(Socket.gethostname)
165
- end
166
-
167
- def prepare_client
168
- check_config
169
- client = Librato::Metrics::Client.new
170
- client.authenticate user, token
171
- client.api_endpoint = @api_endpoint if @api_endpoint
172
- client.custom_user_agent = user_agent
173
- client
174
- end
175
-
176
- def ruby_engine
177
- return RUBY_ENGINE if Object.constants.include?(:RUBY_ENGINE)
178
- RUBY_DESCRIPTION.split[0]
179
- end
180
-
181
- def should_start?
182
- if !self.user || !self.token
183
- # don't show this unless we're debugging, expected behavior
184
- log :debug, 'halting: credentials not present.'
185
- false
186
- elsif qualified_source !~ SOURCE_REGEX
187
- log :warn, "halting: '#{qualified_source}' is an invalid source name."
188
- false
189
- elsif !explicit_source && on_heroku
190
- log :warn, 'halting: source must be provided in configuration.'
191
- false
192
- else
193
- true
194
- end
195
- end
196
-
197
- def source_is_uuid?(source)
198
- source =~ /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i
199
- end
200
-
201
- def user_agent
202
- ua_chunks = []
203
- ua_chunks << "librato-rails/#{Librato::Rails::VERSION}"
204
- ua_chunks << "(#{ruby_engine}; #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}; #{RUBY_PLATFORM}; #{app_server})"
205
- ua_chunks.join(' ')
206
- end
207
-
208
- end # end class << self
209
-
210
- end
211
- end
212
-
213
- # must load after all module setup
214
- require 'librato/rails/railtie' if defined?(Rails)
215
- require 'librato/rails/subscribers'
2
+ require_relative 'rails/configuration'
3
+ require_relative 'rails/tracker'
4
+ require_relative 'rails/version'
5
+
6
+ # must load after all module setup and in this order
7
+ if defined?(Rails)
8
+ require_relative 'rails/railtie'
9
+ require_relative 'rails/subscribers'
10
+ end
@@ -10,6 +10,3 @@
10
10
  // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
11
  // GO AFTER THE REQUIRES BELOW.
12
12
  //
13
- //= require jquery
14
- //= require jquery_ujs
15
- //= require_tree .
@@ -0,0 +1,44 @@
1
+ class CacheController < ApplicationController
2
+ before_filter :instrument_caching
3
+ after_filter :clear_cache
4
+
5
+ def read
6
+ Rails.cache.write('myfoo', 'bar', expires_in: 60.seconds)
7
+ Rails.cache.read('myfoo')
8
+ render :nothing => true
9
+ end
10
+
11
+ def write
12
+ Rails.cache.write('myfoo', 'bar', expires_in: 60.seconds)
13
+ render :nothing => true
14
+ end
15
+
16
+ def fetch_hit
17
+ Rails.cache.write('myfetch', 'bar', expires_in: 60.seconds)
18
+ Rails.cache.fetch('myfetch', expires_in: 60.seconds) { "populate" }
19
+ render :nothing => true
20
+ end
21
+
22
+ def generate
23
+ Rails.cache.fetch('newdata', expires_in: 60.seconds) { "populate" }
24
+ render :nothing => true
25
+ end
26
+
27
+ def delete
28
+ Rails.cache.write('something', 'bar', expires_in: 60.seconds)
29
+ Rails.cache.delete('something')
30
+ render :nothing => true
31
+ end
32
+
33
+ private
34
+
35
+ def clear_cache
36
+ Rails.cache.clear
37
+ end
38
+
39
+ def instrument_caching
40
+ # ensure caching instrumentation is turned on
41
+ Rails.cache.class.instrument = true
42
+ end
43
+
44
+ end
@@ -0,0 +1,4 @@
1
+ class RenderController < ApplicationController
2
+ def render_partial; end
3
+ def render_template; end
4
+ end
@@ -1,17 +1,19 @@
1
1
  class User < ActiveRecord::Base
2
- attr_accessible :email, :password
3
-
2
+ if Rails.version[0] == '3'
3
+ attr_accessible :email, :password
4
+ end
5
+
4
6
  def self.do_custom_events
5
7
  Librato.group 'custom.model' do |g|
6
8
  g.increment 'lookups', 3
7
-
9
+
8
10
  g.timing 'search', 12.3
9
11
  g.timing 'search', 6.7
10
-
12
+
11
13
  g.measure 'total', 12
12
14
  end
13
15
  end
14
-
16
+
15
17
  def touch
16
18
  Librato.increment 'custom.model.touch'
17
19
  end
@@ -0,0 +1 @@
1
+ first
@@ -0,0 +1 @@
1
+ second
@@ -0,0 +1,2 @@
1
+ <%= render :partial => 'first' %>
2
+ <%= render :partial => 'second' %>
@@ -0,0 +1 @@
1
+ Render that template
@@ -43,18 +43,20 @@ module Dummy
43
43
  # like if you have constraints or database-specific column types
44
44
  # config.active_record.schema_format = :sql
45
45
 
46
- # Enforce whitelist mode for mass assignment.
47
- # This will create an empty whitelist of attributes available for mass-assignment for all models
48
- # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
49
- # parameters by using an attr_accessible or attr_protected declaration.
50
- config.active_record.whitelist_attributes = true
46
+ if Rails.version[0] == '3'
47
+ # Enforce whitelist mode for mass assignment.
48
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
49
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
50
+ # parameters by using an attr_accessible or attr_protected declaration.
51
+ config.active_record.whitelist_attributes = true
52
+ end
51
53
 
52
54
  # Enable the asset pipeline
53
55
  config.assets.enabled = true
54
56
 
55
57
  # Version of your assets, change this if you want to expire all your assets
56
58
  config.assets.version = '1.0'
57
-
59
+
58
60
  # set librato_rails prefix
59
61
  # config.librato_rails.prefix = 'dummy'
60
62
  config.librato_rails.flush_interval = 5
@@ -30,7 +30,7 @@ Dummy::Application.configure do
30
30
  config.action_mailer.delivery_method = :test
31
31
 
32
32
  # Raise exception on mass assignment protection for Active Record models
33
- config.active_record.mass_assignment_sanitizer = :strict
33
+ # config.active_record.mass_assignment_sanitizer = :strict
34
34
 
35
35
  # Print deprecation notices to the stderr
36
36
  config.active_support.deprecation = :stderr
@@ -1,13 +1,22 @@
1
1
  Dummy::Application.routes.draw do
2
2
 
3
3
  get "status/:code" => 'status#index'
4
-
4
+
5
5
  get 'exception' => 'home#boom', :as => :exception
6
6
  get 'slow' => 'home#slow', :as => :slow
7
7
  get 'custom' => 'home#custom', :as => :custom
8
-
8
+
9
9
  get 'user/manipulation' => 'user#manipulation', :as => :user_manipulation
10
10
 
11
+ get 'cache/read' => 'cache#read', :as => :cache_read
12
+ get 'cache/write' => 'cache#write', :as => :cache_write
13
+ get 'cache/fetch_hit' => 'cache#fetch_hit', :as => :cache_fetch_hit
14
+ get 'cache/generate' => 'cache#generate', :as => :cache_generate
15
+ get 'cache/delete' => 'cache#delete', :as => :cache_delete
16
+
17
+ get 'render/partial' => 'render#partial', :as => :render_partial
18
+ get 'render/template' => 'render#template', :as => :render_template
19
+
11
20
  # The priority is based upon order of creation:
12
21
  # first created -> highest priority.
13
22
 
@@ -58,5 +67,5 @@ Dummy::Application.routes.draw do
58
67
  # You can have the root of your site routed with "root"
59
68
  # just remember to delete public/index.html.
60
69
  root :to => 'home#index'
61
-
70
+
62
71
  end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class CacheTest < ActiveSupport::IntegrationCase
4
+
5
+ test 'cache read' do
6
+ visit cache_read_path
7
+
8
+ assert_equal 1, counters["rails.cache.read"]
9
+ assert_equal 1, aggregate["rails.cache.read.time"][:count]
10
+ end
11
+
12
+ test 'cache write' do
13
+ visit cache_write_path
14
+
15
+ assert_equal 1, counters["rails.cache.write"]
16
+ assert_equal 1, aggregate["rails.cache.write.time"][:count]
17
+ end
18
+
19
+ test 'cache fetch_hit' do
20
+ visit cache_fetch_hit_path
21
+
22
+ assert_equal 1, counters["rails.cache.fetch_hit"]
23
+ assert_equal 1, aggregate["rails.cache.fetch_hit.time"][:count]
24
+ end
25
+
26
+ test 'cache generate' do
27
+ visit cache_generate_path
28
+
29
+ assert_equal 1, counters["rails.cache.generate"]
30
+ assert_equal 1, aggregate["rails.cache.generate.time"][:count]
31
+ end
32
+
33
+ test 'cache delete' do
34
+ visit cache_delete_path
35
+
36
+ assert_equal 1, counters["rails.cache.delete"]
37
+ assert_equal 1, aggregate["rails.cache.delete.time"][:count]
38
+ end
39
+
40
+ end
@@ -1,13 +1,11 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class MailTest < ActiveSupport::IntegrationCase
4
-
5
- # Query tests - the numbers specified assume running against SQLite
6
-
4
+
7
5
  test 'mail sent' do
8
6
  user = User.create!(:email => 'foo@foo.com', :password => 'wow')
9
7
  UserMailer.welcome_email(user).deliver
10
8
  assert_equal 1, counters["rails.mail.sent"]
11
9
  end
12
-
10
+
13
11
  end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ class RenderTest < ActiveSupport::IntegrationCase
4
+
5
+ test 'render partial' do
6
+ visit render_partial_path
7
+
8
+ assert_equal 1, counters.fetch("rails.view.render_partial",
9
+ source: 'render:first.html.erb')
10
+ assert_equal 1, counters.fetch("rails.view.render_partial",
11
+ source: 'render:second.html.erb')
12
+ assert_equal 1, aggregate.fetch("rails.view.render_partial.time",
13
+ source: 'render:first.html.erb')[:count]
14
+ assert_equal 1, aggregate.fetch("rails.view.render_partial.time",
15
+ source: 'render:second.html.erb')[:count]
16
+ end
17
+
18
+ test 'render template' do
19
+ visit render_template_path
20
+
21
+ assert_equal 1, counters.fetch("rails.view.render_template",
22
+ source: 'render:template.html.erb')
23
+ assert_equal 1, aggregate.fetch("rails.view.render_template.time",
24
+ source: 'render:template.html.erb')[:count]
25
+ end
26
+
27
+ end
@@ -1,37 +1,41 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class RequestTest < ActiveSupport::IntegrationCase
4
-
4
+
5
5
  # Each request
6
-
6
+
7
7
  test 'increment total and status' do
8
8
  visit root_path
9
-
9
+
10
10
  assert_equal 1, counters["rails.request.total"]
11
11
  assert_equal 1, counters["rails.request.status.200"]
12
12
  assert_equal 1, counters["rails.request.status.2xx"]
13
-
13
+ assert_equal 1, counters["rails.request.method.get"]
14
+
14
15
  visit '/status/204'
15
-
16
+
16
17
  assert_equal 2, counters["rails.request.total"]
17
18
  assert_equal 1, counters["rails.request.status.200"]
18
19
  assert_equal 1, counters["rails.request.status.204"]
19
20
  assert_equal 2, counters["rails.request.status.2xx"]
20
21
  end
21
-
22
+
22
23
  test 'request times' do
23
24
  visit root_path
24
-
25
+
25
26
  # common for all paths
26
27
  assert_equal 1, aggregate["rails.request.time"][:count], 'should record total time'
27
28
  assert_equal 1, aggregate["rails.request.time.db"][:count], 'should record db time'
28
29
  assert_equal 1, aggregate["rails.request.time.view"][:count], 'should record view time'
29
-
30
+
30
31
  # status specific
31
32
  assert_equal 1, aggregate["rails.request.status.200.time"][:count]
32
33
  assert_equal 1, aggregate["rails.request.status.2xx.time"][:count]
34
+
35
+ # http method specific
36
+ assert_equal 1, aggregate["rails.request.method.get.time"][:count]
33
37
  end
34
-
38
+
35
39
  test 'track exceptions' do
36
40
  begin
37
41
  visit exception_path #rescue nil
@@ -40,10 +44,10 @@ class RequestTest < ActiveSupport::IntegrationCase
40
44
  end
41
45
  assert_equal 1, counters["rails.request.exceptions"]
42
46
  end
43
-
47
+
44
48
  test 'track slow requests' do
45
49
  visit slow_path
46
50
  assert_equal 1, counters["rails.request.slow"]
47
51
  end
48
-
52
+
49
53
  end
@@ -1,28 +1,28 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class SQLTest < ActiveSupport::IntegrationCase
4
-
4
+
5
5
  # Query tests - the numbers specified assume running against SQLite
6
-
6
+
7
7
  test 'total queries and query types' do
8
8
  # note that modifying queries are wrapped in a transaction which
9
9
  # adds 2 to total queries per operation.
10
10
  user = User.create!(:email => 'foo@foo.com', :password => 'wow')
11
11
  assert_equal 3, counters["rails.sql.queries"]
12
12
  assert_equal 1, counters["rails.sql.inserts"]
13
-
13
+
14
14
  foo = User.find_by_email('foo@foo.com')
15
15
  assert_equal 4, counters["rails.sql.queries"]
16
16
  assert_equal 1, counters["rails.sql.selects"]
17
-
17
+
18
18
  foo.password = 'new password'
19
19
  foo.save
20
20
  assert_equal 7, counters["rails.sql.queries"]
21
21
  assert_equal 1, counters["rails.sql.updates"]
22
-
22
+
23
23
  foo.destroy
24
24
  assert_equal 10, counters["rails.sql.queries"]
25
25
  assert_equal 1, counters["rails.sql.deletes"]
26
26
  end
27
-
27
+
28
28
  end
@@ -1,19 +1,23 @@
1
1
  class ActiveSupport::IntegrationCase < ActiveSupport::TestCase
2
2
  include Capybara::DSL
3
3
  include Rails.application.routes.url_helpers
4
-
4
+
5
5
  setup do
6
6
  # remove any accumulated metrics
7
- Librato::Rails.delete_all
7
+ collector.delete_all
8
8
  end
9
-
9
+
10
10
  private
11
-
11
+
12
12
  def aggregate
13
- Librato::Rails.aggregate
13
+ collector.aggregate
14
14
  end
15
-
15
+
16
+ def collector
17
+ Librato.tracker.collector
18
+ end
19
+
16
20
  def counters
17
- Librato::Rails.counters
21
+ collector.counters
18
22
  end
19
23
  end