peek-performance_bar 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/app/assets/javascripts/peek/views/performance_bar.js +192 -0
- data/lib/peek-performance_bar/version.rb +1 -1
- data/lib/peek/views/performance_bar/process_utilization.rb +3 -27
- metadata +4 -4
- data/app/assets/javascripts/peek/views/performance_bar.coffee +0 -167
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57aac4506b72753e304dd71e6d879103a806c793
|
4
|
+
data.tar.gz: 3b68b08e0a1f4f01044d23678b1aba2a32d58990
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16c23582330e78462677b54481af3d4b3a5f5368aeb45c7244e8271c543b6c33e7e448e44f076db8c49a289be05abaeb49b43b703693202167535a0d15de4b96
|
7
|
+
data.tar.gz: a84688e829e1cfbea5cb8d6f7665586c796887c491445ff7b0b3be7d357e83c2b312ee590cf716e58d1d63562103a0e2d7f1a3821b8f4b1b3e1ff0f04a99ea59
|
data/CHANGELOG.md
CHANGED
@@ -38,3 +38,8 @@
|
|
38
38
|
# 1.2.1
|
39
39
|
|
40
40
|
- Listen to Turbolinks v5 `turbolinks:request-start` and `turbolinks:load` JS events to trigger peek-performance_bar updates. - [#26](https://github.com/peek/peek-performance_bar/pull/26) [@lucasmazza](https://github.com/lucasmazza)
|
41
|
+
|
42
|
+
# 1.3.0
|
43
|
+
|
44
|
+
- Remove CoffeeScript support in favor of plain JavaScript. - [#28](https://github.com/peek/peek-performance_bar/pull/28) [@gfx](https://github.com/gfx)
|
45
|
+
- Use Rack::BodyProxy to fix X-Sendfile header being incorrectly set. - [#27](https://github.com/peek/peek-performance_bar/pull/27) [@rymai](https://github.com/rymai)
|
@@ -0,0 +1,192 @@
|
|
1
|
+
// The mission control window.performance.timing display area.
|
2
|
+
//
|
3
|
+
// Breaks the window.performance.timing numbers down into these groups:
|
4
|
+
//
|
5
|
+
// dns - Time looking up domain. Usually zero.
|
6
|
+
// tcp and ssl - Time used establishing tcp and ssl connections.
|
7
|
+
// redirect - Time spent redirecting since navigation began.
|
8
|
+
// app - Real server time as recorded in the app.
|
9
|
+
// latency - Extra backend and network time where browser was waiting.
|
10
|
+
// frontend - Time spent loading and rendering the DOM until interactive.
|
11
|
+
//
|
12
|
+
// Not all frontend time is counted. The page is considered ready when the
|
13
|
+
// domInteractive ready state is reached. This is before DOMContentLoaded and
|
14
|
+
// onload javascript handlers.
|
15
|
+
class PerformanceBar {
|
16
|
+
static initClass() {
|
17
|
+
// Additional app info to show with the app timing.
|
18
|
+
this.prototype.appInfo = null;
|
19
|
+
|
20
|
+
// The pixel width we're rendering the timing graph into.
|
21
|
+
this.prototype.width = null;
|
22
|
+
}
|
23
|
+
|
24
|
+
// Format a time as ms or s based on how big it is.
|
25
|
+
static formatTime(value) {
|
26
|
+
if (value >= 1000) {
|
27
|
+
return `${(value / 1000).toFixed(3)}s`;
|
28
|
+
} else {
|
29
|
+
return `${value.toFixed(0)}ms`;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
// Create a new PerformanceBar view bound to a given element. The el and width
|
34
|
+
// options should be provided here.
|
35
|
+
constructor(options) {
|
36
|
+
if (options == null) { options = {}; }
|
37
|
+
this.el = $('#peek-view-performance-bar .performance-bar');
|
38
|
+
for (let k in options) { let v = options[k]; this[k] = v; }
|
39
|
+
if (this.width == null) { this.width = this.el.width(); }
|
40
|
+
if (this.timing == null) { this.timing = window.performance.timing; }
|
41
|
+
}
|
42
|
+
|
43
|
+
// Render the performance bar in the associated element. This is a little weird
|
44
|
+
// because it includes the server-side rendering time reported with the
|
45
|
+
// response document which may not line up when using the back/forward button
|
46
|
+
// and loading from cache.
|
47
|
+
render(serverTime) {
|
48
|
+
if (serverTime == null) { serverTime = 0; }
|
49
|
+
this.el.empty();
|
50
|
+
this.addBar('frontend', '#90d35b', 'domLoading', 'domInteractive');
|
51
|
+
|
52
|
+
// time spent talking with the app according to performance.timing
|
53
|
+
let perfNetworkTime = (this.timing.responseEnd - this.timing.requestStart);
|
54
|
+
|
55
|
+
// only include serverTime if it's less than than the browser reported
|
56
|
+
// talking-to-the-app time; otherwise, assume we're loading from cache.
|
57
|
+
if (serverTime && (serverTime <= perfNetworkTime)) {
|
58
|
+
let networkTime = perfNetworkTime - serverTime;
|
59
|
+
this.addBar('latency / receiving', '#f1faff',
|
60
|
+
this.timing.requestStart + serverTime,
|
61
|
+
this.timing.requestStart + serverTime + networkTime);
|
62
|
+
this.addBar('app', '#90afcf',
|
63
|
+
this.timing.requestStart,
|
64
|
+
this.timing.requestStart + serverTime,
|
65
|
+
this.appInfo);
|
66
|
+
} else {
|
67
|
+
this.addBar('backend', '#c1d7ee', 'requestStart', 'responseEnd');
|
68
|
+
}
|
69
|
+
|
70
|
+
this.addBar('tcp / ssl', '#45688e', 'connectStart', 'connectEnd');
|
71
|
+
this.addBar('redirect', '#0c365e', 'redirectStart', 'redirectEnd');
|
72
|
+
this.addBar('dns', '#082541', 'domainLookupStart', 'domainLookupEnd');
|
73
|
+
|
74
|
+
return this.el;
|
75
|
+
}
|
76
|
+
|
77
|
+
// Determine if the page has reached the interactive state yet.
|
78
|
+
isLoaded() {
|
79
|
+
return this.timing.domInteractive;
|
80
|
+
}
|
81
|
+
|
82
|
+
// Integer unix timestamp representing the very beginning of the graph.
|
83
|
+
start() {
|
84
|
+
return this.timing.navigationStart;
|
85
|
+
}
|
86
|
+
|
87
|
+
// Integer unix timestamp representing the very end of the graph.
|
88
|
+
end() {
|
89
|
+
return this.timing.domInteractive;
|
90
|
+
}
|
91
|
+
|
92
|
+
// Total number of milliseconds between the start and end times.
|
93
|
+
total() {
|
94
|
+
return this.end() - this.start();
|
95
|
+
}
|
96
|
+
|
97
|
+
// Helper used to add a bar to the graph.
|
98
|
+
addBar(name, color, start, end, info) {
|
99
|
+
if (typeof start === 'string') { start = this.timing[start]; }
|
100
|
+
if (typeof end === 'string') { end = this.timing[end]; }
|
101
|
+
|
102
|
+
// Skip missing stats
|
103
|
+
if ((start == null) || (end == null)) { return; }
|
104
|
+
|
105
|
+
let time = end - start;
|
106
|
+
let offset = start - this.start();
|
107
|
+
let left = this.mapH(offset);
|
108
|
+
let width = this.mapH(time);
|
109
|
+
|
110
|
+
let title = `${name}: ${PerformanceBar.formatTime(time)}`;
|
111
|
+
let bar = $('<li></li>', {title, class: 'peek-tooltip'});
|
112
|
+
bar.css({
|
113
|
+
width: `${width}px`,
|
114
|
+
left: `${left}px`,
|
115
|
+
background: color
|
116
|
+
});
|
117
|
+
bar.tipsy({gravity: $.fn.tipsy.autoNS});
|
118
|
+
return this.el.append(bar);
|
119
|
+
}
|
120
|
+
|
121
|
+
// Map a time offset value to a horizontal pixel offset.
|
122
|
+
mapH(offset) {
|
123
|
+
return offset * (this.width / this.total());
|
124
|
+
}
|
125
|
+
}
|
126
|
+
PerformanceBar.initClass();
|
127
|
+
|
128
|
+
let renderPerformanceBar = function() {
|
129
|
+
let resp = $('#peek-server_response_time');
|
130
|
+
let time = Math.round(resp.data('time') * 1000);
|
131
|
+
|
132
|
+
let bar = new PerformanceBar;
|
133
|
+
bar.render(time);
|
134
|
+
|
135
|
+
let span = $('<span>', {'class': 'peek-tooltip', title: 'Total navigation time for this page.'})
|
136
|
+
.text(PerformanceBar.formatTime(bar.total()));
|
137
|
+
span.tipsy({gravity: $.fn.tipsy.autoNS});
|
138
|
+
return updateStatus(span);
|
139
|
+
};
|
140
|
+
|
141
|
+
var updateStatus = html => $('#serverstats').html(html);
|
142
|
+
|
143
|
+
let ajaxStart = null;
|
144
|
+
$(document).on('pjax:start page:fetch turbolinks:request-start', event => ajaxStart = event.timeStamp);
|
145
|
+
|
146
|
+
$(document).on('pjax:end page:load turbolinks:load', function(event, xhr) {
|
147
|
+
if (ajaxStart == null) { return; }
|
148
|
+
let ajaxEnd = event.timeStamp;
|
149
|
+
let total = ajaxEnd - ajaxStart;
|
150
|
+
let serverTime = xhr ? parseInt(xhr.getResponseHeader('X-Runtime')) : 0;
|
151
|
+
|
152
|
+
// Defer to include the timing of pjax hook evaluation
|
153
|
+
return setTimeout(function() {
|
154
|
+
let tech;
|
155
|
+
let now = new Date().getTime();
|
156
|
+
let bar = new PerformanceBar({
|
157
|
+
timing: {
|
158
|
+
requestStart: ajaxStart,
|
159
|
+
responseEnd: ajaxEnd,
|
160
|
+
domLoading: ajaxEnd,
|
161
|
+
domInteractive: now
|
162
|
+
},
|
163
|
+
isLoaded() { return true; },
|
164
|
+
start() { return ajaxStart; },
|
165
|
+
end() { return now; }
|
166
|
+
});
|
167
|
+
|
168
|
+
bar.render(serverTime);
|
169
|
+
|
170
|
+
if ($.fn.pjax != null) {
|
171
|
+
tech = 'PJAX';
|
172
|
+
} else {
|
173
|
+
tech = 'Turbolinks';
|
174
|
+
}
|
175
|
+
|
176
|
+
let span = $('<span>', {'class': 'peek-tooltip', title: `${tech} navigation time`})
|
177
|
+
.text(PerformanceBar.formatTime(total));
|
178
|
+
span.tipsy({gravity: $.fn.tipsy.autoNS});
|
179
|
+
updateStatus(span);
|
180
|
+
|
181
|
+
return ajaxStart = null;
|
182
|
+
}
|
183
|
+
, 0);
|
184
|
+
});
|
185
|
+
|
186
|
+
$(function() {
|
187
|
+
if (window.performance) {
|
188
|
+
return renderPerformanceBar();
|
189
|
+
} else {
|
190
|
+
return $('#peek-view-performance-bar').remove();
|
191
|
+
}
|
192
|
+
});
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rack/body_proxy"
|
2
|
+
|
1
3
|
module Peek
|
2
4
|
module Views
|
3
5
|
class PerformanceBar
|
@@ -104,32 +106,6 @@ module Peek
|
|
104
106
|
warn "ProcessUtilization#record_request failed: #{boom.inspect}"
|
105
107
|
end
|
106
108
|
|
107
|
-
# Body wrapper. Yields to the block when body is closed. This is used to
|
108
|
-
# signal when a response is fully finished processing.
|
109
|
-
class Body
|
110
|
-
def initialize(body, &block)
|
111
|
-
@body = body
|
112
|
-
@block = block
|
113
|
-
end
|
114
|
-
|
115
|
-
def each(&block)
|
116
|
-
@body.each(&block)
|
117
|
-
end
|
118
|
-
|
119
|
-
def close
|
120
|
-
@body.close if @body.respond_to?(:close)
|
121
|
-
@block.call
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
|
125
|
-
# Delegate calls to @body to be compatible with other middlewares
|
126
|
-
def method_missing(name, *args, &block)
|
127
|
-
super unless @body.respond_to?(name)
|
128
|
-
|
129
|
-
@body.send(name, *args, &block)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
109
|
# Rack entry point.
|
134
110
|
def call(env)
|
135
111
|
@env = env
|
@@ -142,7 +118,7 @@ module Peek
|
|
142
118
|
env['process.total_requests'] = total_requests
|
143
119
|
|
144
120
|
status, headers, body = @app.call(env)
|
145
|
-
body =
|
121
|
+
body = Rack::BodyProxy.new(body) { record_request }
|
146
122
|
[status, headers, body]
|
147
123
|
end
|
148
124
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peek-performance_bar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garrett Bjerkhoel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: peek
|
@@ -37,7 +37,7 @@ files:
|
|
37
37
|
- LICENSE.txt
|
38
38
|
- README.md
|
39
39
|
- Rakefile
|
40
|
-
- app/assets/javascripts/peek/views/performance_bar.
|
40
|
+
- app/assets/javascripts/peek/views/performance_bar.js
|
41
41
|
- app/assets/stylesheets/peek/views/performance_bar.scss
|
42
42
|
- app/helpers/peek/performance_bar_helper.rb
|
43
43
|
- app/views/peek/results/_performance_bar.html.erb
|
@@ -67,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
67
|
version: '0'
|
68
68
|
requirements: []
|
69
69
|
rubyforge_project:
|
70
|
-
rubygems_version: 2.
|
70
|
+
rubygems_version: 2.6.11
|
71
71
|
signing_key:
|
72
72
|
specification_version: 4
|
73
73
|
summary: Take a peek into the MySQL queries made during your application's requests.
|
@@ -1,167 +0,0 @@
|
|
1
|
-
# The mission control window.performance.timing display area.
|
2
|
-
#
|
3
|
-
# Breaks the window.performance.timing numbers down into these groups:
|
4
|
-
#
|
5
|
-
# dns - Time looking up domain. Usually zero.
|
6
|
-
# tcp and ssl - Time used establishing tcp and ssl connections.
|
7
|
-
# redirect - Time spent redirecting since navigation began.
|
8
|
-
# app - Real server time as recorded in the app.
|
9
|
-
# latency - Extra backend and network time where browser was waiting.
|
10
|
-
# frontend - Time spent loading and rendering the DOM until interactive.
|
11
|
-
#
|
12
|
-
# Not all frontend time is counted. The page is considered ready when the
|
13
|
-
# domInteractive ready state is reached. This is before DOMContentLoaded and
|
14
|
-
# onload javascript handlers.
|
15
|
-
class PerformanceBar
|
16
|
-
# Additional app info to show with the app timing.
|
17
|
-
appInfo: null
|
18
|
-
|
19
|
-
# The pixel width we're rendering the timing graph into.
|
20
|
-
width: null
|
21
|
-
|
22
|
-
# Format a time as ms or s based on how big it is.
|
23
|
-
@formatTime: (value) ->
|
24
|
-
if value >= 1000
|
25
|
-
"#{(value / 1000).toFixed(3)}s"
|
26
|
-
else
|
27
|
-
"#{value.toFixed(0)}ms"
|
28
|
-
|
29
|
-
# Create a new PerformanceBar view bound to a given element. The el and width
|
30
|
-
# options should be provided here.
|
31
|
-
constructor: (options={}) ->
|
32
|
-
@el = $('#peek-view-performance-bar .performance-bar')
|
33
|
-
@[k] = v for k, v of options
|
34
|
-
@width ?= @el.width()
|
35
|
-
@timing ?= window.performance.timing
|
36
|
-
|
37
|
-
# Render the performance bar in the associated element. This is a little weird
|
38
|
-
# because it includes the server-side rendering time reported with the
|
39
|
-
# response document which may not line up when using the back/forward button
|
40
|
-
# and loading from cache.
|
41
|
-
render: (serverTime=0) ->
|
42
|
-
@el.empty()
|
43
|
-
@addBar 'frontend', '#90d35b', 'domLoading', 'domInteractive'
|
44
|
-
|
45
|
-
# time spent talking with the app according to performance.timing
|
46
|
-
perfNetworkTime = (@timing.responseEnd - @timing.requestStart)
|
47
|
-
|
48
|
-
# only include serverTime if it's less than than the browser reported
|
49
|
-
# talking-to-the-app time; otherwise, assume we're loading from cache.
|
50
|
-
if serverTime and serverTime <= perfNetworkTime
|
51
|
-
networkTime = perfNetworkTime - serverTime
|
52
|
-
@addBar 'latency / receiving', '#f1faff',
|
53
|
-
@timing.requestStart + serverTime,
|
54
|
-
@timing.requestStart + serverTime + networkTime
|
55
|
-
@addBar 'app', '#90afcf',
|
56
|
-
@timing.requestStart,
|
57
|
-
@timing.requestStart + serverTime,
|
58
|
-
@appInfo
|
59
|
-
else
|
60
|
-
@addBar 'backend', '#c1d7ee', 'requestStart', 'responseEnd'
|
61
|
-
|
62
|
-
@addBar 'tcp / ssl', '#45688e', 'connectStart', 'connectEnd'
|
63
|
-
@addBar 'redirect', '#0c365e', 'redirectStart', 'redirectEnd'
|
64
|
-
@addBar 'dns', '#082541', 'domainLookupStart', 'domainLookupEnd'
|
65
|
-
|
66
|
-
@el
|
67
|
-
|
68
|
-
# Determine if the page has reached the interactive state yet.
|
69
|
-
isLoaded: ->
|
70
|
-
@timing.domInteractive
|
71
|
-
|
72
|
-
# Integer unix timestamp representing the very beginning of the graph.
|
73
|
-
start: ->
|
74
|
-
@timing.navigationStart
|
75
|
-
|
76
|
-
# Integer unix timestamp representing the very end of the graph.
|
77
|
-
end: ->
|
78
|
-
@timing.domInteractive
|
79
|
-
|
80
|
-
# Total number of milliseconds between the start and end times.
|
81
|
-
total: ->
|
82
|
-
@end() - @start()
|
83
|
-
|
84
|
-
# Helper used to add a bar to the graph.
|
85
|
-
addBar: (name, color, start, end, info) ->
|
86
|
-
start = @timing[start] if typeof start is 'string'
|
87
|
-
end = @timing[end] if typeof end is 'string'
|
88
|
-
|
89
|
-
# Skip missing stats
|
90
|
-
return unless start? and end?
|
91
|
-
|
92
|
-
time = end - start
|
93
|
-
offset = start - @start()
|
94
|
-
left = @mapH(offset)
|
95
|
-
width = @mapH(time)
|
96
|
-
|
97
|
-
title = "#{name}: #{PerformanceBar.formatTime(time)}"
|
98
|
-
bar = $ '<li></li>', title: title, class: 'peek-tooltip'
|
99
|
-
bar.css
|
100
|
-
width: "#{width}px"
|
101
|
-
left: "#{left}px"
|
102
|
-
background: color
|
103
|
-
bar.tipsy gravity: $.fn.tipsy.autoNS
|
104
|
-
@el.append bar
|
105
|
-
|
106
|
-
# Map a time offset value to a horizontal pixel offset.
|
107
|
-
mapH: (offset) ->
|
108
|
-
offset * (@width / @total())
|
109
|
-
|
110
|
-
renderPerformanceBar = ->
|
111
|
-
resp = $('#peek-server_response_time')
|
112
|
-
time = Math.round(resp.data('time') * 1000)
|
113
|
-
|
114
|
-
bar = new PerformanceBar
|
115
|
-
bar.render time
|
116
|
-
|
117
|
-
span = $('<span>', {'class': 'peek-tooltip', title: 'Total navigation time for this page.'})
|
118
|
-
.text(PerformanceBar.formatTime(bar.total()))
|
119
|
-
span.tipsy gravity: $.fn.tipsy.autoNS
|
120
|
-
updateStatus span
|
121
|
-
|
122
|
-
updateStatus = (html) ->
|
123
|
-
$('#serverstats').html html
|
124
|
-
|
125
|
-
ajaxStart = null
|
126
|
-
$(document).on 'pjax:start page:fetch turbolinks:request-start', (event) ->
|
127
|
-
ajaxStart = event.timeStamp
|
128
|
-
|
129
|
-
$(document).on 'pjax:end page:load turbolinks:load', (event, xhr) ->
|
130
|
-
return unless ajaxStart?
|
131
|
-
ajaxEnd = event.timeStamp
|
132
|
-
total = ajaxEnd - ajaxStart
|
133
|
-
serverTime = if xhr then parseInt(xhr.getResponseHeader('X-Runtime')) else 0
|
134
|
-
|
135
|
-
# Defer to include the timing of pjax hook evaluation
|
136
|
-
setTimeout ->
|
137
|
-
now = new Date().getTime()
|
138
|
-
bar = new PerformanceBar
|
139
|
-
timing:
|
140
|
-
requestStart: ajaxStart,
|
141
|
-
responseEnd: ajaxEnd,
|
142
|
-
domLoading: ajaxEnd,
|
143
|
-
domInteractive: now
|
144
|
-
isLoaded: -> true
|
145
|
-
start: -> ajaxStart
|
146
|
-
end: -> now
|
147
|
-
|
148
|
-
bar.render serverTime
|
149
|
-
|
150
|
-
if $.fn.pjax?
|
151
|
-
tech = 'PJAX'
|
152
|
-
else
|
153
|
-
tech = 'Turbolinks'
|
154
|
-
|
155
|
-
span = $('<span>', {'class': 'peek-tooltip', title: "#{tech} navigation time"})
|
156
|
-
.text(PerformanceBar.formatTime(total))
|
157
|
-
span.tipsy gravity: $.fn.tipsy.autoNS
|
158
|
-
updateStatus span
|
159
|
-
|
160
|
-
ajaxStart = null
|
161
|
-
, 0
|
162
|
-
|
163
|
-
$ ->
|
164
|
-
if window.performance
|
165
|
-
renderPerformanceBar()
|
166
|
-
else
|
167
|
-
$('#peek-view-performance-bar').remove()
|