peek-performance-bar-1.3.0/ 0000755 0001750 0001750 00000000000 13154003373 014523 5 ustar pravi pravi peek-performance-bar-1.3.0/CHANGELOG.md 0000644 0001750 0001750 00000002547 13154003373 016344 0 ustar pravi pravi # 1.0.0 - Initial release. # 1.1.0 - Add support for Turbolinks. (#2) # 1.1.1 - Remove HTML from tooltips to remove any text artifacts. (#4) # 1.1.2 - Namespace the tooltips to not conflict with application styles. (#8) - Don't render the performance bar when `window.performance` doesn't exist. (#10) # 1.1.3 - Use auto-gravity for tooltips on Peek bars that may be fixed to the bottom of the window. # 1.1.4 - Fixed incorrect time on initial page load with newer Turbolinks - #17 (@brandonweiss) # 1.1.5 - Don't strip `X-Request-Start` header which New Relic relies on. - #20 # 1.1.6 - Namespace ProcessUtilization middleware to Peek::Views::PerformanceBar - #21 (@jnunemaker) # 1.2.0 - Allow `PerformanceBar::ProcessUtilization::Body` to behave like normal response body - #22 (@tubaxenor) # 1.2.1 - 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) # 1.3.0 - Remove CoffeeScript support in favor of plain JavaScript. - [#28](https://github.com/peek/peek-performance_bar/pull/28) [@gfx](https://github.com/gfx) - 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) peek-performance-bar-1.3.0/README.md 0000644 0001750 0001750 00000002635 13154003373 016010 0 ustar pravi pravi # Peek::PerformanceBar Take a peek into the `window.performance` timing behind your app.  Things this peek view provides: - Frontend - Latency / Receiving - Backend - TCP / SSL - Redirect - DNS Lookup ## Installation Add this line to your application's Gemfile: gem 'peek-performance_bar' And then execute: $ bundle Or install it yourself as: $ gem install peek-performance_bar ## Usage Add the following to your `config/initializers/peek.rb`: ```ruby Peek.into Peek::Views::PerformanceBar ``` You'll then need to add the following CSS and CoffeeScript: CSS: ```scss //= require peek //= require peek/views/performance_bar ``` CoffeeScript: ```coffeescript #= require peek #= require peek/views/performance_bar ``` ## Contributors - [@josh](https://github.com/josh) - The original implementation. - [@tmm1](https://github.com/tmm1) - The original implementation. - [@rtomayko](https://github.com/rtomayko) - The original implementation. - [@kneath](https://github.com/kneath) - The original implementation. - [@dewski](https://github.com/dewski) - Just the extractor. ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request peek-performance-bar-1.3.0/lib/ 0000755 0001750 0001750 00000000000 13154003373 015271 5 ustar pravi pravi peek-performance-bar-1.3.0/lib/peek/ 0000755 0001750 0001750 00000000000 13154003373 016215 5 ustar pravi pravi peek-performance-bar-1.3.0/lib/peek/views/ 0000755 0001750 0001750 00000000000 13154003373 017352 5 ustar pravi pravi peek-performance-bar-1.3.0/lib/peek/views/performance_bar/ 0000755 0001750 0001750 00000000000 13154003373 022477 5 ustar pravi pravi peek-performance-bar-1.3.0/lib/peek/views/performance_bar/process_utilization.rb 0000644 0001750 0001750 00000007550 13154003373 027144 0 ustar pravi pravi require "rack/body_proxy" module Peek module Views class PerformanceBar # Middleware that tracks the amount of time this process spends processing # requests, as opposed to being idle waiting for a connection. Statistics # are dumped to rack.errors every 5 minutes. # # NOTE This middleware is not thread safe. It should only be used when # rack.multiprocess is true and rack.multithread is false. class ProcessUtilization class << self # The instance of this middleware in a single-threaded production server. # Useful for fetching stats about the current request: # # o = Rack::ProcessUtilization.singleton # time, calls = o.gc_stats if o.track_gc? attr_accessor :singleton end def initialize(app, opts={}) @app = app @window = opts[:window] || 100 @horizon = nil @requests = nil @active_time = nil @total_requests = 0 self.class.singleton = self end # time when we began sampling. this is reset every once in a while so # averages don't skew over time. attr_accessor :horizon # total number of requests that have been processed by this worker since # the horizon time. attr_accessor :requests # decimal number of seconds the worker has been active within a request # since the horizon time. attr_accessor :active_time # total requests processed by this worker process since it started attr_accessor :total_requests # the amount of time since the horizon def horizon_time Time.now - horizon end # decimal number of seconds this process has been active since the horizon # time. This is the inverse of the active time. def idle_time horizon_time - active_time end # percentage of time this process has been active since the horizon time. def percentage_active (active_time / horizon_time) * 100 end # percentage of time this process has been idle since the horizon time. def percentage_idle (idle_time / horizon_time) * 100 end # number of requests processed per second since the horizon def requests_per_second requests / horizon_time end # average response time since the horizon in milliseconds def average_response_time (active_time / requests.to_f) * 1000 end # called exactly once before the first request is processed by a worker def first_request reset_horizon end # reset various counters before the new request def reset_stats @start = Time.now end # resets the horizon and all dependent variables def reset_horizon @horizon = Time.now @active_time = 0.0 @requests = 0 end # called immediately after a request to record statistics, update the # procline, and dump information to the logfile def record_request now = Time.now diff = (now - @start) @active_time += diff @requests += 1 reset_horizon if now - horizon > @window rescue => boom warn "ProcessUtilization#record_request failed: #{boom.inspect}" end # Rack entry point. def call(env) @env = env reset_stats @total_requests += 1 first_request if @total_requests == 1 env['process.request_start'] = @start.to_f env['process.total_requests'] = total_requests status, headers, body = @app.call(env) body = Rack::BodyProxy.new(body) { record_request } [status, headers, body] end end end end end peek-performance-bar-1.3.0/lib/peek/views/performance_bar.rb 0000644 0001750 0001750 00000000115 13154003373 023021 0 ustar pravi pravi module Peek module Views class PerformanceBar < View end end end peek-performance-bar-1.3.0/lib/peek-performance_bar/ 0000755 0001750 0001750 00000000000 13154003373 021340 5 ustar pravi pravi peek-performance-bar-1.3.0/lib/peek-performance_bar/railtie.rb 0000644 0001750 0001750 00000000474 13154003373 023323 0 ustar pravi pravi require 'peek/views/performance_bar/process_utilization' module Peek module PerformanceBar class Railtie < ::Rails::Engine initializer 'peek.performance_bar.mount_process_utilization' do |app| app.config.middleware.use Peek::Views::PerformanceBar::ProcessUtilization end end end end peek-performance-bar-1.3.0/lib/peek-performance_bar/version.rb 0000644 0001750 0001750 00000000104 13154003373 023345 0 ustar pravi pravi module Peek module PerformanceBar VERSION = '1.3.0' end end peek-performance-bar-1.3.0/lib/peek-performance_bar.rb 0000644 0001750 0001750 00000000163 13154003373 021665 0 ustar pravi pravi require 'peek-performance_bar/version' require 'peek/views/performance_bar' require 'peek-performance_bar/railtie' peek-performance-bar-1.3.0/peek-performance_bar.gemspec 0000644 0001750 0001750 00000001635 13154003373 022144 0 ustar pravi pravi # -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'peek-performance_bar/version' Gem::Specification.new do |gem| gem.name = 'peek-performance_bar' gem.version = Peek::PerformanceBar::VERSION gem.authors = ['Garrett Bjerkhoel'] gem.email = ['me@garrettbjerkhoel.com'] gem.description = %q{Take a peek into the MySQL queries made during your application's requests.} gem.summary = %q{Take a peek into the MySQL queries made during your application's requests.} gem.homepage = 'https://github.com/peek/peek-performance_bar' gem.files = `git ls-files`.split($/) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] gem.add_dependency 'peek', '>= 0.1.0' end peek-performance-bar-1.3.0/LICENSE.txt 0000644 0001750 0001750 00000002061 13154003373 016345 0 ustar pravi pravi Copyright (c) 2013 Garrett Bjerkhoel MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. peek-performance-bar-1.3.0/.gitignore 0000644 0001750 0001750 00000000232 13154003373 016510 0 ustar pravi pravi *.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp peek-performance-bar-1.3.0/Rakefile 0000644 0001750 0001750 00000000034 13154003373 016165 0 ustar pravi pravi require 'bundler/gem_tasks' peek-performance-bar-1.3.0/app/ 0000755 0001750 0001750 00000000000 13154003373 015303 5 ustar pravi pravi peek-performance-bar-1.3.0/app/helpers/ 0000755 0001750 0001750 00000000000 13154003373 016745 5 ustar pravi pravi peek-performance-bar-1.3.0/app/helpers/peek/ 0000755 0001750 0001750 00000000000 13154003373 017671 5 ustar pravi pravi peek-performance-bar-1.3.0/app/helpers/peek/performance_bar_helper.rb 0000644 0001750 0001750 00000000452 13154003373 024703 0 ustar pravi pravi module Peek module PerformanceBarHelper def render_server_response_time if start_time = request.env['process.request_start'] time = "%.5f" % (Time.now - start_time) "".html_safe end end end end peek-performance-bar-1.3.0/app/views/ 0000755 0001750 0001750 00000000000 13154003373 016440 5 ustar pravi pravi peek-performance-bar-1.3.0/app/views/peek/ 0000755 0001750 0001750 00000000000 13154003373 017364 5 ustar pravi pravi peek-performance-bar-1.3.0/app/views/peek/views/ 0000755 0001750 0001750 00000000000 13154003373 020521 5 ustar pravi pravi peek-performance-bar-1.3.0/app/views/peek/views/_performance_bar.html.erb 0000644 0001750 0001750 00000000104 13154003373 025435 0 ustar pravi pravi ...
peek-performance-bar-1.3.0/app/views/peek/results/ 0000755 0001750 0001750 00000000000 13154003373 021065 5 ustar pravi pravi peek-performance-bar-1.3.0/app/views/peek/results/_performance_bar.html.erb 0000644 0001750 0001750 00000000043 13154003373 026003 0 ustar pravi pravi <%= render_server_response_time %> peek-performance-bar-1.3.0/app/assets/ 0000755 0001750 0001750 00000000000 13154003373 016605 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/javascripts/ 0000755 0001750 0001750 00000000000 13154003373 021136 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/javascripts/peek/ 0000755 0001750 0001750 00000000000 13154003373 022062 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/javascripts/peek/views/ 0000755 0001750 0001750 00000000000 13154003373 023217 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/javascripts/peek/views/performance_bar.js 0000644 0001750 0001750 00000014306 13154003373 026706 0 ustar pravi pravi // The mission control window.performance.timing display area. // // Breaks the window.performance.timing numbers down into these groups: // // dns - Time looking up domain. Usually zero. // tcp and ssl - Time used establishing tcp and ssl connections. // redirect - Time spent redirecting since navigation began. // app - Real server time as recorded in the app. // latency - Extra backend and network time where browser was waiting. // frontend - Time spent loading and rendering the DOM until interactive. // // Not all frontend time is counted. The page is considered ready when the // domInteractive ready state is reached. This is before DOMContentLoaded and // onload javascript handlers. class PerformanceBar { static initClass() { // Additional app info to show with the app timing. this.prototype.appInfo = null; // The pixel width we're rendering the timing graph into. this.prototype.width = null; } // Format a time as ms or s based on how big it is. static formatTime(value) { if (value >= 1000) { return `${(value / 1000).toFixed(3)}s`; } else { return `${value.toFixed(0)}ms`; } } // Create a new PerformanceBar view bound to a given element. The el and width // options should be provided here. constructor(options) { if (options == null) { options = {}; } this.el = $('#peek-view-performance-bar .performance-bar'); for (let k in options) { let v = options[k]; this[k] = v; } if (this.width == null) { this.width = this.el.width(); } if (this.timing == null) { this.timing = window.performance.timing; } } // Render the performance bar in the associated element. This is a little weird // because it includes the server-side rendering time reported with the // response document which may not line up when using the back/forward button // and loading from cache. render(serverTime) { if (serverTime == null) { serverTime = 0; } this.el.empty(); this.addBar('frontend', '#90d35b', 'domLoading', 'domInteractive'); // time spent talking with the app according to performance.timing let perfNetworkTime = (this.timing.responseEnd - this.timing.requestStart); // only include serverTime if it's less than than the browser reported // talking-to-the-app time; otherwise, assume we're loading from cache. if (serverTime && (serverTime <= perfNetworkTime)) { let networkTime = perfNetworkTime - serverTime; this.addBar('latency / receiving', '#f1faff', this.timing.requestStart + serverTime, this.timing.requestStart + serverTime + networkTime); this.addBar('app', '#90afcf', this.timing.requestStart, this.timing.requestStart + serverTime, this.appInfo); } else { this.addBar('backend', '#c1d7ee', 'requestStart', 'responseEnd'); } this.addBar('tcp / ssl', '#45688e', 'connectStart', 'connectEnd'); this.addBar('redirect', '#0c365e', 'redirectStart', 'redirectEnd'); this.addBar('dns', '#082541', 'domainLookupStart', 'domainLookupEnd'); return this.el; } // Determine if the page has reached the interactive state yet. isLoaded() { return this.timing.domInteractive; } // Integer unix timestamp representing the very beginning of the graph. start() { return this.timing.navigationStart; } // Integer unix timestamp representing the very end of the graph. end() { return this.timing.domInteractive; } // Total number of milliseconds between the start and end times. total() { return this.end() - this.start(); } // Helper used to add a bar to the graph. addBar(name, color, start, end, info) { if (typeof start === 'string') { start = this.timing[start]; } if (typeof end === 'string') { end = this.timing[end]; } // Skip missing stats if ((start == null) || (end == null)) { return; } let time = end - start; let offset = start - this.start(); let left = this.mapH(offset); let width = this.mapH(time); let title = `${name}: ${PerformanceBar.formatTime(time)}`; let bar = $('', {title, class: 'peek-tooltip'}); bar.css({ width: `${width}px`, left: `${left}px`, background: color }); bar.tipsy({gravity: $.fn.tipsy.autoNS}); return this.el.append(bar); } // Map a time offset value to a horizontal pixel offset. mapH(offset) { return offset * (this.width / this.total()); } } PerformanceBar.initClass(); let renderPerformanceBar = function() { let resp = $('#peek-server_response_time'); let time = Math.round(resp.data('time') * 1000); let bar = new PerformanceBar; bar.render(time); let span = $('', {'class': 'peek-tooltip', title: 'Total navigation time for this page.'}) .text(PerformanceBar.formatTime(bar.total())); span.tipsy({gravity: $.fn.tipsy.autoNS}); return updateStatus(span); }; var updateStatus = html => $('#serverstats').html(html); let ajaxStart = null; $(document).on('pjax:start page:fetch turbolinks:request-start', event => ajaxStart = event.timeStamp); $(document).on('pjax:end page:load turbolinks:load', function(event, xhr) { if (ajaxStart == null) { return; } let ajaxEnd = event.timeStamp; let total = ajaxEnd - ajaxStart; let serverTime = xhr ? parseInt(xhr.getResponseHeader('X-Runtime')) : 0; // Defer to include the timing of pjax hook evaluation return setTimeout(function() { let tech; let now = new Date().getTime(); let bar = new PerformanceBar({ timing: { requestStart: ajaxStart, responseEnd: ajaxEnd, domLoading: ajaxEnd, domInteractive: now }, isLoaded() { return true; }, start() { return ajaxStart; }, end() { return now; } }); bar.render(serverTime); if ($.fn.pjax != null) { tech = 'PJAX'; } else { tech = 'Turbolinks'; } let span = $('', {'class': 'peek-tooltip', title: `${tech} navigation time`}) .text(PerformanceBar.formatTime(total)); span.tipsy({gravity: $.fn.tipsy.autoNS}); updateStatus(span); return ajaxStart = null; } , 0); }); $(function() { if (window.performance) { return renderPerformanceBar(); } else { return $('#peek-view-performance-bar').remove(); } }); peek-performance-bar-1.3.0/app/assets/stylesheets/ 0000755 0001750 0001750 00000000000 13154003373 021161 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/stylesheets/peek/ 0000755 0001750 0001750 00000000000 13154003373 022105 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/stylesheets/peek/views/ 0000755 0001750 0001750 00000000000 13154003373 023242 5 ustar pravi pravi peek-performance-bar-1.3.0/app/assets/stylesheets/peek/views/performance_bar.scss 0000644 0001750 0001750 00000000750 13154003373 027266 0 ustar pravi pravi .performance-bar { position: relative; top: 2px; display: inline-block; width: 75px; height: 10px; margin: 0 0 0 5px; list-style: none; background-color: rgba(0, 0, 0, .5); border: 1px solid rgba(0, 0, 0, .7); border-radius: 2px; box-shadow: 0 1px 0 rgba(255, 255, 255, .15); li { position: absolute; top: 0; bottom: 0; overflow: hidden; opacity: .8; color: transparent; &:hover { opacity: 1; cursor: default; } } } peek-performance-bar-1.3.0/Gemfile 0000644 0001750 0001750 00000000151 13154003373 016013 0 ustar pravi pravi source 'https://rubygems.org' # Specify your gem's dependencies in peek-performance_bar.gemspec gemspec