metrics-rails 0.1.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 (98) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +105 -0
  3. data/Rakefile +41 -0
  4. data/lib/metrics-rails.rb +1 -0
  5. data/lib/metrics/rails.rb +226 -0
  6. data/lib/metrics/rails/aggregator.rb +48 -0
  7. data/lib/metrics/rails/counter_cache.rb +41 -0
  8. data/lib/metrics/rails/group.rb +27 -0
  9. data/lib/metrics/rails/helpers.rb +19 -0
  10. data/lib/metrics/rails/railtie.rb +17 -0
  11. data/lib/metrics/rails/subscribers.rb +77 -0
  12. data/lib/metrics/rails/version.rb +5 -0
  13. data/lib/metrics/rails/worker.rb +58 -0
  14. data/lib/tasks/metrics-rails_tasks.rake +4 -0
  15. data/test/dummy/README.rdoc +261 -0
  16. data/test/dummy/Rakefile +7 -0
  17. data/test/dummy/app/assets/javascripts/application.js +15 -0
  18. data/test/dummy/app/assets/javascripts/home.js +2 -0
  19. data/test/dummy/app/assets/javascripts/status.js +2 -0
  20. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  21. data/test/dummy/app/assets/stylesheets/home.css +4 -0
  22. data/test/dummy/app/assets/stylesheets/status.css +4 -0
  23. data/test/dummy/app/controllers/application_controller.rb +10 -0
  24. data/test/dummy/app/controllers/home_controller.rb +33 -0
  25. data/test/dummy/app/controllers/status_controller.rb +5 -0
  26. data/test/dummy/app/controllers/user_controller.rb +13 -0
  27. data/test/dummy/app/helpers/application_helper.rb +2 -0
  28. data/test/dummy/app/helpers/home_helper.rb +2 -0
  29. data/test/dummy/app/helpers/status_helper.rb +2 -0
  30. data/test/dummy/app/mailers/user_mailer.rb +8 -0
  31. data/test/dummy/app/models/user.rb +18 -0
  32. data/test/dummy/app/views/home/index.html.erb +2 -0
  33. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  34. data/test/dummy/app/views/status/code.html.erb +2 -0
  35. data/test/dummy/app/views/user_mailer/welcome_email.text.erb +1 -0
  36. data/test/dummy/config.ru +4 -0
  37. data/test/dummy/config/application.rb +63 -0
  38. data/test/dummy/config/boot.rb +10 -0
  39. data/test/dummy/config/database.yml +25 -0
  40. data/test/dummy/config/environment.rb +5 -0
  41. data/test/dummy/config/environments/development.rb +37 -0
  42. data/test/dummy/config/environments/production.rb +67 -0
  43. data/test/dummy/config/environments/test.rb +37 -0
  44. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/test/dummy/config/initializers/inflections.rb +15 -0
  46. data/test/dummy/config/initializers/mime_types.rb +5 -0
  47. data/test/dummy/config/initializers/secret_token.rb +7 -0
  48. data/test/dummy/config/initializers/session_store.rb +8 -0
  49. data/test/dummy/config/initializers/silence_assets.rb +16 -0
  50. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/test/dummy/config/locales/en.yml +5 -0
  52. data/test/dummy/config/routes.rb +62 -0
  53. data/test/dummy/config/unicorn.rb +16 -0
  54. data/test/dummy/db/development.sqlite3 +0 -0
  55. data/test/dummy/db/migrate/20120719231810_create_users.rb +10 -0
  56. data/test/dummy/db/schema.rb +23 -0
  57. data/test/dummy/db/test.sqlite3 +0 -0
  58. data/test/dummy/log/development.log +161447 -0
  59. data/test/dummy/log/test.log +19173 -0
  60. data/test/dummy/public/404.html +26 -0
  61. data/test/dummy/public/422.html +26 -0
  62. data/test/dummy/public/500.html +25 -0
  63. data/test/dummy/public/favicon.ico +0 -0
  64. data/test/dummy/script/rails +6 -0
  65. data/test/dummy/test_env.sh +2 -0
  66. data/test/dummy/tmp/cache/assets/C94/D50/sprockets%2F1a49716f234a1b88976e3f09954f8f14 +0 -0
  67. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  68. data/test/dummy/tmp/cache/assets/CDF/870/sprockets%2Fb878faf942403e313a5b103e5d80488e +0 -0
  69. data/test/dummy/tmp/cache/assets/CE8/7E0/sprockets%2F178e2a1f9aa891d473009c7f3095df28 +0 -0
  70. data/test/dummy/tmp/cache/assets/CF9/7C0/sprockets%2F40fc2f3d2a468a00e463f1d313cb1683 +0 -0
  71. data/test/dummy/tmp/cache/assets/D04/890/sprockets%2F587335c079eef8d5a63784fc8f99905a +0 -0
  72. data/test/dummy/tmp/cache/assets/D05/D40/sprockets%2F1c9faaf28d05409b88ad3113374d613c +0 -0
  73. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  74. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  75. data/test/dummy/tmp/cache/assets/D4F/000/sprockets%2F25e44896aac12384727e9dab827ebef9 +0 -0
  76. data/test/dummy/tmp/cache/assets/D51/0D0/sprockets%2Fe895d60a653d8b87f7c5717a4d4cf1f3 +0 -0
  77. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  78. data/test/dummy/tmp/cache/assets/D8B/F90/sprockets%2Ffe6ce696e9141eb755d8eed79128e17c +0 -0
  79. data/test/dummy/tmp/cache/assets/D98/8B0/sprockets%2Fedbef6e0d0a4742346cf479f2c522eb0 +0 -0
  80. data/test/dummy/tmp/cache/assets/DA1/8B0/sprockets%2F65acae5ede4e92f105c5e8631407c7fc +0 -0
  81. data/test/dummy/tmp/cache/assets/DA5/7B0/sprockets%2Fa0fc1785d4dc1bde68dd7d5652d27a95 +0 -0
  82. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  83. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  84. data/test/fixtures/config/metrics.yml +10 -0
  85. data/test/integration/helper_test.rb +29 -0
  86. data/test/integration/mail_test.rb +15 -0
  87. data/test/integration/request_test.rb +53 -0
  88. data/test/integration/sql_test.rb +30 -0
  89. data/test/metrics-rails_test.rb +39 -0
  90. data/test/remote/rails_remote_test.rb +105 -0
  91. data/test/support/integration_case.rb +19 -0
  92. data/test/test_helper.rb +23 -0
  93. data/test/unit/aggregator_test.rb +16 -0
  94. data/test/unit/configuration_test.rb +49 -0
  95. data/test/unit/counter_cache_test.rb +19 -0
  96. data/test/unit/group_test.rb +49 -0
  97. data/test/unit/worker_test.rb +31 -0
  98. metadata +280 -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.
