peek-performance_bar 1.2.1 → 1.3.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.
- 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()
|