peek-performance_bar 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # 1.0.0
2
+
3
+ - Initial release.
4
+
5
+ # 1.1.0
6
+
7
+ - Add support for Turbolinks. (#2)
8
+
9
+ # 1.1.1
10
+
11
+ - Remove HTML from tooltips to remove any text artifacts. (#4)
12
+
13
+ # 1.1.2
14
+
15
+ - Namespace the tooltips to not conflict with application styles. (#8)
16
+ - Don't render the performance bar when `window.performance` doesn't exist. (#10)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in peek-performance_bar.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Garrett Bjerkhoel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Peek::PerformanceBar
2
+
3
+ Take a peek into the `window.performance` timing behind your app.
4
+
5
+ ![image](https://f.cloud.github.com/assets/79995/268624/14d9df90-8f47-11e2-9718-111c7c367974.png)
6
+
7
+ Things this peek view provides:
8
+
9
+ - Frontend
10
+ - Latency / Receiving
11
+ - Backend
12
+ - TCP / SSL
13
+ - Redirect
14
+ - DNS Lookup
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'peek-performance_bar'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install peek-performance_bar
29
+
30
+ ## Usage
31
+
32
+ Add the following to your `config/initializers/peek.rb`:
33
+
34
+ ```ruby
35
+ Peek.into Peek::Views::PerformanceBar
36
+ ```
37
+
38
+ You'll then need to add the following CSS and CoffeeScript:
39
+
40
+ CSS:
41
+
42
+ ```scss
43
+ //= require peek
44
+ //= require peek/views/performance_bar
45
+ ```
46
+
47
+ CoffeeScript:
48
+
49
+ ```coffeescript
50
+ #= require peek
51
+ #= require peek/views/performance_bar
52
+ ```
53
+
54
+ Lastly this view requires you insert an additional partial after the `peek/results`:
55
+
56
+ ```erb
57
+ ...
58
+ <%= yield %>
59
+ <%= render 'peek/results' %>
60
+ <%= render 'peek/results/performance_bar' %>
61
+ ...
62
+ ```
63
+
64
+ ## Contributors
65
+
66
+ - [@josh](https://github.com/josh) - The original implementation.
67
+ - [@tmm1](https://github.com/tmm1) - The original implementation.
68
+ - [@rtomayko](https://github.com/rtomayko) - The original implementation.
69
+ - [@kneath](https://github.com/kneath) - The original implementation.
70
+ - [@dewski](https://github.com/dewski) - Just the extractor.
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,167 @@
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: 'n'
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: 'n' })
120
+ updateStatus span
121
+
122
+
123
+ updateStatus = (html) ->
124
+ $('#serverstats').html html
125
+
126
+ ajaxStart = null
127
+ $(document).on 'pjax:start page:fetch', (event) ->
128
+ ajaxStart = event.timeStamp
129
+
130
+ $(document).on 'pjax:end page:change', (event, xhr) ->
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: 'n' })
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()
@@ -0,0 +1,27 @@
1
+ .performance-bar {
2
+ position: relative;
3
+ top: 2px;
4
+ display: inline-block;
5
+ width: 75px;
6
+ height: 10px;
7
+ margin: 0 0 0 5px;
8
+ list-style: none;
9
+ background-color: rgba(0, 0, 0, .5);
10
+ border: 1px solid rgba(0, 0, 0, .7);
11
+ border-radius: 2px;
12
+ box-shadow: 0 1px 0 rgba(255, 255, 255, .15);
13
+
14
+ li {
15
+ position: absolute;
16
+ top: 0;
17
+ bottom: 0;
18
+ overflow: hidden;
19
+ opacity: .8;
20
+ color: transparent;
21
+
22
+ &:hover {
23
+ opacity: 1;
24
+ cursor: default;
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,10 @@
1
+ module Peek
2
+ module PerformanceBarHelper
3
+ def render_server_response_time
4
+ if start_time = request.env['process.request_start']
5
+ time = "%.5f" % (Time.now - start_time)
6
+ "<span id='peek-server_response_time' data-time='#{time}'></span>".html_safe
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ <%= render_server_response_time %>
@@ -0,0 +1,2 @@
1
+ <span id="serverstats">...</span>
2
+ <ul class="performance-bar"></ul>
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'peek-performance_bar/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'peek-performance_bar'
8
+ gem.version = Peek::PerformanceBar::VERSION
9
+ gem.authors = ['Garrett Bjerkhoel']
10
+ gem.email = ['me@garrettbjerkhoel.com']
11
+ gem.description = %q{Take a peek into the MySQL queries made during your application's requests.}
12
+ gem.summary = %q{Take a peek into the MySQL queries made during your application's requests.}
13
+ gem.homepage = 'https://github.com/peek/peek-performance_bar'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency 'peek', '>= 0.0.5'
21
+ end
@@ -0,0 +1,6 @@
1
+ module Peek
2
+ module Views
3
+ class PerformanceBar < View
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ require 'rack/process_utilization'
2
+
3
+ module Peek
4
+ module PerformanceBar
5
+ class Railtie < ::Rails::Engine
6
+ initializer 'peek.performance_bar.mount_process_utilization' do |app|
7
+ app.config.middleware.use Rack::ProcessUtilization
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Peek
2
+ module PerformanceBar
3
+ VERSION = '1.1.2'
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'peek-performance_bar/version'
2
+ require 'peek/views/performance_bar'
3
+ require 'peek-performance_bar/railtie'
@@ -0,0 +1,143 @@
1
+ module Rack
2
+ # Middleware that tracks the amount of time this process spends processing
3
+ # requests, as opposed to being idle waiting for a connection. Statistics
4
+ # are dumped to rack.errors every 5 minutes.
5
+ #
6
+ # NOTE This middleware is not thread safe. It should only be used when
7
+ # rack.multiprocess is true and rack.multithread is false.
8
+ class ProcessUtilization
9
+ class << self
10
+ # The instance of this middleware in a single-threaded production server.
11
+ # Useful for fetching stats about the current request:
12
+ #
13
+ # o = Rack::ProcessUtilization.singleton
14
+ # time, calls = o.gc_stats if o.track_gc?
15
+ attr_accessor :singleton
16
+ end
17
+
18
+ def initialize(app, opts={})
19
+ @app = app
20
+ @window = opts[:window] || 100
21
+ @horizon = nil
22
+ @requests = nil
23
+ @active_time = nil
24
+ @total_requests = 0
25
+
26
+ self.class.singleton = self
27
+ end
28
+
29
+ # time when we began sampling. this is reset every once in a while so
30
+ # averages don't skew over time.
31
+ attr_accessor :horizon
32
+
33
+ # total number of requests that have been processed by this worker since
34
+ # the horizon time.
35
+ attr_accessor :requests
36
+
37
+ # decimal number of seconds the worker has been active within a request
38
+ # since the horizon time.
39
+ attr_accessor :active_time
40
+
41
+ # total requests processed by this worker process since it started
42
+ attr_accessor :total_requests
43
+
44
+ # the amount of time since the horizon
45
+ def horizon_time
46
+ Time.now - horizon
47
+ end
48
+
49
+ # decimal number of seconds this process has been active since the horizon
50
+ # time. This is the inverse of the active time.
51
+ def idle_time
52
+ horizon_time - active_time
53
+ end
54
+
55
+ # percentage of time this process has been active since the horizon time.
56
+ def percentage_active
57
+ (active_time / horizon_time) * 100
58
+ end
59
+
60
+ # percentage of time this process has been idle since the horizon time.
61
+ def percentage_idle
62
+ (idle_time / horizon_time) * 100
63
+ end
64
+
65
+ # number of requests processed per second since the horizon
66
+ def requests_per_second
67
+ requests / horizon_time
68
+ end
69
+
70
+ # average response time since the horizon in milliseconds
71
+ def average_response_time
72
+ (active_time / requests.to_f) * 1000
73
+ end
74
+
75
+ # called exactly once before the first request is processed by a worker
76
+ def first_request
77
+ reset_horizon
78
+ end
79
+
80
+ # reset various counters before the new request
81
+ def reset_stats
82
+ @start = Time.now
83
+ end
84
+
85
+ # resets the horizon and all dependent variables
86
+ def reset_horizon
87
+ @horizon = Time.now
88
+ @active_time = 0.0
89
+ @requests = 0
90
+ end
91
+
92
+ # called immediately after a request to record statistics, update the
93
+ # procline, and dump information to the logfile
94
+ def record_request
95
+ now = Time.now
96
+ diff = (now - @start)
97
+ @active_time += diff
98
+ @requests += 1
99
+
100
+ reset_horizon if now - horizon > @window
101
+ rescue => boom
102
+ warn "ProcessUtilization#record_request failed: #{boom.inspect}"
103
+ end
104
+
105
+ # Body wrapper. Yields to the block when body is closed. This is used to
106
+ # signal when a response is fully finished processing.
107
+ class Body
108
+ def initialize(body, &block)
109
+ @body = body
110
+ @block = block
111
+ end
112
+
113
+ def each(&block)
114
+ @body.each(&block)
115
+ end
116
+
117
+ def close
118
+ @body.close if @body.respond_to?(:close)
119
+ @block.call
120
+ nil
121
+ end
122
+ end
123
+
124
+ # Rack entry point.
125
+ def call(env)
126
+ @env = env
127
+ reset_stats
128
+
129
+ @total_requests += 1
130
+ first_request if @total_requests == 1
131
+
132
+ env['process.request_start'] = @start.to_f
133
+ env['process.total_requests'] = total_requests
134
+
135
+ # newrelic X-Request-Start
136
+ env.delete('HTTP_X_REQUEST_START')
137
+
138
+ status, headers, body = @app.call(env)
139
+ body = Body.new(body) { record_request }
140
+ [status, headers, body]
141
+ end
142
+ end
143
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peek-performance_bar
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Garrett Bjerkhoel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: peek
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.5
30
+ description: Take a peek into the MySQL queries made during your application's requests.
31
+ email:
32
+ - me@garrettbjerkhoel.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - CHANGELOG.md
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - app/assets/javascripts/peek/views/performance_bar.coffee
44
+ - app/assets/stylesheets/peek/views/performance_bar.scss
45
+ - app/helpers/peek/performance_bar_helper.rb
46
+ - app/views/peek/results/_performance_bar.html.erb
47
+ - app/views/peek/views/_performance_bar.html.erb
48
+ - glimpse-performance_bar.gemspec
49
+ - lib/peek-performance_bar.rb
50
+ - lib/peek-performance_bar/railtie.rb
51
+ - lib/peek-performance_bar/version.rb
52
+ - lib/peek/views/performance_bar.rb
53
+ - lib/rack/process_utilization.rb
54
+ homepage: https://github.com/peek/peek-performance_bar
55
+ licenses: []
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 1.8.23
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Take a peek into the MySQL queries made during your application's requests.
78
+ test_files: []