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/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+ require "yard"
5
+ require 'yard/rake/yardoc_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ YARD::Rake::YardocTask.new(:yard)
10
+
11
+ ROOT = File.dirname(__FILE__)
12
+
13
+ task :default => :spec
14
+
15
+ namespace :coffee do
16
+ desc "Complile coffee to js"
17
+ task :compile do
18
+ system 'coffee', '-c', "#{ROOT}/lib/pulse-meter/visualize/public/"
19
+ puts "Done"
20
+ end
21
+
22
+ desc "Watch coffee files and recomplile them immediately"
23
+ task :watch do
24
+ system 'coffee', '--watch', '-c', "#{ROOT}/lib/pulse-meter/visualize/public/"
25
+ end
26
+
27
+ end
28
+
29
+ namespace :yard do
30
+ desc "Open doc index in a browser"
31
+ task :open do
32
+ system 'open', "#{ROOT}/doc/index.html"
33
+ end
34
+ end
35
+
36
+ namespace :example do
37
+ desc "Run minimal example"
38
+ task :minimal do
39
+ chdir(ROOT) do
40
+ system "bundle"
41
+ system "cd examples/minimal && bundle exec foreman start"
42
+ end
43
+ end
44
+
45
+ desc "Run full example"
46
+ task :full do
47
+ chdir(ROOT) do
48
+ system "bundle"
49
+ system "cd examples/full && bundle exec foreman start"
50
+ end
51
+ end
52
+
53
+ end
data/bin/pulse ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../lib/'))
3
+ require 'pulse-meter'
4
+ require 'cmd'
5
+
6
+ Cmd::All.start
data/examples/basic.ru ADDED
@@ -0,0 +1,109 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', 'lib')
2
+
3
+ require 'pulse-meter/visualizer'
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ layout = PulseMeter::Visualizer.draw do |l|
8
+
9
+ l.title "WunderZoo Stats"
10
+
11
+ l.page "Counts" do |p|
12
+
13
+ p.spline "Lama count", sensor: :lama_count do |c|
14
+ c.redraw_interval 5
15
+ c.values_label 'Count'
16
+ c.width 5
17
+ c.show_last_point true
18
+ c.timespan 1200
19
+ end
20
+
21
+ p.area "Rhino count", sensor: :rhino_count do |c|
22
+ c.redraw_interval 5
23
+ c.values_label 'Count'
24
+ c.width 5
25
+ c.show_last_point true
26
+ c.timespan 1200
27
+ end
28
+
29
+ p.spline "Rhino & Lama count comparison" do |c|
30
+ c.redraw_interval 5
31
+ c.values_label 'Count'
32
+ c.width 5
33
+ c.show_last_point true
34
+ c.timespan 1200
35
+
36
+ c.sensor :rhino_count, color: '#AAAAAA'
37
+ c.sensor :lama_count, color: '#CC1155'
38
+ end
39
+
40
+ p.pie "Rhino & Lama count comparison" do |c|
41
+ c.redraw_interval 5
42
+ c.values_label 'Count'
43
+ c.width 5
44
+ c.show_last_point false
45
+ c.timespan 1200
46
+
47
+ c.sensor :rhino_count, color: '#AAAAAA'
48
+ c.sensor :lama_count, color: '#CC1155'
49
+ end
50
+
51
+ end
52
+
53
+ l.page "Ages" do |p|
54
+
55
+ p.line "Lama average age", sensor: :lama_average_age do |c|
56
+ c.redraw_interval 5
57
+ c.values_label 'Age'
58
+ c.width 5
59
+ c.show_last_point true
60
+ c.timespan 1200
61
+ end
62
+
63
+ p.line "Rhino average age", sensor: :rhino_average_age do |c|
64
+ c.redraw_interval 5
65
+ c.values_label 'Age'
66
+ c.width 5
67
+ c.show_last_point true
68
+ c.timespan 1200
69
+ end
70
+
71
+ p.area "Rhino & Lama average age comparison" do |c|
72
+ c.redraw_interval 5
73
+ c.values_label 'Age'
74
+ c.width 5
75
+ c.show_last_point true
76
+ c.timespan 1200
77
+
78
+ c.sensor :lama_average_age
79
+ c.sensor :rhino_average_age
80
+ end
81
+
82
+ p.pie "Rhino & Lama average age comparison" do |c|
83
+ c.redraw_interval 5
84
+ c.values_label 'Age'
85
+ c.width 5
86
+ c.show_last_point false
87
+ c.timespan 1200
88
+
89
+ c.sensor :lama_average_age
90
+ c.sensor :rhino_average_age
91
+ end
92
+
93
+ p.highchart_options({
94
+ chart: {
95
+ height: 300
96
+ }
97
+ })
98
+ end
99
+
100
+ l.use_utc false
101
+ l.outlier_color '#FF0000'
102
+ l.highchart_options({
103
+ tooltip: {
104
+ value_decimals: 2
105
+ }
106
+ })
107
+ end
108
+
109
+ run layout.to_app
@@ -0,0 +1,38 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', 'lib')
2
+
3
+ require "pulse-meter"
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ lama_counter = PulseMeter::Sensor::Timelined::Counter.new(:lama_count,
8
+ annotation: 'Lama Count',
9
+ interval: 10,
10
+ ttl: 3600
11
+ )
12
+
13
+ lama_average_age = PulseMeter::Sensor::Timelined::Average.new(:lama_average_age,
14
+ annotation: 'Lama Average Age',
15
+ interval: 20,
16
+ ttl: 3600
17
+ )
18
+
19
+ rhino_counter = PulseMeter::Sensor::Timelined::Counter.new(:rhino_count,
20
+ annotation: 'Rhino Count',
21
+ interval: 10,
22
+ ttl: 3600
23
+ )
24
+
25
+ rhino_average_age = PulseMeter::Sensor::Timelined::Average.new(:rhino_average_age,
26
+ annotation: 'Rhino average age',
27
+ interval: 20,
28
+ ttl: 3600
29
+ )
30
+
31
+ while true
32
+ sleep(Random.rand)
33
+ STDERR.puts "tick"
34
+ lama_counter.event(1)
35
+ rhino_counter.event(2)
36
+ lama_average_age.event(Random.rand(50))
37
+ rhino_average_age.event(Random.rand(100))
38
+ end
@@ -0,0 +1,2 @@
1
+ web: bundle exec rackup server.ru
2
+ sensor_data_generator: bundle exec ruby client.rb
@@ -0,0 +1,82 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', '..', 'lib')
2
+
3
+ require "pulse-meter"
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ requests_per_minute = PulseMeter::Sensor::Timelined::Counter.new(:requests_per_minute,
8
+ :annotation => 'Requests per minute',
9
+ :interval => 60,
10
+ :ttl => 60 * 60 * 24 # keep data one day
11
+ )
12
+
13
+ requests_per_hour = PulseMeter::Sensor::Timelined::Counter.new(:requests_per_hour,
14
+ :annotation => 'Requests per hour',
15
+ :interval => 60 * 60,
16
+ :ttl => 60 * 60 * 24 * 30 # keep data 30 days
17
+ # when ActiveSupport extentions are loaded, a better way is to write just
18
+ # :interval => 1.hour,
19
+ # :ttl => 30.days
20
+ )
21
+
22
+ errors_per_minute = PulseMeter::Sensor::Timelined::Counter.new(:errors_per_minute,
23
+ :annotation => 'Errors per minute',
24
+ :interval => 60,
25
+ :ttl => 60 * 60 * 24
26
+ )
27
+
28
+ errors_per_hour = PulseMeter::Sensor::Timelined::Counter.new(:errors_per_hour,
29
+ :annotation => 'Errors per hour',
30
+ :interval => 60 * 60,
31
+ :ttl => 60 * 60 * 24 * 30
32
+ )
33
+
34
+ longest_minute_request = PulseMeter::Sensor::Timelined::Max.new(:longest_minute_request,
35
+ :annotation => 'Longest minute requests',
36
+ :interval => 60,
37
+ :ttl => 60 * 60 * 24
38
+ )
39
+
40
+ shortest_minute_request = PulseMeter::Sensor::Timelined::Min.new(:shortest_minute_request,
41
+ :annotation => 'Shortest minute requests',
42
+ :interval => 60,
43
+ :ttl => 60 * 60 * 24
44
+ )
45
+
46
+ perc90_minute_request = PulseMeter::Sensor::Timelined::Percentile.new(:perc90_minute_request,
47
+ :annotation => 'Minute request 90-percent percentile',
48
+ :interval => 60,
49
+ :ttl => 60 * 60 * 24,
50
+ :p => 0.9
51
+ )
52
+
53
+ agent_names = [:ie, :firefox, :chrome, :other]
54
+ hour_agents = agent_names.each_with_object({}) do |agent, h|
55
+ h[agent] = PulseMeter::Sensor::Timelined::Counter.new(agent,
56
+ :annotation => "Requests from #{agent} browser",
57
+ :interval => 60 * 60,
58
+ :ttl => 60 * 60 * 24 * 30
59
+ )
60
+ end
61
+
62
+
63
+ while true
64
+ requests_per_minute.event(1)
65
+ requests_per_hour.event(1)
66
+
67
+ if Random.rand(10) < 1 # let "errors" sometimes occur
68
+ errors_per_minute.event(1)
69
+ errors_per_hour.event(1)
70
+ end
71
+
72
+ request_time = 0.1 + Random.rand
73
+
74
+ longest_minute_request.event(request_time)
75
+ shortest_minute_request.event(request_time)
76
+ perc90_minute_request.event(request_time)
77
+
78
+ agent_counter = hour_agents[agent_names.shuffle.first]
79
+ agent_counter.event(1)
80
+
81
+ sleep(Random.rand / 10)
82
+ end
@@ -0,0 +1,114 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', 'lib')
2
+
3
+ require "pulse-meter/visualizer"
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ layout = PulseMeter::Visualizer.draw do |l|
8
+
9
+ # Application title
10
+ l.title "Full Example"
11
+
12
+ # Use local time for x-axis of charts
13
+ l.use_utc false
14
+
15
+ # Color for values cut off
16
+ l.outlier_color '#FF0000'
17
+
18
+ # Transfer some global parameters to Highcharts
19
+ l.highchart_options({
20
+ tooltip: {
21
+ value_decimals: 2
22
+ }
23
+ })
24
+
25
+ # Add some pages
26
+ l.page "Request count" do |p|
27
+
28
+ # Add chart (of Highcharts `area' style, `spline', `pie' and `line' are also available)
29
+ p.area "Requests per minute" do |w|
30
+
31
+ # Plot :requests_per_minute values on this chart with black color
32
+ w.sensor :requests_per_minute, color: '#000000'
33
+
34
+ # Plot :errors_per_minute values on this chart with red color
35
+ w.sensor :errors_per_minute, color: '#FF0000'
36
+
37
+ # Plot values for the last hour
38
+ w.timespan 60 * 60
39
+
40
+ # Redraw chart every 10 seconds
41
+ w.redraw_interval 10
42
+
43
+ # Plot incomplete data
44
+ w.show_last_point true
45
+
46
+ # Meaning of the y-axis
47
+ w.values_label "Request count"
48
+
49
+ # Occupy half (5/10) of the page (horizontally)
50
+ w.width 5
51
+
52
+ # Transfer page-wide (and page-specific) options to Highcharts
53
+ p.highchart_options({
54
+ chart: {
55
+ height: 300
56
+ }
57
+ })
58
+ end
59
+
60
+ p.area "Requests per hour" do |w|
61
+
62
+ w.sensor :requests_per_hour, color: '#555555'
63
+ w.sensor :errors_per_hour, color: '#FF0000'
64
+
65
+ w.timespan 24 * 60 * 60
66
+ w.redraw_interval 10
67
+ w.show_last_point true
68
+ w.values_label "Request count"
69
+ w.width 5
70
+
71
+ end
72
+ end
73
+
74
+ l.page "Request times" do |p|
75
+ p.area "Requests time" do |w|
76
+
77
+ w.sensor :longest_minute_request
78
+ w.sensor :shortest_minute_request
79
+ w.sensor :perc90_minute_request
80
+
81
+ w.timespan 60 * 60
82
+ w.redraw_interval 10
83
+ w.show_last_point true
84
+ w.values_label "Time in seconds"
85
+ w.width 10
86
+
87
+ end
88
+ end
89
+
90
+ l.page "Browsers" do |p|
91
+ p.pie "Requests from browser" do |w|
92
+
93
+ [:ie, :firefox, :chrome, :other].each do |sensor|
94
+ w.sensor sensor
95
+ end
96
+
97
+ w.timespan 24 * 60 * 60
98
+ w.redraw_interval 10
99
+ w.show_last_point true
100
+ w.values_label "Request count"
101
+ w.width 10
102
+
103
+ end
104
+
105
+ p.highchart_options({
106
+ chart: {
107
+ height: 500
108
+ }
109
+ })
110
+ end
111
+
112
+ end
113
+
114
+ run layout.to_app
@@ -0,0 +1,2 @@
1
+ web: bundle exec rackup server.ru
2
+ sensor_data_generator: bundle exec ruby client.rb
@@ -0,0 +1,16 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', '..', 'lib')
2
+
3
+ require "pulse-meter"
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ sensor = PulseMeter::Sensor::Timelined::Counter.new(:simple_sample_counter,
8
+ :interval => 5,
9
+ :ttl => 60 * 60
10
+ )
11
+
12
+ while true
13
+ STDERR.puts "tick"
14
+ sensor.event(1)
15
+ sleep(Random.rand)
16
+ end
@@ -0,0 +1,20 @@
1
+ $: << File.join(File.absolute_path(__FILE__), '..', 'lib')
2
+
3
+ require "pulse-meter/visualizer"
4
+
5
+ PulseMeter.redis = Redis.new
6
+
7
+ layout = PulseMeter::Visualizer.draw do |l|
8
+
9
+ l.title "Minimal App"
10
+
11
+ l.page "Main Page" do |p|
12
+ p.area "Live Counter",
13
+ sensor: :simple_sample_counter,
14
+ timespan: 5 * 60,
15
+ redraw_interval: 1
16
+ end
17
+
18
+ end
19
+
20
+ run layout.to_app