system-metrics 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 (66) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +106 -0
  3. data/app/controllers/system_metrics/metrics_controller.rb +49 -0
  4. data/app/helpers/system_metrics/metrics_helper.rb +36 -0
  5. data/app/models/system_metrics/metric.rb +36 -0
  6. data/app/views/layouts/system_metrics/metrics.html.erb +42 -0
  7. data/app/views/system_metrics/metrics/_menu.html.erb +8 -0
  8. data/app/views/system_metrics/metrics/_metric_row.html.erb +19 -0
  9. data/app/views/system_metrics/metrics/_metric_table.html.erb +24 -0
  10. data/app/views/system_metrics/metrics/_portlet.html.erb +18 -0
  11. data/app/views/system_metrics/metrics/_title_bar.html.erb +17 -0
  12. data/app/views/system_metrics/metrics/admin.html.erb +27 -0
  13. data/app/views/system_metrics/metrics/category.html.erb +4 -0
  14. data/app/views/system_metrics/metrics/index.html.erb +6 -0
  15. data/app/views/system_metrics/metrics/show.html.erb +26 -0
  16. data/config/routes.rb +7 -0
  17. data/init.rb +1 -0
  18. data/lib/generators/system_metrics.rb +9 -0
  19. data/lib/generators/system_metrics/install/install_generator.rb +21 -0
  20. data/lib/generators/system_metrics/migration/migration_generator.rb +26 -0
  21. data/lib/generators/system_metrics/migration/templates/migration.rb +22 -0
  22. data/lib/system-metrics.rb +1 -0
  23. data/lib/system_metrics.rb +33 -0
  24. data/lib/system_metrics/collector.rb +32 -0
  25. data/lib/system_metrics/config.rb +38 -0
  26. data/lib/system_metrics/engine.rb +43 -0
  27. data/lib/system_metrics/instrument.rb +10 -0
  28. data/lib/system_metrics/instrument/action_controller.rb +20 -0
  29. data/lib/system_metrics/instrument/action_mailer.rb +15 -0
  30. data/lib/system_metrics/instrument/action_view.rb +23 -0
  31. data/lib/system_metrics/instrument/active_record.rb +20 -0
  32. data/lib/system_metrics/instrument/base.rb +77 -0
  33. data/lib/system_metrics/instrument/rack.rb +11 -0
  34. data/lib/system_metrics/middleware.rb +32 -0
  35. data/lib/system_metrics/nested_event.rb +57 -0
  36. data/lib/system_metrics/store.rb +31 -0
  37. data/lib/system_metrics/version.rb +3 -0
  38. data/public/images/rings_13.png +0 -0
  39. data/public/stylesheets/app.css +13 -0
  40. data/public/stylesheets/base.css +46 -0
  41. data/public/stylesheets/footer.css +6 -0
  42. data/public/stylesheets/graphs.css +9 -0
  43. data/public/stylesheets/header.css +52 -0
  44. data/public/stylesheets/ie.css +36 -0
  45. data/public/stylesheets/menu_bar.css +7 -0
  46. data/public/stylesheets/metric.css +19 -0
  47. data/public/stylesheets/payload.css +4 -0
  48. data/public/stylesheets/portlet.css +11 -0
  49. data/public/stylesheets/print.css +29 -0
  50. data/public/stylesheets/reset.css +65 -0
  51. data/public/stylesheets/title_bar.css +29 -0
  52. data/public/stylesheets/typography.css +123 -0
  53. data/spec/spec_helper.rb +10 -0
  54. data/spec/support/db_setup.rb +41 -0
  55. data/spec/support/mock_app.rb +23 -0
  56. data/spec/support/notifications_support.rb +12 -0
  57. data/spec/support/test_logger.rb +11 -0
  58. data/spec/support/test_store.rb +11 -0
  59. data/spec/support/transactional_specs.rb +17 -0
  60. data/spec/system_metrics/collector_spec.rb +60 -0
  61. data/spec/system_metrics/config_spec.rb +24 -0
  62. data/spec/system_metrics/engine_spec.rb +50 -0
  63. data/spec/system_metrics/middleware_spec.rb +45 -0
  64. data/spec/system_metrics_spec.rb +29 -0
  65. data/system-metrics.gemspec +24 -0
  66. metadata +163 -0
