pulse-meter 0.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.
Files changed (96) hide show
  1. data/.gitignore +19 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +4 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +3 -0
  9. data/README.md +440 -0
  10. data/Rakefile +53 -0
  11. data/bin/pulse +6 -0
  12. data/examples/basic.ru +109 -0
  13. data/examples/basic_sensor_data.rb +38 -0
  14. data/examples/full/Procfile +2 -0
  15. data/examples/full/client.rb +82 -0
  16. data/examples/full/server.ru +114 -0
  17. data/examples/minimal/Procfile +2 -0
  18. data/examples/minimal/client.rb +16 -0
  19. data/examples/minimal/server.ru +20 -0
  20. data/examples/readme_client_example.rb +52 -0
  21. data/lib/cmd.rb +150 -0
  22. data/lib/pulse-meter.rb +17 -0
  23. data/lib/pulse-meter/mixins/dumper.rb +72 -0
  24. data/lib/pulse-meter/mixins/utils.rb +91 -0
  25. data/lib/pulse-meter/sensor.rb +44 -0
  26. data/lib/pulse-meter/sensor/base.rb +75 -0
  27. data/lib/pulse-meter/sensor/counter.rb +36 -0
  28. data/lib/pulse-meter/sensor/hashed_counter.rb +31 -0
  29. data/lib/pulse-meter/sensor/indicator.rb +33 -0
  30. data/lib/pulse-meter/sensor/timeline.rb +180 -0
  31. data/lib/pulse-meter/sensor/timelined/average.rb +26 -0
  32. data/lib/pulse-meter/sensor/timelined/counter.rb +16 -0
  33. data/lib/pulse-meter/sensor/timelined/hashed_counter.rb +22 -0
  34. data/lib/pulse-meter/sensor/timelined/max.rb +25 -0
  35. data/lib/pulse-meter/sensor/timelined/median.rb +14 -0
  36. data/lib/pulse-meter/sensor/timelined/min.rb +25 -0
  37. data/lib/pulse-meter/sensor/timelined/percentile.rb +31 -0
  38. data/lib/pulse-meter/version.rb +3 -0
  39. data/lib/pulse-meter/visualize/app.rb +43 -0
  40. data/lib/pulse-meter/visualize/dsl.rb +0 -0
  41. data/lib/pulse-meter/visualize/dsl/errors.rb +46 -0
  42. data/lib/pulse-meter/visualize/dsl/layout.rb +55 -0
  43. data/lib/pulse-meter/visualize/dsl/page.rb +50 -0
  44. data/lib/pulse-meter/visualize/dsl/sensor.rb +21 -0
  45. data/lib/pulse-meter/visualize/dsl/widget.rb +84 -0
  46. data/lib/pulse-meter/visualize/layout.rb +54 -0
  47. data/lib/pulse-meter/visualize/page.rb +30 -0
  48. data/lib/pulse-meter/visualize/public/css/application.css +19 -0
  49. data/lib/pulse-meter/visualize/public/css/bootstrap.css +4883 -0
  50. data/lib/pulse-meter/visualize/public/css/bootstrap.min.css +729 -0
  51. data/lib/pulse-meter/visualize/public/favicon.ico +0 -0
  52. data/lib/pulse-meter/visualize/public/img/glyphicons-halflings-white.png +0 -0
  53. data/lib/pulse-meter/visualize/public/img/glyphicons-halflings.png +0 -0
  54. data/lib/pulse-meter/visualize/public/js/application.coffee +262 -0
  55. data/lib/pulse-meter/visualize/public/js/application.js +279 -0
  56. data/lib/pulse-meter/visualize/public/js/backbone-min.js +38 -0
  57. data/lib/pulse-meter/visualize/public/js/bootstrap.js +1835 -0
  58. data/lib/pulse-meter/visualize/public/js/highcharts.js +203 -0
  59. data/lib/pulse-meter/visualize/public/js/jquery-1.7.2.min.js +4 -0
  60. data/lib/pulse-meter/visualize/public/js/json2.js +487 -0
  61. data/lib/pulse-meter/visualize/public/js/underscore-min.js +32 -0
  62. data/lib/pulse-meter/visualize/sensor.rb +60 -0
  63. data/lib/pulse-meter/visualize/views/main.haml +40 -0
  64. data/lib/pulse-meter/visualize/widget.rb +68 -0
  65. data/lib/pulse-meter/visualizer.rb +30 -0
  66. data/lib/test_helpers/matchers.rb +36 -0
  67. data/pulse-meter.gemspec +39 -0
  68. data/spec/pulse_meter/mixins/dumper_spec.rb +158 -0
  69. data/spec/pulse_meter/mixins/utils_spec.rb +134 -0
  70. data/spec/pulse_meter/sensor/base_spec.rb +97 -0
  71. data/spec/pulse_meter/sensor/counter_spec.rb +54 -0
  72. data/spec/pulse_meter/sensor/hashed_counter_spec.rb +39 -0
  73. data/spec/pulse_meter/sensor/indicator_spec.rb +43 -0
  74. data/spec/pulse_meter/sensor/timeline_spec.rb +58 -0
  75. data/spec/pulse_meter/sensor/timelined/average_spec.rb +6 -0
  76. data/spec/pulse_meter/sensor/timelined/counter_spec.rb +6 -0
  77. data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +8 -0
  78. data/spec/pulse_meter/sensor/timelined/max_spec.rb +7 -0
  79. data/spec/pulse_meter/sensor/timelined/median_spec.rb +7 -0
  80. data/spec/pulse_meter/sensor/timelined/min_spec.rb +7 -0
  81. data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +17 -0
  82. data/spec/pulse_meter/visualize/app_spec.rb +27 -0
  83. data/spec/pulse_meter/visualize/dsl/layout_spec.rb +64 -0
  84. data/spec/pulse_meter/visualize/dsl/page_spec.rb +75 -0
  85. data/spec/pulse_meter/visualize/dsl/sensor_spec.rb +30 -0
  86. data/spec/pulse_meter/visualize/dsl/widget_spec.rb +127 -0
  87. data/spec/pulse_meter/visualize/layout_spec.rb +55 -0
  88. data/spec/pulse_meter/visualize/page_spec.rb +150 -0
  89. data/spec/pulse_meter/visualize/sensor_spec.rb +120 -0
  90. data/spec/pulse_meter/visualize/widget_spec.rb +113 -0
  91. data/spec/pulse_meter/visualizer_spec.rb +42 -0
  92. data/spec/pulse_meter_spec.rb +16 -0
  93. data/spec/shared_examples/timeline_sensor.rb +279 -0
  94. data/spec/shared_examples/timelined_subclass.rb +23 -0
  95. data/spec/spec_helper.rb +29 -0
  96. metadata +435 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sw?
