librato-rails 0.3.0

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 (101) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +105 -0
  3. data/Rakefile +41 -0
  4. data/lib/librato-rails.rb +1 -0
  5. data/lib/librato/rack.rb +4 -0
  6. data/lib/librato/rack/middleware.rb +47 -0
  7. data/lib/librato/rails.rb +209 -0
  8. data/lib/librato/rails/aggregator.rb +48 -0
  9. data/lib/librato/rails/counter_cache.rb +41 -0
  10. data/lib/librato/rails/group.rb +27 -0
  11. data/lib/librato/rails/helpers.rb +19 -0
  12. data/lib/librato/rails/railtie.rb +18 -0
  13. data/lib/librato/rails/subscribers.rb +77 -0
  14. data/lib/librato/rails/version.rb +5 -0
  15. data/lib/librato/rails/worker.rb +58 -0
  16. data/lib/tasks/metrics-rails_tasks.rake +4 -0
  17. data/test/dummy/README.rdoc +261 -0
  18. data/test/dummy/Rakefile +7 -0
  19. data/test/dummy/app/assets/javascripts/application.js +15 -0
  20. data/test/dummy/app/assets/javascripts/home.js +2 -0
  21. data/test/dummy/app/assets/javascripts/status.js +2 -0
  22. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  23. data/test/dummy/app/assets/stylesheets/home.css +4 -0
  24. data/test/dummy/app/assets/stylesheets/status.css +4 -0
  25. data/test/dummy/app/controllers/application_controller.rb +10 -0
  26. data/test/dummy/app/controllers/home_controller.rb +33 -0
  27. data/test/dummy/app/controllers/status_controller.rb +5 -0
  28. data/test/dummy/app/controllers/user_controller.rb +13 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/helpers/home_helper.rb +2 -0
  31. data/test/dummy/app/helpers/status_helper.rb +2 -0
  32. data/test/dummy/app/mailers/user_mailer.rb +8 -0
  33. data/test/dummy/app/models/user.rb +18 -0
  34. data/test/dummy/app/views/home/index.html.erb +2 -0
  35. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/test/dummy/app/views/status/code.html.erb +2 -0
  37. data/test/dummy/app/views/user_mailer/welcome_email.text.erb +1 -0
  38. data/test/dummy/config.ru +4 -0
  39. data/test/dummy/config/application.rb +63 -0
  40. data/test/dummy/config/boot.rb +10 -0
  41. data/test/dummy/config/database.yml +25 -0
  42. data/test/dummy/config/environment.rb +5 -0
  43. data/test/dummy/config/environments/development.rb +37 -0
  44. data/test/dummy/config/environments/production.rb +67 -0
  45. data/test/dummy/config/environments/test.rb +37 -0
  46. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  47. data/test/dummy/config/initializers/inflections.rb +15 -0
  48. data/test/dummy/config/initializers/mime_types.rb +5 -0
  49. data/test/dummy/config/initializers/secret_token.rb +7 -0
  50. data/test/dummy/config/initializers/session_store.rb +8 -0
  51. data/test/dummy/config/initializers/silence_assets.rb +16 -0
  52. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  53. data/test/dummy/config/locales/en.yml +5 -0
  54. data/test/dummy/config/routes.rb +62 -0
  55. data/test/dummy/config/unicorn.rb +16 -0
  56. data/test/dummy/db/development.sqlite3 +0 -0
  57. data/test/dummy/db/migrate/20120719231810_create_users.rb +10 -0
  58. data/test/dummy/db/schema.rb +23 -0
  59. data/test/dummy/db/test.sqlite3 +0 -0
  60. data/test/dummy/log/development.log +23936 -0
  61. data/test/dummy/log/test.log +17942 -0
  62. data/test/dummy/public/404.html +26 -0
  63. data/test/dummy/public/422.html +26 -0
  64. data/test/dummy/public/500.html +25 -0
  65. data/test/dummy/public/favicon.ico +0 -0
  66. data/test/dummy/script/rails +6 -0
  67. data/test/dummy/test_env.sh +2 -0
  68. data/test/dummy/tmp/cache/assets/C94/D50/sprockets%2F1a49716f234a1b88976e3f09954f8f14 +0 -0
  69. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  70. data/test/dummy/tmp/cache/assets/CDF/870/sprockets%2Fb878faf942403e313a5b103e5d80488e +0 -0
  71. data/test/dummy/tmp/cache/assets/CE8/7E0/sprockets%2F178e2a1f9aa891d473009c7f3095df28 +0 -0
  72. data/test/dummy/tmp/cache/assets/CF9/7C0/sprockets%2F40fc2f3d2a468a00e463f1d313cb1683 +0 -0
  73. data/test/dummy/tmp/cache/assets/D04/890/sprockets%2F587335c079eef8d5a63784fc8f99905a +0 -0
  74. data/test/dummy/tmp/cache/assets/D05/D40/sprockets%2F1c9faaf28d05409b88ad3113374d613c +0 -0
  75. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  76. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  77. data/test/dummy/tmp/cache/assets/D4F/000/sprockets%2F25e44896aac12384727e9dab827ebef9 +0 -0
  78. data/test/dummy/tmp/cache/assets/D51/0D0/sprockets%2Fe895d60a653d8b87f7c5717a4d4cf1f3 +0 -0
  79. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  80. data/test/dummy/tmp/cache/assets/D8B/F90/sprockets%2Ffe6ce696e9141eb755d8eed79128e17c +0 -0
  81. data/test/dummy/tmp/cache/assets/D98/8B0/sprockets%2Fedbef6e0d0a4742346cf479f2c522eb0 +0 -0
  82. data/test/dummy/tmp/cache/assets/DA1/8B0/sprockets%2F65acae5ede4e92f105c5e8631407c7fc +0 -0
  83. data/test/dummy/tmp/cache/assets/DA5/7B0/sprockets%2Fa0fc1785d4dc1bde68dd7d5652d27a95 +0 -0
  84. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  85. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  86. data/test/fixtures/config/librato.yml +10 -0
  87. data/test/integration/helper_test.rb +29 -0
  88. data/test/integration/mail_test.rb +15 -0
  89. data/test/integration/request_test.rb +53 -0
  90. data/test/integration/sql_test.rb +30 -0
  91. data/test/metrics-rails_test.rb +39 -0
  92. data/test/remote/rails_remote_test.rb +105 -0
  93. data/test/support/integration_case.rb +19 -0
  94. data/test/test_helper.rb +23 -0
  95. data/test/unit/aggregator_test.rb +16 -0
  96. data/test/unit/configuration_test.rb +49 -0
  97. data/test/unit/counter_cache_test.rb +19 -0
  98. data/test/unit/group_test.rb +49 -0
  99. data/test/unit/middleware_test.rb +82 -0
  100. data/test/unit/worker_test.rb +31 -0
  101. metadata +300 -0
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012. Librato, Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the <organization> nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL LIBRATO, INC. BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ librato-rails
2
+ =======
3
+
4
+ [![Build Status](https://secure.travis-ci.org/librato/librato-rails.png?branch=master)](http://travis-ci.org/librato/librato-rails)
5
+
6
+ Report key statistics for your Rails app to [Librato Metrics](https://metrics.librato.com/), easily track your own custom metrics. Currently supports Rails 3.0+.
7
+
8
+ **NOTE: This is currently in alpha development and is not yet officially supported**
9
+
10
+ ## Installation
11
+
12
+ In your `Gemfile` add:
13
+
14
+ gem 'librato-rails'
15
+
16
+ Then run `bundle install`.
17
+
18
+ ## Configuration
19
+
20
+ If you don't have a Metrics account already, [sign up](https://metrics.librato.com/). In order to send measurements to Metrics you need to provide your account credentials to `librato-rails`. You can provide these one of two ways:
21
+
22
+ Create a `config/librato.yml` like the following:
23
+
24
+ production:
25
+ email: <your-email>
26
+ api_key: <your-api-key>
27
+
28
+ OR provide `LIBRATO_METRICS_USER` and `LIBRATO_METRICS_TOKEN` environment variables. If both env variables and a config file are present, environment variables will take precendence.
29
+
30
+ Note that using a configuration file allows you to specify configurations per-environment. Submission will be disabled in any environment without credentials. However, if environment variables are set they will be used in all environments.
31
+
32
+ Full information on configuration options is available on the [configuration wiki page](https://github.com/librato/librato-rails/wiki/Configuration).
33
+
34
+ ## Automatic Measurements
35
+
36
+ After installing `librato-rails` and restarting your app and you will see a number of new metrics appear in your Metrics account. These track request performance, sql queries, mail handling, and other key stats. All built-in performance metrics start with the prefix `rails` by convention &mdash; for example: `rails.request.total` is the total number of requests received during an interval.
37
+
38
+ If you have multiple apps reporting to the same Metrics account you can change this prefix in your [configuration](https://github.com/librato/librato-rails/wiki/Configuration).
39
+
40
+ ## Custom Measurements
41
+
42
+ Tracking anything that interests you is easy with Metrics. There are four primary helpers available:
43
+
44
+ #### increment
45
+
46
+ Use for tracking a running total of something _across_ requests, examples:
47
+
48
+ # increment the 'sales_completed' metric by one
49
+ Librato.increment 'sales_completed'
50
+
51
+ # increment by five
52
+ Librato.increment 'items_purchased', 5
53
+
54
+ Other things you might track this way: user signups, requests of a certain type or to a certain route, total jobs queued or processed, emails sent or received
55
+
56
+ #### measure
57
+
58
+ Use when you want to track an average value _per_-request. Examples:
59
+
60
+ Librato.measure 'user.social_graph.nodes', 212
61
+
62
+ Librato.measure 'jobs.queued', 3
63
+
64
+
65
+ #### timing
66
+
67
+ Like `Librato.measure` this is per-request, but specialized for timing information:
68
+
69
+ Librato.timing 'twitter.lookup.time', 21.2
70
+
71
+ The block form auto-submits the time it took for its contents to execute as the measurement value:
72
+
73
+ Librato.timing 'twitter.lookup.time' do
74
+ @twitter = Twitter.lookup(user)
75
+ end
76
+
77
+ #### group
78
+
79
+ There is also a grouping helper, to make managing nested metrics easier. So this:
80
+
81
+ Librato.measure 'memcached.gets', 20
82
+ Librato.measure 'memcached.sets', 2
83
+ Librato.measure 'memcached.hits', 18
84
+
85
+ Can also be written as:
86
+
87
+ Librato.group 'memcached' do |g|
88
+ g.measure 'gets', 20
89
+ g.measure 'sets', 2
90
+ g.measure 'hits', 18
91
+ end
92
+
93
+ Symbols can be used interchangably with strings for metric names.
94
+
95
+ ## Contribution
96
+
97
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
98
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
99
+ * Fork the project and submit a pull request from a feature or bugfix branch.
100
+ * Please include tests. This is important so we don't break your changes unintentionally in a future version.
101
+ * Please don't modify the gemspec, Rakefile, version, or changelog. If you do change these files, please isolate a separate commit so we can cherry-pick around it.
102
+
103
+ ## Copyright
104
+
105
+ Copyright (c) 2012 [Librato Inc.](http://librato.com) See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'LibratoRails'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ # IRB
24
+ desc "Open an irb session preloaded with this library"
25
+ task :console do
26
+ sh "pry -rubygems -r rails -r ./lib/librato-rails.rb"
27
+ end
28
+
29
+ Bundler::GemHelper.install_tasks
30
+
31
+ require 'rake/testtask'
32
+
33
+ Rake::TestTask.new(:test) do |t|
34
+ t.libs << 'lib'
35
+ t.libs << 'test'
36
+ t.pattern = 'test/**/*_test.rb'
37
+ t.verbose = false
38
+ end
39
+
40
+
41
+ task :default => :test
@@ -0,0 +1 @@
1
+ require 'librato/rails'
@@ -0,0 +1,4 @@
1
+ module Librato::Rack
2
+ end
3
+
4
+ require 'librato/rack/middleware'
@@ -0,0 +1,47 @@
1
+ class Librato::Rack::Middleware
2
+ def initialize(app, metrics = Librato::Rails)
3
+ @app, @metrics = app, metrics
4
+ end
5
+
6
+ def call(env)
7
+ @metrics.check_worker if @metrics.send(:forking_server?)
8
+
9
+ header_metrics env
10
+
11
+ time = Time.now
12
+ response = @app.call(env)
13
+ duration = (Time.now - time) * 1000.0
14
+
15
+ request_metrics response.first, duration
16
+
17
+ response
18
+ end
19
+
20
+ private
21
+
22
+ def header_metrics(env)
23
+ return unless env.keys.include?('HTTP_X_HEROKU_QUEUE_DEPTH')
24
+
25
+ @metrics.group 'rack.heroku' do |group|
26
+ group.measure 'queue.depth', env['HTTP_X_HEROKU_QUEUE_DEPTH'].to_f
27
+ group.timing 'queue.wait_time', env['HTTP_X_HEROKU_QUEUE_WAIT_TIME'].to_f
28
+ group.measure 'queue.dynos', env['HTTP_X_HEROKU_DYNOS_IN_USE'].to_f
29
+ end
30
+ end
31
+
32
+ def request_metrics(status, duration)
33
+ @metrics.group 'rack.request' do |group|
34
+ group.increment 'total'
35
+ group.timing 'time', duration
36
+ group.increment 'slow' if duration > 200.0
37
+
38
+ group.group 'status' do |s|
39
+ s.increment status
40
+ s.increment "#{status.to_s[0]}xx"
41
+
42
+ s.timing "#{status}.time", duration
43
+ s.timing "#{status.to_s[0]}xx.time", duration
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,209 @@
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
+ require 'librato/rack'
9
+ require 'librato/rails/aggregator'
10
+ require 'librato/rails/counter_cache'
11
+ require 'librato/rails/group'
12
+ require 'librato/rails/helpers'
13
+ require 'librato/rails/worker'
14
+ require 'librato/rails/version'
15
+
16
+ module Librato
17
+ extend SingleForwardable
18
+ def_delegators Librato::Rails, :increment, :measure, :timing, :group
19
+
20
+ module Rails
21
+ extend SingleForwardable
22
+ CONFIG_SETTABLE = %w{api_key email flush_interval prefix source}
23
+ FORKING_SERVERS = [:unicorn, :passenger]
24
+
25
+ mattr_accessor :config_file
26
+ self.config_file = 'config/librato.yml'
27
+
28
+ # config options
29
+ mattr_accessor :api_key
30
+ mattr_accessor :email
31
+ mattr_accessor :flush_interval
32
+ mattr_accessor :prefix
33
+
34
+ # config defaults
35
+ self.flush_interval = 60 # seconds
36
+ self.prefix = 'rails'
37
+
38
+ def_delegators :counters, :increment
39
+ def_delegators :aggregate, :measure, :timing
40
+
41
+ class << self
42
+
43
+ # access to internal aggregator object
44
+ def aggregate
45
+ @aggregator_cache ||= Aggregator.new
46
+ end
47
+
48
+ # set custom api endpoint
49
+ def api_endpoint=(endpoint)
50
+ @api_endpoint = endpoint
51
+ end
52
+
53
+ # access to client instance
54
+ def client
55
+ @client ||= prepare_client
56
+ end
57
+
58
+ # detect / update configuration
59
+ def check_config
60
+ if self.config_file && File.exists?(self.config_file)
61
+ configs = YAML.load_file(config_file)
62
+ if env_specific = configs[::Rails.env]
63
+ settable = CONFIG_SETTABLE & env_specific.keys
64
+ settable.each { |key| self.send("#{key}=", env_specific[key]) }
65
+ end
66
+ end
67
+ self.api_key = ENV['LIBRATO_METRICS_TOKEN'] if ENV['LIBRATO_METRICS_TOKEN']
68
+ self.email = ENV['LIBRATO_METRICS_USER'] if ENV['LIBRATO_METRICS_USER']
69
+ end
70
+
71
+ # check to see if we've forked into a process where a worker
72
+ # isn't running yet, if so start it up!
73
+ def check_worker
74
+ if @pid != $$
75
+ start_worker
76
+ #aggregate.clear
77
+ #counters.clear
78
+ end
79
+ end
80
+
81
+ # access to internal counters object
82
+ def counters
83
+ @counter_cache ||= CounterCache.new
84
+ end
85
+
86
+ # remove any accumulated but unsent metrics
87
+ def delete_all
88
+ aggregate.delete_all
89
+ counters.delete_all
90
+ end
91
+
92
+ # send all current data to Metrics
93
+ def flush
94
+ logger.debug "[librato-rails] flushing #{Process.pid} (#{Time.now}):"
95
+ queue = client.new_queue(:source => qualified_source)
96
+ # thread safety is handled internally for both stores
97
+ counters.flush_to(queue)
98
+ aggregate.flush_to(queue)
99
+ logger.debug queue.queued
100
+ queue.submit unless queue.empty?
101
+ rescue Exception => error
102
+ logger.error "[librato-rails] submission failed permanently, worker exiting: #{error}"
103
+ end
104
+
105
+ def group(prefix)
106
+ group = Group.new(prefix)
107
+ yield group
108
+ end
109
+
110
+ def logger
111
+ @logger ||= ::Rails.logger
112
+ end
113
+
114
+ # source including process pid
115
+ def qualified_source
116
+ "#{source}.#{$$}"
117
+ end
118
+
119
+ # run once during Rails startup sequence
120
+ def setup
121
+ check_config
122
+ #return unless self.email && self.api_key
123
+ logger.info "[librato-rails] starting up with #{app_server}..."
124
+ @pid = $$
125
+ if forking_server?
126
+ install_worker_check
127
+ else
128
+ start_worker # start immediately
129
+ end
130
+ end
131
+
132
+ def source
133
+ @source ||= Socket.gethostname
134
+ end
135
+
136
+ # set a custom source
137
+ def source=(src)
138
+ @source = src
139
+ end
140
+
141
+ # start the worker thread, one is needed per process.
142
+ # if this process has been forked from an one with an active
143
+ # worker thread we don't need to worry about cleanup as only
144
+ # the forking thread is copied.
145
+ def start_worker
146
+ return if @worker # already running
147
+ @pid = $$
148
+ logger.debug "[librato-rails] >> starting up worker for pid #{@pid}..."
149
+ @worker = Thread.new do
150
+ worker = Worker.new
151
+ worker.run_periodically(self.flush_interval) do
152
+ flush
153
+ end
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def app_server
160
+ if defined?(::Unicorn) && defined?(::Unicorn::HttpServer) && !::Unicorn.listener_names.empty?
161
+ :unicorn
162
+ elsif defined?(::IN_PHUSION_PASSENGER) || (defined?(::Passenger) && defined?(::Passenger::AbstractServer))
163
+ :passenger
164
+ elsif defined?(::Thin) && defined?(::Thin::Server)
165
+ :thin
166
+ else
167
+ :other
168
+ end
169
+ end
170
+
171
+ def forking_server?
172
+ FORKING_SERVERS.include?(app_server)
173
+ end
174
+
175
+ def install_worker_check
176
+ ::ApplicationController.prepend_before_filter do |c|
177
+ Librato::Rails.check_worker
178
+ end
179
+ end
180
+
181
+ def prepare_client
182
+ check_config
183
+ client = Librato::Metrics::Client.new
184
+ client.authenticate email, api_key
185
+ client.api_endpoint = @api_endpoint if @api_endpoint
186
+ client.custom_user_agent = user_agent
187
+ client
188
+ end
189
+
190
+ def ruby_engine
191
+ return RUBY_ENGINE if Object.constants.include?(:RUBY_ENGINE)
192
+ RUBY_DESCRIPTION.split[0]
193
+ end
194
+
195
+ def user_agent
196
+ ua_chunks = []
197
+ ua_chunks << "librato-rails/#{Librato::Rails::VERSION}"
198
+ ua_chunks << "(#{ruby_engine}; #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}; #{RUBY_PLATFORM}; #{app_server})"
199
+ ua_chunks.join(' ')
200
+ end
201
+
202
+ end # end class << self
203
+
204
+ end
205
+ end
206
+
207
+ # must load after all module setup
208
+ require 'librato/rails/railtie' if defined?(Rails)
209
+ require 'librato/rails/subscribers'
@@ -0,0 +1,48 @@
1
+ module Librato
2
+ module Rails
3
+ class Aggregator
4
+ extend Forwardable
5
+
6
+ def_delegators :@cache, :empty?
7
+
8
+ def initialize
9
+ @cache = Librato::Metrics::Aggregator.new
10
+ @lock = Mutex.new
11
+ end
12
+
13
+ def [](key)
14
+ return nil if @cache.empty?
15
+ gauges = nil
16
+ @lock.synchronize { gauges = @cache.queued[:gauges] }
17
+ gauges.each do |metric|
18
+ return metric if metric[:name] == key.to_s
19
+ end
20
+ nil
21
+ end
22
+
23
+ def delete_all
24
+ @lock.synchronize { @cache.clear }
25
+ end
26
+
27
+ # transfer all measurements to a queue and
28
+ # reset internal status
29
+ def flush_to(queue, options={})
30
+ queued = nil
31
+ @lock.synchronize do
32
+ return if @cache.empty?
33
+ queued = @cache.queued
34
+ @cache.clear
35
+ end
36
+ queue.merge!(queued) if queued
37
+ end
38
+
39
+ def measure(event, duration)
40
+ @lock.synchronize do
41
+ @cache.add event.to_s => duration
42
+ end
43
+ end
44
+ alias :timing :measure
45
+
46
+ end
47
+ end
48
+ end