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.
- data/LICENSE +20 -0
- data/README.rdoc +106 -0
- data/app/controllers/system_metrics/metrics_controller.rb +49 -0
- data/app/helpers/system_metrics/metrics_helper.rb +36 -0
- data/app/models/system_metrics/metric.rb +36 -0
- data/app/views/layouts/system_metrics/metrics.html.erb +42 -0
- data/app/views/system_metrics/metrics/_menu.html.erb +8 -0
- data/app/views/system_metrics/metrics/_metric_row.html.erb +19 -0
- data/app/views/system_metrics/metrics/_metric_table.html.erb +24 -0
- data/app/views/system_metrics/metrics/_portlet.html.erb +18 -0
- data/app/views/system_metrics/metrics/_title_bar.html.erb +17 -0
- data/app/views/system_metrics/metrics/admin.html.erb +27 -0
- data/app/views/system_metrics/metrics/category.html.erb +4 -0
- data/app/views/system_metrics/metrics/index.html.erb +6 -0
- data/app/views/system_metrics/metrics/show.html.erb +26 -0
- data/config/routes.rb +7 -0
- data/init.rb +1 -0
- data/lib/generators/system_metrics.rb +9 -0
- data/lib/generators/system_metrics/install/install_generator.rb +21 -0
- data/lib/generators/system_metrics/migration/migration_generator.rb +26 -0
- data/lib/generators/system_metrics/migration/templates/migration.rb +22 -0
- data/lib/system-metrics.rb +1 -0
- data/lib/system_metrics.rb +33 -0
- data/lib/system_metrics/collector.rb +32 -0
- data/lib/system_metrics/config.rb +38 -0
- data/lib/system_metrics/engine.rb +43 -0
- data/lib/system_metrics/instrument.rb +10 -0
- data/lib/system_metrics/instrument/action_controller.rb +20 -0
- data/lib/system_metrics/instrument/action_mailer.rb +15 -0
- data/lib/system_metrics/instrument/action_view.rb +23 -0
- data/lib/system_metrics/instrument/active_record.rb +20 -0
- data/lib/system_metrics/instrument/base.rb +77 -0
- data/lib/system_metrics/instrument/rack.rb +11 -0
- data/lib/system_metrics/middleware.rb +32 -0
- data/lib/system_metrics/nested_event.rb +57 -0
- data/lib/system_metrics/store.rb +31 -0
- data/lib/system_metrics/version.rb +3 -0
- data/public/images/rings_13.png +0 -0
- data/public/stylesheets/app.css +13 -0
- data/public/stylesheets/base.css +46 -0
- data/public/stylesheets/footer.css +6 -0
- data/public/stylesheets/graphs.css +9 -0
- data/public/stylesheets/header.css +52 -0
- data/public/stylesheets/ie.css +36 -0
- data/public/stylesheets/menu_bar.css +7 -0
- data/public/stylesheets/metric.css +19 -0
- data/public/stylesheets/payload.css +4 -0
- data/public/stylesheets/portlet.css +11 -0
- data/public/stylesheets/print.css +29 -0
- data/public/stylesheets/reset.css +65 -0
- data/public/stylesheets/title_bar.css +29 -0
- data/public/stylesheets/typography.css +123 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/db_setup.rb +41 -0
- data/spec/support/mock_app.rb +23 -0
- data/spec/support/notifications_support.rb +12 -0
- data/spec/support/test_logger.rb +11 -0
- data/spec/support/test_store.rb +11 -0
- data/spec/support/transactional_specs.rb +17 -0
- data/spec/system_metrics/collector_spec.rb +60 -0
- data/spec/system_metrics/config_spec.rb +24 -0
- data/spec/system_metrics/engine_spec.rb +50 -0
- data/spec/system_metrics/middleware_spec.rb +45 -0
- data/spec/system_metrics_spec.rb +29 -0
- data/system-metrics.gemspec +24 -0
- 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; }
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|