19
+ /.idea
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.2-p290
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color -fd
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pulse-meter.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Ilya Averyanov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Procfile ADDED
@@ -0,0 +1,3 @@
1
+ watch_coffee: bundle exec rake coffee:watch
2
+ web_sample: bundle exec rackup examples/basic.ru
3
+ sensor_sample: bundle exec ruby examples/basic_sensor_data.rb
data/README.md ADDED
@@ -0,0 +1,440 @@
1
+ [![Build Status](https://secure.travis-ci.org/savonarola/pulse-meter.png)](http://travis-ci.org/savonarola/pulse-meter)
2
+
3
+ # PulseMeter
4
+
5
+ PulseMeter is a gem for fast and convenient realtime aggregating of software internal stats through Redis.
6
+
7
+ ## Features
8
+
9
+ PulseMeter is designed to provide the following features:
10
+
11
+ * Simple deployment. The only infrastructure resource you are required to have is Redis.
12
+
13
+ * Low resource consumption. Since different kinds of events are aggregated in Redis,
14
+ you are as light and fast as Redis is.
15
+ Event data is stored in constant space and expires over time.
16
+
17
+ * Focus on the client. To start gathering some metrics, you should only modify your client: create a sensor object
18
+ and send events to it. All aggregated data can be accessed immediately without any
19
+ sort of "server reconfiguration"
20
+
21
+ ## Concept
22
+
23
+ The fundamental concept of PulseMeter is *sensor*. Sensor is some named piece of data in Redis which
24
+ can be updated through client side objects associated with this data. The semantics of the data can be
25
+ different: some counter, value, series of values, etc. There is no need to care about explicit creation this data:
26
+ one just creates a client object and writes data to it, e.g.
27
+
28
+ PulseMeter.redis = Redis.new
29
+ sensor = PulseMeter::Sensor::Counter.new :my_counter
30
+ sensor.event(5)
31
+ ...
32
+ sensor.event(3)
33
+
34
+ After that the value associated with the counter is immediately available (through CLI, for example). Any other
35
+ client can access the associated counter by creating object with the same redis db and sensor name.
36
+
37
+ Sensors can be divided into two large groups.
38
+
39
+ ### Static sensors
40
+
41
+ These are just single values which can be read by CLI, e.g. some counter or some value
42
+ representing current state of a resource (current free memory amount, current la etc.). Currently, the
43
+ following static sensors are available:
44
+
45
+ * Counter
46
+ * Hashed Counter
47
+ * Indicator
48
+
49
+ They have no web visualisation interface and they are assumed to be used by external visualisation tools.
50
+
51
+
52
+ ### Timeline sensors
53
+
54
+ These sensors are series of values, one value for each consequent time interval. They
55
+ are available by CLI and have web visualisation interface. Examples of such sensors include: count of
56
+ requests to some resource per hour, the longest request to a database per minute, etc.
57
+
58
+ The following timeline sensors are available:
59
+
60
+ * Average value
61
+ * Counter
62
+ * Hashed counter
63
+ * Max value
64
+ * Min value
65
+ * Median value
66
+ * Percentile
67
+
68
+ There are several caveats with timeline sensors:
69
+
70
+ * The value of a sensor for the last interval (which is not finished yet) is often not very useful.
71
+ When building a visualisation you may choose to display the last value or not.
72
+ * For some sensors (currently Median and Percentile) considerable amount of data should be stored for a
73
+ particular interval to obtain value for this interval. So it is a good idea to schedule
74
+ <tt>pulse reduce</tt>
75
+ command on a regular basis. This command reduces the stored data for passed intervals to single values,
76
+ so that they do not consume storage space.
77
+
78
+ ## Client usage
79
+
80
+ Just create sensor objects and write data. Some examples below.
81
+
82
+ require 'pulse-meter'
83
+ PulseMeter.redis = Redis.new
84
+
85
+ # static sensor examples
86
+
87
+ counter = PulseMeter::Sensor::Counter.new :my_counter
88
+ counter.event(1)
89
+ counter.event(2)
90
+ puts counter.value
91
+ # prints
92
+ # 3
93
+
94
+ indicator = PulseMeter::Sensor::Indicator.new :my_value
95
+ indicator.event(3.14)
96
+ indicator.event(2.71)
97
+ puts indicator.value
98
+ # prints
99
+ # 2.71
100
+
101
+ hashed_counter = PulseMeter::Sensor::HashedCounter.new :my_h_counter
102
+ hashed_counter.event(:x => 1)
103
+ hashed_counter.event(:y => 5)
104
+ hashed_counter.event(:y => 1)
105
+ p hashed_counter.value
106
+ # prints
107
+ # {"x"=>1, "y"=>6}
108
+
109
+ # timeline sensor examples
110
+
111
+ requests_per_minute = PulseMeter::Sensor::Timelined::Counter.new(:my_t_counter,
112
+ :interval => 60, # count for each minute
113
+ :ttl => 24 * 60 * 60 # keep data one day
114
+ )
115
+ requests_per_minute.event(1)
116
+ requests_per_minute.event(1)
117
+ sleep(60)
118
+ requests_per_minute.event(1)
119
+ requests_per_minute.timeline(2 * 60).each do |v|
120
+ puts "#{v.start_time}: #{v.value}"
121
+ end
122
+ # prints somewhat like
123
+ # 2012-05-24 11:06:00 +0400: 2
124
+ # 2012-05-24 11:07:00 +0400: 1
125
+
126
+ max_per_minute = PulseMeter::Sensor::Timelined::Max.new(:my_t_max,
127
+ :interval => 60, # max for each minute
128
+ :ttl => 24 * 60 * 60 # keep data one day
129
+ )
130
+ max_per_minute.event(3)
131
+ max_per_minute.event(1)
132
+ max_per_minute.event(2)
133
+ sleep(60)
134
+ max_per_minute.event(5)
135
+ max_per_minute.event(7)
136
+ max_per_minute.event(6)
137
+ max_per_minute.timeline(2 * 60).each do |v|
138
+ puts "#{v.start_time}: #{v.value}"
139
+ end
140
+ # prints somewhat like
141
+ # 2012-05-24 11:07:00 +0400: 3.0
142
+ # 2012-05-24 11:08:00 +0400: 7.0
143
+
144
+ ## Command line interface
145
+
146
+ Gem includes a tool <tt>pulse</tt>, which allows to send events to sensors, list them, etc.
147
+ You should pay attention to the command <tt>pulse reduce</tt>, which is generally should be
148
+ scheduled on a regular basis to keep data in Redis small.
149
+
150
+ To see available commands of this tool one can run the example above(see <tt>examples/readme\_client\_example.rb</tt>)
151
+ and run <tt>pulse help</tt>.
152
+
153
+ ## Visualisation
154
+
155
+ PulseMeter comes with a simple DSL which allows to build a self-contained Rack application for
156
+ visualizing timeline sensor data.
157
+
158
+ The application is described by *Layout* which contains some general application options and a list of *Pages*.
159
+ Each page contain a list of *Widgets* (charts), and each widget is associated with several sensors, which produce
160
+ data series for the chart.
161
+
162
+ There is a minimal and a full example below.
163
+
164
+ ### Minimal example
165
+
166
+ It can be found in <tt>examples/minimal</tt> folder. To run it, execute
167
+ <tt>bundle && cd examples/minimal && bundle exec foreman start</tt> (or just <tt>rake example:minimal</tt>)
168
+ at project root and visit
169
+ <tt>http://localhost:9292</tt> at your browser.
170
+
171
+ <tt>client.rb</tt> just creates a timelined counter an sends data to it in an infinite loop.
172
+
173
+ require "pulse-meter"
174
+
175
+ PulseMeter.redis = Redis.new
176
+
177
+ sensor = PulseMeter::Sensor::Timelined::Counter.new(:simple_sample_counter,
178
+ :interval => 5,
179
+ :ttl => 60 * 60
180
+ )
181
+
182
+ while true
183
+ STDERR.puts "tick"
184
+ sensor.event(1)
185
+ sleep(Random.rand)
186
+ end
187
+
188
+ <tt>server.ru</tt> is a Rackup file creating a simple layout with one page and one widget on it, which displays
189
+ the sensor's data. The layout is converted to a rack application and launched.
190
+
191
+ require "pulse-meter/visualizer"
192
+
193
+ PulseMeter.redis = Redis.new
194
+
195
+ layout = PulseMeter::Visualizer.draw do |l|
196
+
197
+ l.title "Minimal App"
198
+
199
+ l.page "Main Page" do |p|
200
+ p.area "Live Counter",
201
+ sensor: :simple_sample_counter,
202
+ timespan: 5 * 60,
203
+ redraw_interval: 1
204
+ end
205
+
206
+ end
207
+
208
+ run layout.to_app
209
+
210
+ <tt>Procfile</tt> allows to launch both "client" script and the web server with <tt>foreman</tt>.
211
+
212
+ web: bundle exec rackup server.ru
213
+ sensor_data_generator: bundle exec ruby client.rb
214
+
215
+ ### Full example with DSL explanation
216
+
217
+ It can be found in <tt>examples/full</tt> folder. To run it, execute
218
+ <tt>bundle && cd examples/full && bundle exec foreman start</tt> (or just <tt>rake example:full</tt>)
219
+ at project root and visit
220
+ <tt>http://localhost:9292</tt> at your browser.
221
+
222
+ <tt>client.rb</tt> imitating users visiting some imaginary site
223
+
224
+ require "pulse-meter"
225
+
226
+ PulseMeter.redis = Redis.new
227
+
228
+ requests_per_minute = PulseMeter::Sensor::Timelined::Counter.new(:requests_per_minute,
229
+ :annotation => 'Requests per minute',
230
+ :interval => 60,
231
+ :ttl => 60 * 60 * 24 # keep data one day
232
+ )
233
+
234
+ requests_per_hour = PulseMeter::Sensor::Timelined::Counter.new(:requests_per_hour,
235
+ :annotation => 'Requests per hour',
236
+ :interval => 60 * 60,
237
+ :ttl => 60 * 60 * 24 * 30 # keep data 30 days
238
+ # when ActiveSupport extentions are loaded, a better way is to write just
239
+ # :interval => 1.hour,
240
+ # :ttl => 30.days
241
+ )
242
+
243
+ errors_per_minute = PulseMeter::Sensor::Timelined::Counter.new(:errors_per_minute,
244
+ :annotation => 'Errors per minute',
245
+ :interval => 60,
246
+ :ttl => 60 * 60 * 24
247
+ )
248
+
249
+ errors_per_hour = PulseMeter::Sensor::Timelined::Counter.new(:errors_per_hour,
250
+ :annotation => 'Errors per hour',
251
+ :interval => 60 * 60,
252
+ :ttl => 60 * 60 * 24 * 30
253
+ )
254
+
255
+ longest_minute_request = PulseMeter::Sensor::Timelined::Max.new(:longest_minute_request,
256
+ :annotation => 'Longest minute requests',
257
+ :interval => 60,
258
+ :ttl => 60 * 60 * 24
259
+ )
260
+
261
+ shortest_minute_request = PulseMeter::Sensor::Timelined::Min.new(:shortest_minute_request,
262
+ :annotation => 'Shortest minute requests',
263
+ :interval => 60,
264
+ :ttl => 60 * 60 * 24
265
+ )
266
+
267
+ perc90_minute_request = PulseMeter::Sensor::Timelined::Percentile.new(:perc90_minute_request,
268
+ :annotation => 'Minute request 90-percent percentile',
269
+ :interval => 60,
270
+ :ttl => 60 * 60 * 24,
271
+ :p => 0.9
272
+ )
273
+
274
+ agent_names = [:ie, :firefox, :chrome, :other]
275
+ hour_agents = agent_names.each_with_object({}) do |agent, h|
276
+ h[agent] = PulseMeter::Sensor::Timelined::Counter.new(agent,
277
+ :annotation => "Requests from #{agent} browser",
278
+ :interval => 60 * 60,
279
+ :ttl => 60 * 60 * 24 * 30
280
+ )
281
+ end
282
+
283
+
284
+ while true
285
+ requests_per_minute.event(1)
286
+ requests_per_hour.event(1)
287
+
288
+ if Random.rand(10) < 1 # let "errors" sometimes occur
289
+ errors_per_minute.event(1)
290
+ errors_per_hour.event(1)
291
+ end
292
+
293
+ request_time = 0.1 + Random.rand
294
+
295
+ longest_minute_request.event(request_time)
296
+ shortest_minute_request.event(request_time)
297
+ perc90_minute_request.event(request_time)
298
+
299
+ agent_counter = hour_agents[agent_names.shuffle.first]
300
+ agent_counter.event(1)
301
+
302
+ sleep(Random.rand / 10)
303
+ end
304
+
305
+ A more complicated visualization
306
+
307
+ require "pulse-meter/visualizer"
308
+
309
+ PulseMeter.redis = Redis.new
310
+
311
+ layout = PulseMeter::Visualizer.draw do |l|
312
+
313
+ # Application title
314
+ l.title "Full Example"
315
+
316
+ # Use local time for x-axis of charts
317
+ l.use_utc false
318
+
319
+ # Color for values cut off
320
+ l.outlier_color '#FF0000'
321
+
322
+ # Transfer some global parameters to Highcharts
323
+ l.highchart_options({
324
+ tooltip: {
325
+ value_decimals: 2
326
+ }
327
+ })
328
+
329
+ # Add some pages
330
+ l.page "Request count" do |p|
331
+
332
+ # Add chart (of Highcharts `area' style, `spline', `pie' and `line' are also available)
333
+ p.area "Requests per minute" do |w|
334
+
335
+ # Plot :requests_per_minute values on this chart with black color
336
+ w.sensor :requests_per_minute, color: '#000000'
337
+
338
+ # Plot :errors_per_minute values on this chart with red color
339
+ w.sensor :errors_per_minute, color: '#FF0000'
340
+
341
+ # Plot values for the last hour
342
+ w.timespan 60 * 60
343
+
344
+ # Redraw chart every 10 seconds
345
+ w.redraw_interval 10
346
+
347
+ # Plot incomplete data
348
+ w.show_last_point true
349
+
350
+ # Meaning of the y-axis
351
+ w.values_label "Request count"
352
+
353
+ # Occupy half (5/10) of the page (horizontally)
354
+ w.width 5
355
+
356
+ # Transfer page-wide (and page-specific) options to Highcharts
357
+ p.highchart_options({
358
+ chart: {
359
+ height: 300
360
+ }
361
+ })
362
+ end
363
+
364
+ p.area "Requests per hour" do |w|
365
+
366
+ w.sensor :requests_per_hour, color: '#555555'
367
+ w.sensor :errors_per_hour, color: '#FF0000'
368
+
369
+ w.timespan 24 * 60 * 60
370
+ w.redraw_interval 10
371
+ w.show_last_point true
372
+ w.values_label "Request count"
373
+ w.width 5
374
+
375
+ end
376
+ end
377
+
378
+ l.page "Request times" do |p|
379
+ p.area "Requests time" do |w|
380
+
381
+ w.sensor :longest_minute_request
382
+ w.sensor :shortest_minute_request
383
+ w.sensor :perc90_minute_request
384
+
385
+ w.timespan 60 * 60
386
+ w.redraw_interval 10
387
+ w.show_last_point true
388
+ w.values_label "Time in seconds"
389
+ w.width 10
390
+
391
+ end
392
+ end
393
+
394
+ l.page "Browsers" do |p|
395
+ p.pie "Requests from browser" do |w|
396
+
397
+ [:ie, :firefox, :chrome, :other].each do |sensor|
398
+ w.sensor sensor
399
+ end
400
+
401
+ w.timespan 24 * 60 * 60
402
+ w.redraw_interval 10
403
+ w.show_last_point true
404
+ w.values_label "Request count"
405
+ w.width 10
406
+
407
+ end
408
+
409
+ p.highchart_options({
410
+ chart: {
411
+ height: 500
412
+ }
413
+ })
414
+ end
415
+
416
+ end
417
+
418
+ run layout.to_app
419
+
420
+ ## Installation
421
+
422
+ Add this line to your application's Gemfile:
423
+
424
+ gem 'pulse-meter'
425
+
426
+ And then execute:
427
+
428
+ $ bundle
429
+
430
+ Or install it yourself as:
431
+
432
+ $ gem install pulse-meter
433
+
434
+ ## Contributing
435
+
436
+ 1. Fork it
437
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
438
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
439
+ 4. Push to the branch (`git push origin my-new-feature`)
440
+ 5. Create new Pull Request