glimpse-performance_bar 1.0.0

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/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: []