@@ -0,0 +1,105 @@
1
+ metrics-rails
2
+ =======
3
+
4
+ [![Build Status](https://secure.travis-ci.org/librato/metrics-rails.png?branch=master)](http://travis-ci.org/librato/metrics-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 'metrics-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 `metrics-rails`. You can provide these one of two ways:
21
+
22
+ Create a `config/metrics.yml` like the following:
23
+
24
+ production:
25
+ email: <your-email>
26
+ api_key: <your-api-key>
27
+
28
+ OR provide `METRICS_EMAIL` and `METRICS_API_KEY` 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/metrics-rails/wiki/Configuration).
33
+
34
+ ## Automatic Measurements
35
+
36
+ After installing `metrics-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/metrics-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
+ Metrics.increment 'sales_completed'
50
+
51
+ # increment by five
52
+ Metrics.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
+ Metrics.measure 'user.social_graph.nodes', 212
61
+
62
+ Metrics.measure 'jobs.queued', 3
63
+
64
+
65
+ #### timing
66
+
67
+ Like `Metrics.measure` this is per-request, but specialized for timing information:
68
+
69
+ Metrics.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
+ Metrics.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
+ Metrics.measure 'memcached.gets', 20
82
+ Metrics.measure 'memcached.sets', 2
83
+ Metrics.measure 'memcached.hits', 18
84
+
85
+ Can also be written as:
86
+
87
+ Metrics.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.
@@ -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 = 'MetricsRails'
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/metrics-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 'metrics/rails'
@@ -0,0 +1,226 @@
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 'metrics/rails/aggregator'
9
+ require 'metrics/rails/counter_cache'
10
+ require 'metrics/rails/group'
11
+ require 'metrics/rails/helpers'
12
+ require 'metrics/rails/worker'
13
+ require 'metrics/rails/version'
14
+
15
+ module Metrics
16
+ extend SingleForwardable
17
+ def_delegators Metrics::Rails, :increment, :measure, :timing, :group
18
+
19
+ module Rails
20
+ extend SingleForwardable
21
+ CONFIG_SETTABLE = %w{api_key email flush_interval prefix source}
22
+
23
+ mattr_accessor :config_file
24
+
25
+ # config options
26
+ mattr_accessor :api_key
27
+ mattr_accessor :email
28
+ mattr_accessor :flush_interval
29
+ mattr_accessor :prefix
30
+
31
+ # config defaults
32
+ self.flush_interval = 60 # seconds
33
+ self.prefix = 'rails'
34
+
35
+ def_delegators :counters, :increment
36
+ def_delegators :aggregate, :measure, :timing
37
+
38
+ class << self
39
+
40
+ # access to internal aggregator object
41
+ def aggregate
42
+ @aggregator_cache ||= Aggregator.new
43
+ end
44
+
45
+ # set custom api endpoint
46
+ def api_endpoint=(endpoint)
47
+ @api_endpoint = endpoint
48
+ end
49
+
50
+ # access to client instance
51
+ def client
52
+ @client ||= prepare_client
53
+ end
54
+
55
+ # detect / update configuration
56
+ def check_config
57
+ if self.config_file && File.exists?(self.config_file)
58
+ configs = YAML.load_file(config_file)
59
+ if env_specific = configs[::Rails.env]
60
+ settable = CONFIG_SETTABLE & env_specific.keys
61
+ settable.each { |key| self.send("#{key}=", env_specific[key]) }
62
+ end
63
+ end
64
+ self.api_key = ENV['METRICS_API_KEY'] if ENV['METRICS_API_KEY']
65
+ self.email = ENV['METRICS_EMAIL'] if ENV['METRICS_EMAIL']
66
+ end
67
+
68
+ # check to see if we've forked into a process where a worker
69
+ # isn't running yet, if so start it up!
70
+ def check_worker
71
+ if @pid != $$
72
+ start_worker
73
+ #aggregate.clear
74
+ #counters.clear
75
+ end
76
+ end
77
+
78
+ # access to internal counters object
79
+ def counters
80
+ @counter_cache ||= CounterCache.new
81
+ end
82
+
83
+ # remove any accumulated but unsent metrics
84
+ def delete_all
85
+ aggregate.delete_all
86
+ counters.delete_all
87
+ end
88
+
89
+ # catches request-environment specific things we can't
90
+ # find through another means.
91
+ def evaluate_request(request)
92
+ if request.env.keys.include?('HTTP_X_HEROKU_QUEUE_DEPTH')
93
+ # heroku
94
+ group "#{prefix}.heroku" do |h|
95
+ h.measure 'queue.depth', request.env['HTTP_X_HEROKU_QUEUE_DEPTH'].to_f
96
+ h.timing 'queue.wait_time', request.env['HTTP_X_HEROKU_QUEUE_WAIT_TIME'].to_f
97
+ h.measure 'queue.dynos', request.env['HTTP_X_HEROKU_DYNOS_IN_USE'].to_f
98
+ end
99
+ end
100
+ end
101
+
102
+ # send all current data to Metrics
103
+ def flush
104
+ logger.debug "[metrics-rails] flushing #{Process.pid} (#{Time.now}):"
105
+ queue = client.new_queue(:source => qualified_source)
106
+ # thread safety is handled internally for both stores
107
+ counters.flush_to(queue)
108
+ aggregate.flush_to(queue)
109
+ logger.debug queue.queued
110
+ queue.submit unless queue.empty?
111
+ rescue Exception => error
112
+ logger.error "[metrics-rails] submission failed permanently, worker exiting: #{error}"
113
+ end
114
+
115
+ def group(prefix)
116
+ group = Group.new(prefix)
117
+ yield group
118
+ end
119
+
120
+ def logger
121
+ @logger ||= ::Rails.logger
122
+ end
123
+
124
+ # source including process pid
125
+ def qualified_source
126
+ "#{source}.#{$$}"
127
+ end
128
+
129
+ # run once during Rails startup sequence
130
+ def setup
131
+ check_config
132
+ #return unless self.email && self.api_key
133
+ logger.info "[metrics-rails] starting up with #{app_server}..."
134
+ @pid = $$
135
+ install_request_evaluator
136
+ if forking_server?
137
+ install_worker_check
138
+ else
139
+ start_worker # start immediately
140
+ end
141
+ end
142
+
143
+ def source
144
+ @source ||= Socket.gethostname
145
+ end
146
+
147
+ # set a custom source
148
+ def source=(src)
149
+ @source = src
150
+ end
151
+
152
+ # start the worker thread, one is needed per process.
153
+ # if this process has been forked from an one with an active
154
+ # worker thread we don't need to worry about cleanup as only
155
+ # the forking thread is copied.
156
+ def start_worker
157
+ return if @worker # already running
158
+ @pid = $$
159
+ logger.debug "[metrics-rails] >> starting up worker for pid #{@pid}..."
160
+ @worker = Thread.new do
161
+ worker = Worker.new
162
+ worker.run_periodically(self.flush_interval) do
163
+ flush
164
+ end
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def app_server
171
+ if defined?(::Unicorn) && defined?(::Unicorn::HttpServer) && !::Unicorn.listener_names.empty?
172
+ :unicorn
173
+ elsif defined?(::IN_PHUSION_PASSENGER) || (defined?(::Passenger) && defined?(::Passenger::AbstractServer))
174
+ :passenger
175
+ elsif defined?(::Thin) && defined?(::Thin::Server)
176
+ :thin
177
+ else
178
+ :other
179
+ end
180
+ end
181
+
182
+ def forking_server?
183
+ %w{unicorn passenger}.include?(app_server.to_s)
184
+ end
185
+
186
+ def install_worker_check
187
+ ::ApplicationController.prepend_before_filter do |c|
188
+ Metrics::Rails.check_worker
189
+ end
190
+ end
191
+
192
+ def install_request_evaluator
193
+ ::ApplicationController.prepend_before_filter do |c|
194
+ Metrics::Rails.evaluate_request(request)
195
+ end
196
+ end
197
+
198
+ def prepare_client
199
+ check_config
200
+ client = Librato::Metrics::Client.new
201
+ client.authenticate email, api_key
202
+ client.api_endpoint = @api_endpoint if @api_endpoint
203
+ client.custom_user_agent = user_agent
204
+ client
205
+ end
206
+
207
+ def ruby_engine
208
+ return RUBY_ENGINE if Object.constants.include?(:RUBY_ENGINE)
209
+ RUBY_DESCRIPTION.split[0]
210
+ end
211
+
212
+ def user_agent
213
+ ua_chunks = []
214
+ ua_chunks << "metrics-rails/#{Metrics::Rails::VERSION}"
215
+ ua_chunks << "(#{ruby_engine}; #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}; #{RUBY_PLATFORM}; #{app_server})"
216
+ ua_chunks.join(' ')
217
+ end
218
+
219
+ end # end class << self
220
+
221
+ end
222
+ end
223
+
224
+ # must load after all module setup
225
+ require 'metrics/rails/railtie'
226
+ require 'metrics/rails/subscribers'
@@ -0,0 +1,48 @@
1
+ module Metrics
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