glimpse-performance_bar 1.0.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/.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/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in glimpse-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,38 @@
1
+ # Glimpse::PerformanceBar
2
+
3
+ Provide a glimpse into the MySQL queries made during your application's requests.
4
+
5
+ Things this glimpse view provides:
6
+
7
+ - Total number of MySQL queries called during the request
8
+ - The duration of the queries made during the request
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'glimpse-performance_bar'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install glimpse-performance_bar
23
+
24
+ ## Usage
25
+
26
+ Add the following to your `config/initializers/glimpse.rb`:
27
+
28
+ ```ruby
29
+ Glimpse.into Glimpse::Views::PerformanceBar
30
+ ```
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,162 @@
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 = $('.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
+ bar = $("<li class='tooltip'>#{name}</li>")
98
+ title = "#{name}: #{PerformanceBar.formatTime(time)}"
99
+ title += "\n\n#{info}".replace(/\n/g, '<br/>') if info
100
+ bar.attr 'title', title
101
+ bar.css
102
+ width: "#{width}px"
103
+ left: "#{left}px"
104
+ background: color
105
+ bar.tipsy gravity: 'n', html: true
106
+ @el.append bar
107
+
108
+ # Map a time offset value to a horizontal pixel offset.
109
+ mapH: (offset) ->
110
+ offset * (@width / @total())
111
+
112
+ renderPerformanceBar = ->
113
+ resp = $('#glimpse-server_response_time')
114
+ time = Math.round(resp.data('time') * 1000)
115
+
116
+ bar = new PerformanceBar
117
+ bar.render time
118
+
119
+ span = $('<span>', {'class': 'tooltip', title: 'Total navigation time for this page.'})
120
+ .text(PerformanceBar.formatTime(bar.total()))
121
+ span.tipsy({ gravity: 'n' })
122
+ updateStatus span
123
+
124
+
125
+ updateStatus = (html) ->
126
+ $('#serverstats').html html
127
+
128
+ pjaxStart = null
129
+ $(document).on 'pjax:start', (event) ->
130
+ pjaxStart = event.timeStamp
131
+
132
+ $(document).on 'pjax:end', (event, xhr) ->
133
+ pjaxEnd = event.timeStamp
134
+ total = pjaxEnd - pjaxStart
135
+ serverTime = if xhr then parseInt(xhr.getResponseHeader('X-Runtime')) else 0
136
+
137
+ # Defer to include the timing of pjax hook evaluation
138
+ setTimeout ->
139
+ now = new Date().getTime()
140
+ bar = new PerformanceBar
141
+ timing:
142
+ requestStart: pjaxStart,
143
+ responseEnd: pjaxEnd,
144
+ domLoading: pjaxEnd,
145
+ domInteractive: now
146
+ isLoaded: -> true
147
+ start: -> pjaxStart
148
+ end: -> now
149
+
150
+ bar.render serverTime
151
+
152
+ console.log 'why u called'
153
+ span = $('<span>', {'class': 'tooltip', title: 'PJAX navigation time'})
154
+ .text(PerformanceBar.formatTime(total))
155
+ span.tipsy({ gravity: 'n' })
156
+ updateStatus span
157
+
158
+ pjaxStart = null
159
+ , 0
160
+
161
+ $ ->
162
+ renderPerformanceBar()
@@ -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 Glimpse
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='glimpse-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 'glimpse-performance_bar/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'glimpse-performance_bar'
8
+ gem.version = Glimpse::PerformanceBar::VERSION
9
+ gem.authors = ['Garrett Bjerkhoel']
10
+ gem.email = ['me@garrettbjerkhoel.com']
11
+ gem.description = %q{Provide a glimpse into the MySQL queries made during your application's requests.}
12
+ gem.summary = %q{Provide a glimpse into the MySQL queries made during your application's requests.}
13
+ gem.homepage = 'https://github.com/dewski/glimpse-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 'glimpse'
21
+ end
@@ -0,0 +1,6 @@
1
+ module Glimpse
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 Glimpse
4
+ module PerformanceBar
5
+ class Railtie < ::Rails::Engine
6
+ initializer 'glimpse.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 Glimpse
2
+ module PerformanceBar
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'glimpse-performance_bar/version'
2
+ require 'glimpse/views/performance_bar'
3
+ require 'glimpse-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: glimpse-performance_bar
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Garrett Bjerkhoel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: glimpse
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
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'
30
+ description: Provide a glimpse into the MySQL queries made during your application's
31
+ requests.
32
+ email:
33
+ - me@garrettbjerkhoel.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - app/assets/javascripts/glimpse/views/performance_bar.coffee
44
+ - app/assets/stylesheets/glimpse/views/performance_bar.scss
45
+ - app/helpers/glimpse/performance_bar_helper.rb
46
+ - app/views/glimpse/results/_performance_bar.html.erb
47
+ - app/views/glimpse/views/_performance_bar.html.erb
48
+ - glimpse-performance_bar.gemspec
49
+ - lib/glimpse-performance_bar.rb
50
+ - lib/glimpse-performance_bar/railtie.rb
51
+ - lib/glimpse-performance_bar/version.rb
52
+ - lib/glimpse/views/performance_bar.rb
53
+ - lib/rack/process_utilization.rb
54
+ homepage: https://github.com/dewski/glimpse-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: Provide a glimpse into the MySQL queries made during your application's requests.
78
+ test_files: []