peek-performance-bar-1.3.0/0000755000175000017500000000000013154003373014523 5ustar pravipravipeek-performance-bar-1.3.0/CHANGELOG.md0000644000175000017500000000254713154003373016344 0ustar pravipravi# 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.md0000644000175000017500000000263513154003373016010 0ustar pravipravi# Peek::PerformanceBar Take a peek into the `window.performance` timing behind your app. ![image](https://f.cloud.github.com/assets/79995/268624/14d9df90-8f47-11e2-9718-111c7c367974.png) 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/0000755000175000017500000000000013154003373015271 5ustar pravipravipeek-performance-bar-1.3.0/lib/peek/0000755000175000017500000000000013154003373016215 5ustar pravipravipeek-performance-bar-1.3.0/lib/peek/views/0000755000175000017500000000000013154003373017352 5ustar pravipravipeek-performance-bar-1.3.0/lib/peek/views/performance_bar/0000755000175000017500000000000013154003373022477 5ustar pravipravipeek-performance-bar-1.3.0/lib/peek/views/performance_bar/process_utilization.rb0000644000175000017500000000755013154003373027144 0ustar pravipravirequire "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.rb0000644000175000017500000000011513154003373023021 0ustar pravipravimodule Peek module Views class PerformanceBar < View end end end peek-performance-bar-1.3.0/lib/peek-performance_bar/0000755000175000017500000000000013154003373021340 5ustar pravipravipeek-performance-bar-1.3.0/lib/peek-performance_bar/railtie.rb0000644000175000017500000000047413154003373023323 0ustar pravipravirequire '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.rb0000644000175000017500000000010413154003373023345 0ustar pravipravimodule Peek module PerformanceBar VERSION = '1.3.0' end end peek-performance-bar-1.3.0/lib/peek-performance_bar.rb0000644000175000017500000000016313154003373021665 0ustar pravipravirequire 'peek-performance_bar/version' require 'peek/views/performance_bar' require 'peek-performance_bar/railtie' peek-performance-bar-1.3.0/peek-performance_bar.gemspec0000644000175000017500000000163513154003373022144 0ustar pravipravi# -*- 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.txt0000644000175000017500000000206113154003373016345 0ustar pravipraviCopyright (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/.gitignore0000644000175000017500000000023213154003373016510 0ustar pravipravi*.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/Rakefile0000644000175000017500000000003413154003373016165 0ustar pravipravirequire 'bundler/gem_tasks' peek-performance-bar-1.3.0/app/0000755000175000017500000000000013154003373015303 5ustar pravipravipeek-performance-bar-1.3.0/app/helpers/0000755000175000017500000000000013154003373016745 5ustar pravipravipeek-performance-bar-1.3.0/app/helpers/peek/0000755000175000017500000000000013154003373017671 5ustar pravipravipeek-performance-bar-1.3.0/app/helpers/peek/performance_bar_helper.rb0000644000175000017500000000045213154003373024703 0ustar pravipravimodule 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/0000755000175000017500000000000013154003373016440 5ustar pravipravipeek-performance-bar-1.3.0/app/views/peek/0000755000175000017500000000000013154003373017364 5ustar pravipravipeek-performance-bar-1.3.0/app/views/peek/views/0000755000175000017500000000000013154003373020521 5ustar pravipravipeek-performance-bar-1.3.0/app/views/peek/views/_performance_bar.html.erb0000644000175000017500000000010413154003373025435 0ustar pravipravi... peek-performance-bar-1.3.0/app/views/peek/results/0000755000175000017500000000000013154003373021065 5ustar pravipravipeek-performance-bar-1.3.0/app/views/peek/results/_performance_bar.html.erb0000644000175000017500000000004313154003373026003 0ustar pravipravi<%= render_server_response_time %> peek-performance-bar-1.3.0/app/assets/0000755000175000017500000000000013154003373016605 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/javascripts/0000755000175000017500000000000013154003373021136 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/javascripts/peek/0000755000175000017500000000000013154003373022062 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/javascripts/peek/views/0000755000175000017500000000000013154003373023217 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/javascripts/peek/views/performance_bar.js0000644000175000017500000001430613154003373026706 0ustar pravipravi// 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/0000755000175000017500000000000013154003373021161 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/stylesheets/peek/0000755000175000017500000000000013154003373022105 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/stylesheets/peek/views/0000755000175000017500000000000013154003373023242 5ustar pravipravipeek-performance-bar-1.3.0/app/assets/stylesheets/peek/views/performance_bar.scss0000644000175000017500000000075013154003373027266 0ustar pravipravi.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/Gemfile0000644000175000017500000000015113154003373016013 0ustar pravipravisource 'https://rubygems.org' # Specify your gem's dependencies in peek-performance_bar.gemspec gemspec