system-metrics 0.1.0 → 0.2.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/README.rdoc +16 -1
- data/app/views/layouts/system_metrics/metrics.html.erb +4 -15
- data/app/views/system_metrics/metrics/_menu.html.erb +1 -1
- data/app/views/system_metrics/metrics/admin.html.erb +1 -1
- data/lib/system_metrics/engine.rb +3 -3
- data/lib/system_metrics/instrument/base.rb +1 -1
- data/lib/system_metrics/nested_event.rb +8 -1
- data/lib/system_metrics/store.rb +10 -15
- data/lib/system_metrics/version.rb +1 -1
- data/public/images/gradient.png +0 -0
- data/public/stylesheets/app.css +197 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/support/db_setup.rb +3 -0
- data/spec/support/mock_app.rb +1 -3
- data/spec/support/notifications_support.rb +10 -0
- data/spec/system_metrics/collector_spec.rb +1 -2
- data/spec/system_metrics/config_spec.rb +16 -8
- data/spec/system_metrics/engine_spec.rb +53 -28
- data/spec/system_metrics/instrument/action_controller_spec.rb +51 -0
- data/spec/system_metrics/instrument/action_mailer_spec.rb +40 -0
- data/spec/system_metrics/instrument/action_view_spec.rb +40 -0
- data/spec/system_metrics/instrument/active_record_spec.rb +50 -0
- data/spec/system_metrics/instrument/base_spec.rb +38 -0
- data/spec/system_metrics/instrument/rack_spec.rb +28 -0
- data/spec/system_metrics/middleware_spec.rb +1 -3
- data/spec/system_metrics/nested_event_spec.rb +87 -0
- data/spec/system_metrics/store_spec.rb +49 -0
- data/spec/system_metrics_spec.rb +0 -1
- data/system-metrics.gemspec +1 -0
- metadata +30 -11
- data/public/stylesheets/base.css +0 -46
- data/public/stylesheets/footer.css +0 -6
- data/public/stylesheets/graphs.css +0 -9
- data/public/stylesheets/header.css +0 -52
- data/public/stylesheets/menu_bar.css +0 -7
- data/public/stylesheets/metric.css +0 -19
- data/public/stylesheets/payload.css +0 -4
- data/public/stylesheets/portlet.css +0 -11
- data/public/stylesheets/title_bar.css +0 -29
data/README.rdoc
CHANGED
@@ -74,9 +74,24 @@ This example instrument illustrates three concepts
|
|
74
74
|
(2) It will inform SystemMetrics that the event should not be collected if the current user is an administrator.
|
75
75
|
(3) It will add the current user's name to the event payload
|
76
76
|
|
77
|
+
= Date/Time Ranges
|
78
|
+
|
79
|
+
You can change the time range of metrics shown on the dashboard and category screens using two query parameters; from and to. Consider the following examples:
|
80
|
+
|
81
|
+
Show dashboard metrics from three hours ago until now
|
82
|
+
http://localhost:3000/system/metrics?from=3.hours
|
83
|
+
|
84
|
+
Show one day of dashboard metrics beginning two days ago
|
85
|
+
http://localhost:3000/system/metrics?from=2.days&to=1.day
|
86
|
+
|
87
|
+
Show one hour of category metrics ending 30 minutes ago
|
88
|
+
http://localhost:3000/system/metrics/category/active_record?from=90.minutes&to=30.minutes
|
89
|
+
|
90
|
+
As you might have guessed, the 'from' and 'to' query parameters take an integer and any valid method added by ActiveSupport::CoreExtensions::Numeric::Time to create a time in the past. The default 'from' time is 30 minutes and 'to' is 0 minutes (current time).
|
91
|
+
|
77
92
|
= Authorization
|
78
93
|
|
79
|
-
|
94
|
+
Since SystemMetrics is not recommended for production use, the lack of authorization is not likely a problem. However, it should be rather straightforward to add your own. Consider the following example implementation:
|
80
95
|
|
81
96
|
# config/initializers/system_metrics_authorization.rb
|
82
97
|
module SystemMetricsAuthorization
|
@@ -10,31 +10,20 @@
|
|
10
10
|
<!--[if lt IE 8]>
|
11
11
|
<%= stylesheet_link_tag 'system_metrics/ie', :media => 'screen, projection' %>
|
12
12
|
<![endif]-->
|
13
|
-
<%= stylesheet_link_tag 'system_metrics/
|
14
|
-
'system_metrics/app',
|
15
|
-
'system_metrics/header',
|
16
|
-
'system_metrics/footer',
|
17
|
-
'system_metrics/title_bar',
|
18
|
-
'system_metrics/portlet',
|
19
|
-
'system_metrics/payload',
|
20
|
-
'system_metrics/metric',
|
21
|
-
'system_metrics/menu_bar',
|
22
|
-
'system_metrics/graphs',
|
23
|
-
:media => 'screen, projection', :cache => 'system_metrics' %>
|
13
|
+
<%= stylesheet_link_tag 'system_metrics/app', :media => 'screen, projection' %>
|
24
14
|
<%= csrf_meta_tag %>
|
25
15
|
</head>
|
26
16
|
<body>
|
27
17
|
<div class="container">
|
28
|
-
<div id="
|
29
|
-
<%= image_tag 'system_metrics/rings_13.png' %>
|
18
|
+
<div id="hd">
|
30
19
|
<h1>System Metrics</h1>
|
31
20
|
<%= render "menu" %>
|
32
21
|
</div>
|
33
22
|
<%= render "title_bar" %>
|
34
|
-
<div id="
|
23
|
+
<div id="bd">
|
35
24
|
<%= yield %>
|
36
25
|
</div>
|
37
|
-
<div id="
|
26
|
+
<div id="ft">
|
38
27
|
<span>Developed by <a href="http://www.nearinfinity.com">Near Infinity</a></span>
|
39
28
|
</div>
|
40
29
|
</div>
|
@@ -5,13 +5,13 @@ module SystemMetrics
|
|
5
5
|
|
6
6
|
config.system_metrics = SystemMetrics::Config.new
|
7
7
|
|
8
|
-
initializer "system_metrics.initialize" do |app|
|
8
|
+
initializer "system_metrics.initialize", :before => "system_metrics.start_subscriber" do |app|
|
9
9
|
self.smc = app.config.system_metrics
|
10
10
|
raise ArgumentError.new(smc.errors) if smc.invalid?
|
11
11
|
self.collector = SystemMetrics::Collector.new(smc.store)
|
12
12
|
end
|
13
13
|
|
14
|
-
initializer "system_metrics.start_subscriber" do |app|
|
14
|
+
initializer "system_metrics.start_subscriber", :before => "system_metrics.add_middleware" do |app|
|
15
15
|
ActiveSupport::Notifications.subscribe /^[^!]/ do |*args|
|
16
16
|
unless smc.notification_exclude_patterns.any? { |pattern| pattern =~ name }
|
17
17
|
process_event SystemMetrics::NestedEvent.new(*args)
|
@@ -19,7 +19,7 @@ module SystemMetrics
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
initializer "system_metrics.add_middleware" do |app|
|
22
|
+
initializer "system_metrics.add_middleware", :before => :load_environment_config do |app|
|
23
23
|
app.config.middleware.use SystemMetrics::Middleware, collector, smc.path_exclude_patterns
|
24
24
|
end
|
25
25
|
|
@@ -41,7 +41,7 @@ module SystemMetrics
|
|
41
41
|
# ignore the event, it should still return true if it generally
|
42
42
|
# handles events like the one passed.
|
43
43
|
def handles?(event)
|
44
|
-
event.name =~ pattern
|
44
|
+
(event.name =~ pattern) != nil
|
45
45
|
end
|
46
46
|
|
47
47
|
# Indicates whether the given event should be completely ingored
|
@@ -2,6 +2,7 @@ require 'active_support/notifications'
|
|
2
2
|
|
3
3
|
module SystemMetrics
|
4
4
|
class NestedEvent < ActiveSupport::Notifications::Event
|
5
|
+
attr_reader :action, :category
|
5
6
|
|
6
7
|
def self.arrange(events, options={})
|
7
8
|
events.sort! { |a, b| a.end <=> b.end } unless options[:presort] == false
|
@@ -17,6 +18,11 @@ module SystemMetrics
|
|
17
18
|
root
|
18
19
|
end
|
19
20
|
|
21
|
+
def initialize(*args)
|
22
|
+
super
|
23
|
+
@action, @category = name.split('.')
|
24
|
+
end
|
25
|
+
|
20
26
|
def exclusive_duration
|
21
27
|
@exclusive_duration ||= duration - children.inject(0.0) { |sum, child| sum + child.duration }
|
22
28
|
end
|
@@ -45,8 +51,9 @@ module SystemMetrics
|
|
45
51
|
def to_hash
|
46
52
|
{
|
47
53
|
:name => name,
|
54
|
+
:action => action,
|
55
|
+
:category => category,
|
48
56
|
:started_at => started_at,
|
49
|
-
:ended_at => self.ended_at,
|
50
57
|
:transaction_id => transaction_id,
|
51
58
|
:payload => payload,
|
52
59
|
:duration => duration,
|
data/lib/system_metrics/store.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module SystemMetrics
|
2
2
|
class Store
|
3
|
+
|
3
4
|
def save(events)
|
5
|
+
return unless events.present?
|
4
6
|
root_event = SystemMetrics::NestedEvent.arrange(events, :presort => false)
|
5
7
|
root_model = create_metric(root_event)
|
6
8
|
root_model.update_attributes(:request_id => root_model.id)
|
@@ -9,23 +11,16 @@ module SystemMetrics
|
|
9
11
|
|
10
12
|
private
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def save_tree(events, request_id, parent_id)
|
15
|
+
events.each do |event|
|
16
|
+
model = create_metric(event, :request_id => request_id, :parent_id => parent_id)
|
17
|
+
save_tree(event.children, request_id, model.id)
|
18
|
+
end
|
16
19
|
end
|
17
|
-
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
params = event.to_hash.delete_if do |key, value|
|
22
|
-
![:name, :started_at, :transaction_id, :payload, :duration, :exclusive_duration].include?(key)
|
21
|
+
def create_metric(event, merge_params={})
|
22
|
+
SystemMetrics::Metric.create(event.to_hash.merge(merge_params))
|
23
23
|
end
|
24
|
-
|
25
|
-
:action => action,
|
26
|
-
:category => category
|
27
|
-
)
|
28
|
-
SystemMetrics::Metric.create(params)
|
29
|
-
end
|
24
|
+
|
30
25
|
end
|
31
26
|
end
|
Binary file
|
data/public/stylesheets/app.css
CHANGED
@@ -1,13 +1,207 @@
|
|
1
|
-
|
1
|
+
body {
|
2
|
+
color: #555555;
|
3
|
+
font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 75%;
|
5
|
+
line-height: 150%;
|
6
|
+
background-color: #FAD08A;
|
7
|
+
margin: 0 auto;
|
8
|
+
max-width: 100%;
|
2
9
|
width: 100%;
|
3
10
|
}
|
4
11
|
|
5
|
-
|
12
|
+
a:hover {
|
13
|
+
color: #0099FF;
|
14
|
+
}
|
15
|
+
|
16
|
+
a:visited {
|
17
|
+
color: #004C99;
|
18
|
+
}
|
19
|
+
|
20
|
+
a {
|
21
|
+
color: #0066CC;
|
22
|
+
text-decoration: underline;
|
23
|
+
}
|
24
|
+
|
25
|
+
h1, h2, h3, h4, h5, h6 {
|
26
|
+
color: #555555;
|
27
|
+
font-weight: bold;
|
28
|
+
}
|
29
|
+
|
30
|
+
#container {
|
31
|
+
height: 100%;
|
32
|
+
min-height: 100%;
|
33
|
+
}
|
34
|
+
|
35
|
+
#bd {
|
6
36
|
padding: 20px;
|
7
|
-
background: #
|
37
|
+
background-color: #FAFCFF;
|
38
|
+
}
|
39
|
+
|
40
|
+
tr {
|
41
|
+
height: 45px;
|
42
|
+
}
|
43
|
+
|
44
|
+
thead tr {
|
45
|
+
color: #667495;
|
8
46
|
}
|
9
47
|
|
10
48
|
div.clear {
|
11
49
|
clear: both;
|
12
50
|
}
|
13
51
|
|
52
|
+
#hd {
|
53
|
+
top: 0;
|
54
|
+
width: 100%;
|
55
|
+
height: 40px;
|
56
|
+
z-index: 2;
|
57
|
+
background: url("../../images/system_metrics/gradient.png") repeat-x scroll 0 -58px transparent;
|
58
|
+
text-align: right;
|
59
|
+
border-top: 2px solid #F8BC59;
|
60
|
+
}
|
61
|
+
|
62
|
+
#hd h1 {
|
63
|
+
color: #F8BC59;
|
64
|
+
font-size: 1.8em;
|
65
|
+
position: absolute;
|
66
|
+
top: 10px;
|
67
|
+
left: 20px;
|
68
|
+
text-shadow: 0 1px 0 black;
|
69
|
+
}
|
70
|
+
|
71
|
+
#hd ul {
|
72
|
+
border: 0 none;
|
73
|
+
clear: both;
|
74
|
+
margin: 0 1.622%;
|
75
|
+
overflow: hidden;
|
76
|
+
padding: 0;
|
77
|
+
text-shadow: 0 1px 0 black;
|
78
|
+
}
|
79
|
+
|
80
|
+
#hd ul li {
|
81
|
+
color: #C4D9FF;
|
82
|
+
cursor: pointer;
|
83
|
+
margin: 6px 0.5em 0;
|
84
|
+
padding: 0;
|
85
|
+
display: inline-block;
|
86
|
+
list-style-image: none;
|
87
|
+
list-style-type: none;
|
88
|
+
margin-left: 0;
|
89
|
+
padding-left: 10px;
|
90
|
+
padding-right: 10px;
|
91
|
+
vertical-align: middle;
|
92
|
+
white-space: nowrap;
|
93
|
+
-moz-box-orient: vertical;
|
94
|
+
}
|
95
|
+
|
96
|
+
#hd ul li a {
|
97
|
+
color: inherit;
|
98
|
+
cursor: inherit;
|
99
|
+
line-height: 28px;
|
100
|
+
padding: 0.5em 1em;
|
101
|
+
text-decoration: inherit;
|
102
|
+
}
|
103
|
+
|
104
|
+
#hd ul li.active {
|
105
|
+
background-color: #323C46;
|
106
|
+
color: #F6A828;
|
107
|
+
-moz-border-radius: 25px 25px 25px 25px;
|
108
|
+
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5) inset, 0 -1px 0 rgba(255, 255, 255, 0.2) inset;
|
109
|
+
}
|
110
|
+
|
111
|
+
#hd ul li:hover {
|
112
|
+
color: #EEE;
|
113
|
+
}
|
114
|
+
|
115
|
+
#title-bar {
|
116
|
+
background: #EFEFEF;
|
117
|
+
border-bottom: 1px solid #EDEDED;
|
118
|
+
color: #5E6469;
|
119
|
+
font-size: 1em;
|
120
|
+
font-weight: bold;
|
121
|
+
line-height: 140%;
|
122
|
+
padding: 10px 30px;
|
123
|
+
position: relative;
|
124
|
+
text-shadow: 0 1px 0 white;
|
125
|
+
-moz-box-shadow: 0 1px 2px #AAAAAA;
|
126
|
+
-webkit-box-shadow: 0 1px 2px #AAAAAA;
|
127
|
+
box-shadow: 0 1px 2px #AAAAAA;
|
128
|
+
}
|
129
|
+
|
130
|
+
#title-bar h2 {
|
131
|
+
font-size: 2.6em;
|
132
|
+
font-weight: bold;
|
133
|
+
margin: 12px 0 10px;
|
134
|
+
}
|
135
|
+
|
136
|
+
#title-bar h4 {
|
137
|
+
margin-bottom: 0.25em;
|
138
|
+
font-weight: normal;
|
139
|
+
}
|
140
|
+
|
141
|
+
#ft {
|
142
|
+
background-color: #FAD08A;
|
143
|
+
border-top: 1px solid #F8BC59;
|
144
|
+
color: #B17107;
|
145
|
+
height: 45px;
|
146
|
+
line-height: 44px;
|
147
|
+
text-align: center;
|
148
|
+
text-shadow: 0 -1px 0 #FBDAA2;
|
149
|
+
width: 100%;
|
150
|
+
z-index: 2;
|
151
|
+
-moz-box-shadow: 0 1px 0 #FCE3BB inset;
|
152
|
+
}
|
153
|
+
|
154
|
+
#menu-bar {
|
155
|
+
margin-bottom: 10px;
|
156
|
+
}
|
157
|
+
|
158
|
+
#menu-bar form {
|
159
|
+
float: right;
|
160
|
+
}
|
161
|
+
|
162
|
+
#metric-details {
|
163
|
+
border-right: 1px solid #EFEFEF;
|
164
|
+
float: left;
|
165
|
+
padding-right: 20px;
|
166
|
+
width: 37%;
|
167
|
+
}
|
168
|
+
|
169
|
+
#metric-details h2 {
|
170
|
+
font-size: 500%;
|
171
|
+
line-height: normal;
|
172
|
+
margin-bottom: 20px;
|
173
|
+
color: #323537;
|
174
|
+
text-align: center;
|
175
|
+
}
|
176
|
+
|
177
|
+
#metric-children {
|
178
|
+
float: right;
|
179
|
+
width: 60%;
|
180
|
+
}
|
181
|
+
|
182
|
+
div.payload {
|
183
|
+
border-left: 1px solid #EFEFEF;
|
184
|
+
padding-left: 10px;
|
185
|
+
}
|
186
|
+
|
187
|
+
div.portlet {
|
188
|
+
position: relative;
|
189
|
+
float: left;
|
190
|
+
width: 45em;
|
191
|
+
border: 1px solid #EDEDED;
|
192
|
+
padding: 10px;
|
193
|
+
margin: 0 20px 20px 0;
|
194
|
+
-moz-box-shadow: 1px 1px 1px #AAA;
|
195
|
+
-webkit-box-shadow: 1px 1px 1px #AAA;
|
196
|
+
box-shadow: 1px 1px 1px #AAA;
|
197
|
+
}
|
198
|
+
|
199
|
+
.hbar {
|
200
|
+
display: block;
|
201
|
+
background: #0A0;
|
202
|
+
height: 10px;
|
203
|
+
}
|
204
|
+
|
205
|
+
.hbar.slow {
|
206
|
+
background: #A00;
|
207
|
+
}
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/db_setup.rb
CHANGED
@@ -23,10 +23,13 @@ class CreateMeasurements < ActiveRecord::Migration
|
|
23
23
|
def self.up
|
24
24
|
create_table :system_metrics, :force => true do |t|
|
25
25
|
t.string :name
|
26
|
+
t.string :action
|
27
|
+
t.string :category
|
26
28
|
t.datetime :started_at
|
27
29
|
t.string :transaction_id
|
28
30
|
t.text :payload
|
29
31
|
t.float :duration
|
32
|
+
t.float :exclusive_duration
|
30
33
|
t.integer :request_id
|
31
34
|
t.integer :parent_id
|
32
35
|
end
|
data/spec/support/mock_app.rb
CHANGED
@@ -9,4 +9,14 @@ module NotificationsSupport
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
def event(options={})
|
13
|
+
SystemMetrics::NestedEvent.new(
|
14
|
+
options[:name] || 'sql.active_record',
|
15
|
+
options[:start] || (Time.now - 5.seconds),
|
16
|
+
options[:end] || Time.now,
|
17
|
+
options[:transaction_id] || 'tid',
|
18
|
+
options[:payload] || {}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
12
22
|
end
|