@@ -0,0 +1,123 @@
1
+ /* --------------------------------------------------------------
2
+
3
+ typography.css
4
+ * Sets up some sensible default typography.
5
+
6
+ -------------------------------------------------------------- */
7
+
8
+ /* Default font settings.
9
+ The font-size percentage is of 16px. (0.75 * 16px = 12px) */
10
+ html { font-size:100.01%; }
11
+ body {
12
+ font-size: 75%;
13
+ color: #222;
14
+ background: #fff;
15
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
16
+ }
17
+
18
+
19
+ /* Headings
20
+ -------------------------------------------------------------- */
21
+
22
+ h1,h2,h3,h4,h5,h6 { font-weight: normal; color: #111; }
23
+
24
+ h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; }
25
+ h2 { font-size: 2em; margin-bottom: 0.75em; }
26
+ h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; }
27
+ h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; }
28
+ h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; }
29
+ h6 { font-size: 1em; font-weight: bold; }
30
+
31
+ h1 img, h2 img, h3 img,
32
+ h4 img, h5 img, h6 img {
33
+ margin: 0;
34
+ }
35
+
36
+
37
+ /* Text elements
38
+ -------------------------------------------------------------- */
39
+
40
+ p { margin: 0 0 1.5em; }
41
+ /*
42
+ These can be used to pull an image at the start of a paragraph, so
43
+ that the text flows around it (usage: <p><img class="left">Text</p>)
44
+ */
45
+ .left { float: left !important; }
46
+ p .left { margin: 1.5em 1.5em 1.5em 0; padding: 0; }
47
+ .right { float: right !important; }
48
+ p .right { margin: 1.5em 0 1.5em 1.5em; padding: 0; }
49
+
50
+ a:focus,
51
+ a:hover { color: #09f; }
52
+ a { color: #06c; text-decoration: underline; }
53
+
54
+ blockquote { margin: 1.5em; color: #666; font-style: italic; }
55
+ strong,dfn { font-weight: bold; }
56
+ em,dfn { font-style: italic; }
57
+ sup, sub { line-height: 0; }
58
+
59
+ abbr,
60
+ acronym { border-bottom: 1px dotted #666; }
61
+ address { margin: 0 0 1.5em; font-style: italic; }
62
+ del { color:#666; }
63
+
64
+ pre { margin: 1.5em 0; white-space: pre; }
65
+ pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; }
66
+
67
+
68
+ /* Lists
69
+ -------------------------------------------------------------- */
70
+
71
+ li ul,
72
+ li ol { margin: 0; }
73
+ ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; }
74
+
75
+ ul { list-style-type: disc; }
76
+ ol { list-style-type: decimal; }
77
+
78
+ dl { margin: 0 0 1.5em 0; }
79
+ dl dt { font-weight: bold; }
80
+ dd { margin-left: 1.5em;}
81
+
82
+
83
+ /* Tables
84
+ -------------------------------------------------------------- */
85
+
86
+ /*
87
+ Because of the need for padding on TH and TD, the vertical rhythm
88
+ on table cells has to be 27px, instead of the standard 18px or 36px
89
+ of other elements.
90
+ */
91
+ table { margin-bottom: 1.4em; width:100%; }
92
+ th { font-weight: bold; }
93
+ thead th { background: #c3d9ff; }
94
+ th,td,caption { padding: 4px 10px 4px 5px; }
95
+ /*
96
+ You can zebra-stripe your tables in outdated browsers by adding
97
+ the class "even" to every other table row.
98
+ */
99
+ tbody tr:nth-child(even) td,
100
+ tbody tr.even td {
101
+ background: #e5ecf9;
102
+ }
103
+ tfoot { font-style: italic; }
104
+ caption { background: #eee; }
105
+
106
+
107
+ /* Misc classes
108
+ -------------------------------------------------------------- */
109
+
110
+ .small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; }
111
+ .large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; }
112
+ .hide { display: none; }
113
+
114
+ .quiet { color: #666; }
115
+ .loud { color: #000; }
116
+ .highlight { background:#ff0; }
117
+ .added { background:#060; color: #fff; }
118
+ .removed { background:#900; color: #fff; }
119
+
120
+ .first { margin-left:0; padding-left:0; }
121
+ .last { margin-right:0; padding-right:0; }
122
+ .top { margin-top:0; padding-top:0; }
123
+ .bottom { margin-bottom:0; padding-bottom:0; }
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'rails'
3
+
4
+ # Requires supporting files with custom matchers and macros, etc,
5
+ # in ./support/ and its subdirectories.
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
7
+
8
+ RSpec.configure do |config|
9
+ config.include NotificationsSupport
10
+ end
@@ -0,0 +1,41 @@
1
+ require 'active_support/core_ext'
2
+ require 'active_record'
3
+ require 'fileutils'
4
+
5
+ tmpdir = File.join(File.dirname(__FILE__), '..', '..', 'tmp')
6
+ FileUtils.mkdir(tmpdir) unless File.exist?(tmpdir)
7
+ test_db = File.join(tmpdir, 'test.db')
8
+
9
+ connection_spec = {
10
+ :adapter => 'sqlite3',
11
+ :database => test_db
12
+ }
13
+
14
+ # Delete any existing instance of the test database
15
+ FileUtils.rm test_db, :force => true
16
+
17
+ # Create a new test database
18
+ ActiveRecord::Base.establish_connection(connection_spec)
19
+
20
+ # ActiveRecord::Base.connection.initialize_schema_migrations_table
21
+
22
+ class CreateMeasurements < ActiveRecord::Migration
23
+ def self.up
24
+ create_table :system_metrics, :force => true do |t|
25
+ t.string :name
26
+ t.datetime :started_at
27
+ t.string :transaction_id
28
+ t.text :payload
29
+ t.float :duration
30
+ t.integer :request_id
31
+ t.integer :parent_id
32
+ end
33
+ end
34
+
35
+ def self.down
36
+ drop_table :system_metrics
37
+ end
38
+ end
39
+
40
+ CreateMeasurements.up
41
+
@@ -0,0 +1,23 @@
1
+ class MockApp
2
+ attr_accessor :config
3
+
4
+ def config
5
+ @config ||= Config.new
6
+ end
7
+
8
+ class Config
9
+ def system_metrics
10
+ @system_metrics ||= {}
11
+ end
12
+
13
+ def middleware
14
+ @middleware ||= Middleware.new
15
+ end
16
+ end
17
+
18
+ class Middleware < Array
19
+ def use(*args)
20
+ self.push args
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module NotificationsSupport
2
+
3
+ def self.included(base)
4
+ base.class_eval do
5
+ after(:each) do
6
+ ActiveSupport::Notifications.notifier.instance_variable_set(:@subscribers, [])
7
+ ActiveSupport::Notifications.notifier.instance_variable_set(:@listeners_for, {})
8
+ end
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,11 @@
1
+ class TestLogger
2
+ attr_reader :messages
3
+
4
+ def initialize
5
+ @messages = []
6
+ end
7
+
8
+ def info(msg)
9
+ messages << msg
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class TestStore
2
+ attr_reader :events
3
+
4
+ def initialize
5
+ @events = []
6
+ end
7
+
8
+ def save(events)
9
+ @events = events
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module TransactionalSpecs
2
+
3
+ def self.included(base)
4
+ base.class_eval do
5
+ around(:each) do |spec|
6
+ ActiveRecord::Base.transaction do
7
+ begin
8
+ spec.call
9
+ ensure
10
+ raise ActiveRecord::Rollback
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,60 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'system_metrics/collector'
3
+
4
+ describe SystemMetrics::Collector do
5
+ before(:each) do
6
+ @store = TestStore.new
7
+ @collector = SystemMetrics::Collector.new(@store)
8
+ end
9
+
10
+ it 'should collect events while collecting' do
11
+ @collector.collect do
12
+ @collector.collect_event(Object.new)
13
+ @collector.collect_event(Object.new)
14
+ end
15
+
16
+ @store.should have(2).events
17
+ end
18
+
19
+ it 'should not collect events if collecting is turned off' do
20
+ @collector.collect do
21
+ @collector.collect_event(Object.new)
22
+ SystemMetrics.collection_off
23
+ @collector.collect_event(Object.new)
24
+ SystemMetrics.collection_on
25
+ end
26
+
27
+ @store.should have(1).events
28
+ end
29
+
30
+ it 'should clear out all thread resident events after collecting' do
31
+ @collector.collect do
32
+ @collector.collect_event(Object.new)
33
+ @collector.collect_event(Object.new)
34
+ end
35
+
36
+ @store.should have(2).events
37
+ @collector.send(:events).should be_empty
38
+ end
39
+
40
+ it 'should set collecting to off after a call to collect' do
41
+ @collector.collect do
42
+ @collector.collect_event(Object.new)
43
+ @collector.collect_event(Object.new)
44
+ end
45
+
46
+ SystemMetrics.should_not be_collecting
47
+ end
48
+
49
+ it 'should not save events to the store if an exception occurs' do
50
+ lambda {
51
+ @collector.collect do
52
+ @collector.collect_event(Object.new)
53
+ @collector.collect_event(Object.new)
54
+ raise StandardError.new
55
+ end
56
+ }.should raise_error
57
+
58
+ @store.should have(0).events
59
+ end
60
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'system_metrics/config'
3
+
4
+ describe SystemMetrics::Config do
5
+ it 'should be valid with instruments, path_exclude_patterns, and a store' do
6
+ config = SystemMetrics::Config.new({:instruments => [Object.new], :store => Object.new})
7
+ config.should be_valid
8
+ end
9
+
10
+ it 'should be invalid without instruments' do
11
+ config = SystemMetrics::Config.new({:store => Object.new})
12
+ config.should_not be_valid
13
+ end
14
+
15
+ it 'should be invalid without a store' do
16
+ config = SystemMetrics::Config.new({:instruments => [Object.new]})
17
+ config.should_not be_valid
18
+ end
19
+
20
+ it 'should be invalid if passed an unrecognized option' do
21
+ config = SystemMetrics::Config.new({:instruments => [Object.new], :path_exclude_patterns => [], :store => Object.new, :bogus => true})
22
+ config.should_not be_valid
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'system_metrics/engine'
3
+
4
+ describe SystemMetrics::Engine do
5
+ before(:each) do
6
+ @app = MockApp.new
7
+ @store = TestStore.new
8
+ @app.config.system_metrics[:instruments] = [Comb::Instrument::Base.new(/xyz123/)]
9
+ @app.config.system_metrics[:store] = @store
10
+ end
11
+
12
+ it 'should establish ActiveSupport::Notification subscribers for each instrument' do
13
+ @app.config.system_metrics[:instruments] = [Comb::Instrument::Base.new(/^sql/), Comb::Instrument::Base.new(/^render/)]
14
+ engine = SystemMetrics::Engine.new
15
+ run_initializers(engine)
16
+
17
+ collector = SystemMetrics::Collector.new(@store)
18
+ collector.collect do
19
+ ActiveSupport::Notifications.instrument 'sql.active_record'
20
+ ActiveSupport::Notifications.instrument 'render.action_view'
21
+ ActiveSupport::Notifications.instrument 'process.action_controller'
22
+ end
23
+
24
+ @store.should have(2).events
25
+ @store.events.map(&:name) =~ ['sql.active_record', 'render.action_view']
26
+ end
27
+
28
+ it 'should setup the SystemMetrics::Middleware' do
29
+ engine = SystemMetrics::Engine.new
30
+ run_initializers(engine)
31
+
32
+ middleware = @app.config.middleware.first
33
+ middleware[0].should == SystemMetrics::Middleware
34
+ middleware[1].should_not be_nil
35
+ middleware[2].should_not be_nil
36
+ end
37
+
38
+ def run_initializer(engine, name)
39
+ initializer = engine.initializers.find do |initializer|
40
+ initializer.name == name
41
+ end
42
+ initializer.run @app
43
+ end
44
+
45
+ def run_initializers(engine)
46
+ ['system_metrics.initialize', 'system_metrics.add_subscribers', 'system_metrics.add_middleware'].each do |name|
47
+ run_initializer(engine, name)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'system_metrics/middleware'
3
+ require 'system_metrics/collector'
4
+
5
+ describe SystemMetrics::Middleware do
6
+ before(:each) do
7
+ @store = TestStore.new
8
+ @collector = SystemMetrics::Collector.new(@store)
9
+ end
10
+
11
+ it 'should invoke the collector for a non-excluded paths' do
12
+ event = Object.new
13
+ blk = Proc.new { @collector.collect_event(event) }
14
+ middleware = SystemMetrics::Middleware.new(blk, @collector, [])
15
+ env = { "PATH_INFO" => '/collect' }
16
+
17
+ middleware.call(env)
18
+ @store.should have(1).events
19
+ end
20
+
21
+ it 'should not invoke the collector for excluded paths' do
22
+ event = Object.new
23
+ blk = Proc.new { @collector.collect_event(event) }
24
+ middleware = SystemMetrics::Middleware.new(blk, @collector, [/^\/collect/])
25
+ env = { "PATH_INFO" => '/collect' }
26
+
27
+ middleware.call(env)
28
+ @store.should have(0).events
29
+ end
30
+
31
+ it 'should wrap everything with an instrumentation for rack requests' do
32
+ ActiveSupport::Notifications.subscribe(/\.rack$/) do |*args|
33
+ @collector.collect_event(ActiveSupport::Notifications::Event.new(*args))
34
+ end
35
+
36
+ event = Object.new
37
+ blk = Proc.new {}
38
+ middleware = SystemMetrics::Middleware.new(blk, @collector, [])
39
+ env = { "PATH_INFO" => '/collect' }
40
+
41
+ middleware.call(env)
42
+ @store.should have(1).events
43
+ @store.events.first.name.should == 'request.rack'
44
+ end
45
+ end