From d39ecf1ca7e9455abcdeb17c251a2d248a47d471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 17 May 2017 13:20:55 +0200 Subject: New performance bar that can be enabled with the `p b` shortcut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- Gemfile | 12 + Gemfile.lock | 45 ++++ app/assets/javascripts/peek.js | 4 + app/assets/javascripts/shortcuts.js | 14 ++ app/controllers/application_controller.rb | 15 +- app/views/help/_shortcuts.html.haml | 4 + app/views/layouts/_head.html.haml | 2 + app/views/layouts/application.html.haml | 1 + config/application.rb | 1 + config/initializers/peek.rb | 9 + config/routes.rb | 1 + config/webpack.config.js | 1 + lib/gitlab/performance_bar.rb | 7 + vendor/assets/javascripts/jquery.tipsy.js | 258 ++++++++++++++++++++++ vendor/assets/javascripts/peek.js | 84 +++++++ vendor/assets/javascripts/peek.performance_bar.js | 191 ++++++++++++++++ vendor/assets/javascripts/peek.rblineprof.js | 5 + vendor/assets/stylesheets/peek.scss | 138 ++++++++++++ 18 files changed, 791 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/peek.js create mode 100644 config/initializers/peek.rb create mode 100644 lib/gitlab/performance_bar.rb create mode 100644 vendor/assets/javascripts/jquery.tipsy.js create mode 100644 vendor/assets/javascripts/peek.js create mode 100644 vendor/assets/javascripts/peek.performance_bar.js create mode 100644 vendor/assets/javascripts/peek.rblineprof.js create mode 100644 vendor/assets/stylesheets/peek.scss diff --git a/Gemfile b/Gemfile index 715ce2bc6c2..7a5b32a447d 100644 --- a/Gemfile +++ b/Gemfile @@ -264,6 +264,18 @@ gem 'gettext_i18n_rails', '~> 1.8.0' gem 'gettext_i18n_rails_js', '~> 1.2.0' gem 'gettext', '~> 3.2.2', require: false, group: :development +# Perf bar +gem 'peek', '~> 1.0.1' +gem 'peek-gc', '~> 0.0.2' +gem 'peek-host', '~> 1.0.0' +gem 'peek-mysql2', '~> 1.1.0', group: :mysql +gem 'peek-performance_bar', '~> 1.2.1' +gem 'peek-pg', '~> 1.3.0' +gem 'peek-rblineprof', '~> 0.2.0' +gem 'pygments.rb', require: false +gem 'peek-redis', '~> 1.2.0' +gem 'peek-sidekiq', '~> 1.0.3' + # Metrics group :metrics do gem 'allocations', '~> 1.0', require: false, platform: :mri diff --git a/Gemfile.lock b/Gemfile.lock index d34b84df5e6..4c5c3db40c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,6 +56,7 @@ GEM asciidoctor-plantuml (0.0.7) asciidoctor (~> 1.5) ast (2.3.0) + atomic (1.1.99) attr_encrypted (3.0.3) encryptor (~> 3.0.0) attr_required (1.0.0) @@ -131,6 +132,8 @@ GEM coffee-script-source (1.10.0) colorize (0.7.7) concurrent-ruby (1.0.5) + concurrent-ruby-ext (1.0.5) + concurrent-ruby (= 1.0.5) connection_pool (2.2.1) crack (0.4.3) safe_yaml (~> 1.0.0) @@ -548,6 +551,36 @@ GEM parser (2.4.0.0) ast (~> 2.2) path_expander (1.0.1) + peek (1.0.1) + concurrent-ruby (>= 0.9.0) + concurrent-ruby-ext (>= 0.9.0) + railties (>= 4.0.0) + peek-gc (0.0.2) + peek + peek-host (1.0.0) + peek + peek-mysql2 (1.1.0) + atomic (>= 1.0.0) + mysql2 + peek + peek-performance_bar (1.2.1) + peek (>= 0.1.0) + peek-pg (1.3.0) + concurrent-ruby + concurrent-ruby-ext + peek + pg + peek-rblineprof (0.2.0) + peek + rblineprof + peek-redis (1.2.0) + atomic (>= 1.0.0) + peek + redis + peek-sidekiq (1.0.3) + atomic (>= 1.0.0) + peek + sidekiq pg (0.18.4) po_to_json (1.0.1) json (>= 1.6.0) @@ -575,6 +608,8 @@ GEM pry (~> 0.10) pry-rails (0.3.5) pry (>= 0.9.10) + pygments.rb (1.1.2) + multi_json (>= 1.0.0) pyu-ruby-sasl (0.0.3.3) rack (1.6.5) rack-accept (0.4.5) @@ -999,12 +1034,22 @@ DEPENDENCIES omniauth_crowd (~> 2.2.0) org-ruby (~> 0.9.12) paranoia (~> 2.2) + peek (~> 1.0.1) + peek-gc (~> 0.0.2) + peek-host (~> 1.0.0) + peek-mysql2 (~> 1.1.0) + peek-performance_bar (~> 1.2.1) + peek-pg (~> 1.3.0) + peek-rblineprof (~> 0.2.0) + peek-redis (~> 1.2.0) + peek-sidekiq (~> 1.0.3) pg (~> 0.18.2) poltergeist (~> 1.9.0) premailer-rails (~> 1.9.0) prometheus-client-mmap (~> 0.7.0.beta5) pry-byebug (~> 3.4.1) pry-rails (~> 0.3.4) + pygments.rb rack-attack (~> 4.4.1) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js new file mode 100644 index 00000000000..4ba23ea1a09 --- /dev/null +++ b/app/assets/javascripts/peek.js @@ -0,0 +1,4 @@ +import 'vendor/jquery.tipsy'; +import 'vendor/peek'; +import 'vendor/peek.performance_bar'; +import 'vendor/peek.rblineprof'; diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index 8ac71797c14..2c7698eb174 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -1,6 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-else-return, comma-dangle, max-len */ /* global Mousetrap */ /* global findFileURL */ +import Cookies from 'js-cookie'; + import findAndFollowLink from './shortcuts_dashboard_navigation'; (function() { @@ -14,6 +16,7 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; Mousetrap.bind('?', this.onToggleHelp); Mousetrap.bind('s', Shortcuts.focusSearch); Mousetrap.bind('f', (e => this.focusFilter(e))); + Mousetrap.bind('p b', this.onTogglePerfBar); const $globalDropdownMenu = $('.global-dropdown-menu'); const $globalDropdownToggle = $('.global-dropdown-toggle'); @@ -53,6 +56,17 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; return Shortcuts.toggleHelp(this.enabledHelp); }; + Shortcuts.prototype.onTogglePerfBar = function(e) { + e.preventDefault(); + if (Cookies.get('perf_bar_enabled') === 'true') { + Cookies.remove('perf_bar_enabled', { path: '/' }); + } + else { + Cookies.set('perf_bar_enabled', true, { path: '/' }); + } + return gl.utils.refreshCurrentPage(); + }; + Shortcuts.prototype.toggleMarkdownPreview = function(e) { // Check if short-cut was triggered while in Write Mode const $target = $(e.target); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 47ce21d238b..01b1462d5ec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,7 +18,7 @@ class ApplicationController < ActionController::Base before_action :ldap_security_check before_action :sentry_context before_action :default_headers - before_action :add_gon_variables + before_action :add_gon_variables, unless: -> { request.path.start_with?('/peek') } before_action :configure_permitted_parameters, if: :devise_controller? before_action :require_email, unless: :devise_controller? @@ -63,6 +63,19 @@ class ApplicationController < ActionController::Base end end + def peek_enabled? + return false unless Gitlab::PerformanceBar.enabled? + return false unless current_user + + if RequestStore.active? + if RequestStore.store.key?(:peek_enabled) + RequestStore.store[:peek_enabled] + else + RequestStore.store[:peek_enabled] = cookies[:perf_bar_enabled].present? + end + end + end + protected # This filter handles both private tokens and personal access tokens diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index ea8bbe92d86..42017228908 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -27,6 +27,10 @@ %td.shortcut .key f %td Focus Filter + %tr + %td.shortcut + .key p b + %td Enable the Performance Bar %tr %td.shortcut .key ? diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 1ef0d524dbb..eea33b5966f 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -28,6 +28,7 @@ = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "test", media: "all" if Rails.env.test? + = stylesheet_link_tag 'peek' if peek_enabled? = Gon::Base.render_data @@ -37,6 +38,7 @@ = webpack_bundle_tag "main" = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "test" if Rails.env.test? + = webpack_bundle_tag 'peek' if peek_enabled? - if content_for?(:page_specific_javascripts) = yield :page_specific_javascripts diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 03688e9ff21..2b07273a0a8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -3,6 +3,7 @@ = render "layouts/head" %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } = render "layouts/init_auto_complete" if @gfm_form + = render 'peek/bar' = render "layouts/header/default", title: header_title = render 'layouts/page', sidebar: sidebar, nav: nav diff --git a/config/application.rb b/config/application.rb index b0533759252..8bbecf3ed0f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -105,6 +105,7 @@ module Gitlab config.assets.precompile << "katex.css" config.assets.precompile << "katex.js" config.assets.precompile << "xterm/xterm.css" + config.assets.precompile << "peek.css" config.assets.precompile << "lib/ace.js" config.assets.precompile << "vendor/assets/fonts/*" config.assets.precompile << "test.css" diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb new file mode 100644 index 00000000000..73da7be7889 --- /dev/null +++ b/config/initializers/peek.rb @@ -0,0 +1,9 @@ +Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Redis.params) } + +Peek.into Peek::Views::Host +Peek.into Peek::Views::PerformanceBar +Peek.into Gitlab::Database.mysql? ? Peek::Views::Mysql2 : Peek::Views::PG +Peek.into Peek::Views::Redis +Peek.into Peek::Views::Sidekiq +Peek.into Peek::Views::Rblineprof +Peek.into Peek::Views::GC diff --git a/config/routes.rb b/config/routes.rb index d909be38b42..9a117711083 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,7 @@ Rails.application.routes.draw do post :toggle_award_emoji, on: :member end + mount Peek::Railtie => '/peek' draw :sherlock draw :development draw :ci diff --git a/config/webpack.config.js b/config/webpack.config.js index 7501acb7633..bb77c12f88a 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -68,6 +68,7 @@ var config = { raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', + peek: './peek.js', }, output: { diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb new file mode 100644 index 00000000000..3324fec94d4 --- /dev/null +++ b/lib/gitlab/performance_bar.rb @@ -0,0 +1,7 @@ +module Gitlab + module PerformanceBar + def self.enabled? + ENV["PERFORMANCE_BAR"] == '1' + end + end +end diff --git a/vendor/assets/javascripts/jquery.tipsy.js b/vendor/assets/javascripts/jquery.tipsy.js new file mode 100644 index 00000000000..d9fced24b60 --- /dev/null +++ b/vendor/assets/javascripts/jquery.tipsy.js @@ -0,0 +1,258 @@ +// tipsy, facebook style tooltips for jquery +// version 1.0.0a +// (c) 2008-2010 jason frame [jason@onehackoranother.com] +// released under the MIT license + +(function($) { + + function maybeCall(thing, ctx) { + return (typeof thing == 'function') ? (thing.call(ctx)) : thing; + }; + + function isElementInDOM(ele) { + while (ele = ele.parentNode) { + if (ele == document) return true; + } + return false; + }; + + function Tipsy(element, options) { + this.$element = $(element); + this.options = options; + this.enabled = true; + this.fixTitle(); + }; + + Tipsy.prototype = { + show: function() { + var title = this.getTitle(); + if (title && this.enabled) { + var $tip = this.tip(); + + $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); + $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity + $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body); + + var pos = $.extend({}, this.$element.offset(), { + width: this.$element[0].offsetWidth, + height: this.$element[0].offsetHeight + }); + + var actualWidth = $tip[0].offsetWidth, + actualHeight = $tip[0].offsetHeight, + gravity = maybeCall(this.options.gravity, this.$element[0]); + + var tp; + switch (gravity.charAt(0)) { + case 'n': + tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; + break; + case 's': + tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; + break; + case 'e': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; + break; + case 'w': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; + break; + } + + if (gravity.length == 2) { + if (gravity.charAt(1) == 'w') { + tp.left = pos.left + pos.width / 2 - 15; + } else { + tp.left = pos.left + pos.width / 2 - actualWidth + 15; + } + } + + $tip.css(tp).addClass('tipsy-' + gravity); + $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0); + if (this.options.className) { + $tip.addClass(maybeCall(this.options.className, this.$element[0])); + } + + if (this.options.fade) { + $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); + } else { + $tip.css({visibility: 'visible', opacity: this.options.opacity}); + } + } + }, + + hide: function() { + if (this.options.fade) { + this.tip().stop().fadeOut(function() { $(this).remove(); }); + } else { + this.tip().remove(); + } + }, + + fixTitle: function() { + var $e = this.$element; + if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') { + $e.attr('original-title', $e.attr('title') || '').removeAttr('title'); + } + }, + + getTitle: function() { + var title, $e = this.$element, o = this.options; + this.fixTitle(); + var title, o = this.options; + if (typeof o.title == 'string') { + title = $e.attr(o.title == 'title' ? 'original-title' : o.title); + } else if (typeof o.title == 'function') { + title = o.title.call($e[0]); + } + title = ('' + title).replace(/(^\s*|\s*$)/, ""); + return title || o.fallback; + }, + + tip: function() { + if (!this.$tip) { + this.$tip = $('
').html('
'); + this.$tip.data('tipsy-pointee', this.$element[0]); + } + return this.$tip; + }, + + validate: function() { + if (!this.$element[0].parentNode) { + this.hide(); + this.$element = null; + this.options = null; + } + }, + + enable: function() { this.enabled = true; }, + disable: function() { this.enabled = false; }, + toggleEnabled: function() { this.enabled = !this.enabled; } + }; + + $.fn.tipsy = function(options) { + + if (options === true) { + return this.data('tipsy'); + } else if (typeof options == 'string') { + var tipsy = this.data('tipsy'); + if (tipsy) tipsy[options](); + return this; + } + + options = $.extend({}, $.fn.tipsy.defaults, options); + + function get(ele) { + var tipsy = $.data(ele, 'tipsy'); + if (!tipsy) { + tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); + $.data(ele, 'tipsy', tipsy); + } + return tipsy; + } + + function enter() { + var tipsy = get(this); + tipsy.hoverState = 'in'; + if (options.delayIn == 0) { + tipsy.show(); + } else { + tipsy.fixTitle(); + setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); + } + }; + + function leave() { + var tipsy = get(this); + tipsy.hoverState = 'out'; + if (options.delayOut == 0) { + tipsy.hide(); + } else { + setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut); + } + }; + + if (!options.live) this.each(function() { get(this); }); + + if (options.trigger != 'manual') { + var binder = options.live ? 'live' : 'bind', + eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', + eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; + this[binder](eventIn, enter)[binder](eventOut, leave); + } + + return this; + + }; + + $.fn.tipsy.defaults = { + className: null, + delayIn: 0, + delayOut: 0, + fade: false, + fallback: '', + gravity: 'n', + html: false, + live: false, + offset: 0, + opacity: 0.8, + title: 'title', + trigger: 'hover' + }; + + $.fn.tipsy.revalidate = function() { + $('.tipsy').each(function() { + var pointee = $.data(this, 'tipsy-pointee'); + if (!pointee || !isElementInDOM(pointee)) { + $(this).remove(); + } + }); + }; + + // Overwrite this method to provide options on a per-element basis. + // For example, you could store the gravity in a 'tipsy-gravity' attribute: + // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); + // (remember - do not modify 'options' in place!) + $.fn.tipsy.elementOptions = function(ele, options) { + return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; + }; + + $.fn.tipsy.autoNS = function() { + return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; + }; + + $.fn.tipsy.autoWE = function() { + return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; + }; + + /** + * yields a closure of the supplied parameters, producing a function that takes + * no arguments and is suitable for use as an autogravity function like so: + * + * @param margin (int) - distance from the viewable region edge that an + * element should be before setting its tooltip's gravity to be away + * from that edge. + * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer + * if there are no viewable region edges effecting the tooltip's + * gravity. It will try to vary from this minimally, for example, + * if 'sw' is preferred and an element is near the right viewable + * region edge, but not the top edge, it will set the gravity for + * that element's tooltip to be 'se', preserving the southern + * component. + */ + $.fn.tipsy.autoBounds = function(margin, prefer) { + return function() { + var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)}, + boundTop = $(document).scrollTop() + margin, + boundLeft = $(document).scrollLeft() + margin, + $this = $(this); + + if ($this.offset().top < boundTop) dir.ns = 'n'; + if ($this.offset().left < boundLeft) dir.ew = 'w'; + if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e'; + if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's'; + + return dir.ns + (dir.ew ? dir.ew : ''); + } + }; + +})(jQuery); diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js new file mode 100644 index 00000000000..2d5d05ca8e6 --- /dev/null +++ b/vendor/assets/javascripts/peek.js @@ -0,0 +1,84 @@ +var requestId; + +requestId = null; + +(function($) { + var fetchRequestResults, getRequestId, initializeTipsy, peekEnabled, toggleBar, updatePerformanceBar; + getRequestId = function() { + if (requestId != null) { + return requestId; + } else { + return $('#peek').data('request-id'); + } + }; + peekEnabled = function() { + return $('#peek').length; + }; + updatePerformanceBar = function(results) { + var key, label; + for (key in results.data) { + for (label in results.data[key]) { + $("[data-defer-to=" + key + "-" + label + "]").text(results.data[key][label]); + } + } + return $(document).trigger('peek:render', [getRequestId(), results]); + }; + initializeTipsy = function() { + return $('#peek .peek-tooltip, #peek .tooltip').each(function() { + var el, gravity; + el = $(this); + gravity = el.hasClass('rightwards') || el.hasClass('leftwards') ? $.fn.tipsy.autoWE : $.fn.tipsy.autoNS; + return el.tipsy({ + gravity: gravity + }); + }); + }; + toggleBar = function(event) { + var wrapper; + if ($(event.target).is(':input')) { + return; + } + if (event.which === 96 && !event.metaKey) { + wrapper = $('#peek'); + if (wrapper.hasClass('disabled')) { + wrapper.removeClass('disabled'); + return document.cookie = "peek=true; path=/"; + } else { + wrapper.addClass('disabled'); + return document.cookie = "peek=false; path=/"; + } + } + }; + fetchRequestResults = function() { + return $.ajax('/peek/results', { + data: { + request_id: getRequestId() + }, + success: function(data, textStatus, xhr) { + return updatePerformanceBar(data); + }, + error: function(xhr, textStatus, error) {} + }); + }; + $(document).on('keypress', toggleBar); + $(document).on('peek:update', initializeTipsy); + $(document).on('peek:update', fetchRequestResults); + $(document).on('pjax:end', function(event, xhr, options) { + if (xhr != null) { + requestId = xhr.getResponseHeader('X-Request-Id'); + } + if (peekEnabled()) { + return $(this).trigger('peek:update'); + } + }); + $(document).on('page:change turbolinks:load', function() { + if (peekEnabled()) { + return $(this).trigger('peek:update'); + } + }); + return $(function() { + if (peekEnabled()) { + return $(this).trigger('peek:update'); + } + }); +})(jQuery); diff --git a/vendor/assets/javascripts/peek.performance_bar.js b/vendor/assets/javascripts/peek.performance_bar.js new file mode 100644 index 00000000000..3318e218890 --- /dev/null +++ b/vendor/assets/javascripts/peek.performance_bar.js @@ -0,0 +1,191 @@ +var PerformanceBar, ajaxStart, renderPerformanceBar, updateStatus; + +PerformanceBar = (function() { + PerformanceBar.prototype.appInfo = null; + + PerformanceBar.prototype.width = null; + + PerformanceBar.formatTime = function(value) { + if (value >= 1000) { + return ((value / 1000).toFixed(3)) + "s"; + } else { + return (value.toFixed(0)) + "ms"; + } + }; + + function PerformanceBar(options) { + var k, v; + if (options == null) { + options = {}; + } + this.el = $('#peek-view-performance-bar .performance-bar'); + for (k in options) { + v = options[k]; + this[k] = v; + } + if (this.width == null) { + this.width = this.el.width(); + } + if (this.timing == null) { + this.timing = window.performance.timing; + } + } + + PerformanceBar.prototype.render = function(serverTime) { + var networkTime, perfNetworkTime; + if (serverTime == null) { + serverTime = 0; + } + this.el.empty(); + this.addBar('frontend', '#90d35b', 'domLoading', 'domInteractive'); + perfNetworkTime = this.timing.responseEnd - this.timing.requestStart; + if (serverTime && serverTime <= perfNetworkTime) { + 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; + }; + + PerformanceBar.prototype.isLoaded = function() { + return this.timing.domInteractive; + }; + + PerformanceBar.prototype.start = function() { + return this.timing.navigationStart; + }; + + PerformanceBar.prototype.end = function() { + return this.timing.domInteractive; + }; + + PerformanceBar.prototype.total = function() { + return this.end() - this.start(); + }; + + PerformanceBar.prototype.addBar = function(name, color, start, end, info) { + var bar, left, offset, time, title, width; + if (typeof start === 'string') { + start = this.timing[start]; + } + if (typeof end === 'string') { + end = this.timing[end]; + } + if (!((start != null) && (end != null))) { + return; + } + time = end - start; + offset = start - this.start(); + left = this.mapH(offset); + width = this.mapH(time); + title = name + ": " + (PerformanceBar.formatTime(time)); + bar = $('
  • ', { + title: 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); + }; + + PerformanceBar.prototype.mapH = function(offset) { + return offset * (this.width / this.total()); + }; + + return PerformanceBar; + +})(); + +renderPerformanceBar = function() { + var bar, resp, span, time; + resp = $('#peek-server_response_time'); + time = Math.round(resp.data('time') * 1000); + bar = new PerformanceBar; + bar.render(time); + 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); +}; + +updateStatus = function(html) { + return $('#serverstats').html(html); +}; + +ajaxStart = null; + +$(document).on('pjax:start page:fetch turbolinks:request-start', function(event) { + return ajaxStart = event.timeStamp; +}); + +$(document).on('pjax:end page:load turbolinks:load', function(event, xhr) { + var ajaxEnd, serverTime, total; + if (ajaxStart == null) { + return; + } + ajaxEnd = event.timeStamp; + total = ajaxEnd - ajaxStart; + serverTime = xhr ? parseInt(xhr.getResponseHeader('X-Runtime')) : 0; + return setTimeout(function() { + var bar, now, span, tech; + now = new Date().getTime(); + bar = new PerformanceBar({ + timing: { + requestStart: ajaxStart, + responseEnd: ajaxEnd, + domLoading: ajaxEnd, + domInteractive: now + }, + isLoaded: function() { + return true; + }, + start: function() { + return ajaxStart; + }, + end: function() { + return now; + } + }); + bar.render(serverTime); + if ($.fn.pjax != null) { + tech = 'PJAX'; + } else { + tech = 'Turbolinks'; + } + 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(); + } +}); + +// --- +// generated by coffee-script 1.9.2 diff --git a/vendor/assets/javascripts/peek.rblineprof.js b/vendor/assets/javascripts/peek.rblineprof.js new file mode 100644 index 00000000000..cad6e24d40e --- /dev/null +++ b/vendor/assets/javascripts/peek.rblineprof.js @@ -0,0 +1,5 @@ +$(document).on('click', '.js-lineprof-file', function(e) { + $(this).parents('.heading').next('div').toggle(); + e.preventDefault(); + return false; +}); diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss new file mode 100644 index 00000000000..4b2957c5575 --- /dev/null +++ b/vendor/assets/stylesheets/peek.scss @@ -0,0 +1,138 @@ +//= require peek/views/performance_bar +//= require peek/views/rblineprof +//= require peek/views/rblineprof/pygments + +#peek { + background: #000; + height: 35px; + line-height: 35px; + color: #999; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); + + .hidden { + display: none; + visibility: visible; + } + + &.disabled { + display: none; + } + + &.production { + background-color: #222; + } + + &.staging { + background-color: #291430; + } + + &.development { + background-color: #4c1210; + } + + .wrapper { + width: 800px; + margin: 0 auto; + } + + // UI Elements + .bucket { + background: #111; + display: inline-block; + padding: 4px 6px; + font-family: Consolas, "Liberation Mono", Courier, monospace; + line-height: 1; + color: #ccc; + border-radius: 3px; + box-shadow: 0 1px 0 rgba(255,255,255,.2), inset 0 1px 2px rgba(0,0,0,.25); + + .hidden { + display: none; + } + + &:hover .hidden { + display: inline; + } + } + + strong { + color: #fff; + } + + .view { + margin-right: 15px; + float: left; + + &:last-child { + margin-right: 0; + } + } + + .css-truncate { + &.css-truncate-target, + .css-truncate-target { + display: inline-block; + max-width: 125px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: top; + } + + &.expandable:hover .css-truncate-target, + &.expandable:hover.css-truncate-target { + max-width: 10000px !important; + } + } +} + +// .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; +// } +// } +// } + +.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; } + .tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; } + + /* Rounded corners */ + .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; } + + .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; } + + /* Rules to colour arrows */ + .tipsy-arrow-n { border-bottom-color: #000; } + .tipsy-arrow-s { border-top-color: #000; } + .tipsy-arrow-e { border-left-color: #000; } + .tipsy-arrow-w { border-right-color: #000; } + + .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; } + .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} + .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} + .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } + .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } + .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } + .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; } + .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; } -- cgit v1.2.1 From 46273e14465f98ac1db306122cdf06d3d87b319e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 1 Jun 2017 16:10:17 +0200 Subject: Store Sherlock::Query in Peek adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- config/initializers/peek.rb | 32 +++++++++++++++++++ .../performance_bar/peek_mysql_with_queries.rb | 36 ++++++++++++++++++++++ lib/gitlab/performance_bar/peek_pg_with_queries.rb | 36 ++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 lib/gitlab/performance_bar/peek_mysql_with_queries.rb create mode 100644 lib/gitlab/performance_bar/peek_pg_with_queries.rb diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 73da7be7889..6cb0e711e48 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -7,3 +7,35 @@ Peek.into Peek::Views::Redis Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::GC + +if Gitlab::Database.mysql? + class Mysql2::Client + class << self + attr_accessor :query_details + end + self.query_details = Concurrent::Array.new + end + + module Peek + module Views + class Mysql2 < View + prepend ::Gitlab::PerformanceBar::PeekMysqlWithQueries + end + end + end +else + class PG::Connection + class << self + attr_accessor :query_details + end + self.query_details = Concurrent::Array.new + end + + module Peek + module Views + class PG < View + prepend ::Gitlab::PerformanceBar::PeekPgWithQueries + end + end + end +end diff --git a/lib/gitlab/performance_bar/peek_mysql_with_queries.rb b/lib/gitlab/performance_bar/peek_mysql_with_queries.rb new file mode 100644 index 00000000000..ea577de5a69 --- /dev/null +++ b/lib/gitlab/performance_bar/peek_mysql_with_queries.rb @@ -0,0 +1,36 @@ +# Inspired by https://github.com/peek/peek-mysql2/blob/master/lib/peek/views/mysql2.rb +module Gitlab + module PerformanceBar + module PeekMysqlWithQueries + def queries + ::Mysql2::Client.query_details + end + + def results + super.merge(queries: queries) + end + + private + + def setup_subscribers + super + + # Reset each counter when a new request starts + before_request do + ::Mysql2::Client.query_details = [] + end + + subscribe('sql.active_record') do |_, start, finish, _, data| + if RequestStore.active? && RequestStore.store[:peek_enabled] + track_query(data[:sql].strip, data[:binds], start, finish) + end + end + end + + def track_query(raw_query, bindings, start, finish) + query = Gitlab::Sherlock::Query.new(raw_query, start, finish) + ::Mysql2::Client.query_details << query.formatted_query + end + end + end +end diff --git a/lib/gitlab/performance_bar/peek_pg_with_queries.rb b/lib/gitlab/performance_bar/peek_pg_with_queries.rb new file mode 100644 index 00000000000..6e9cb593d36 --- /dev/null +++ b/lib/gitlab/performance_bar/peek_pg_with_queries.rb @@ -0,0 +1,36 @@ +# Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb +module Gitlab + module PerformanceBar + module PeekPgWithQueries + def queries + ::PG::Connection.query_details + end + + def results + super.merge(queries: queries) + end + + private + + def setup_subscribers + super + + # Reset each counter when a new request starts + before_request do + ::PG::Connection.query_details = [] + end + + subscribe('sql.active_record') do |_, start, finish, _, data| + if RequestStore.active? && RequestStore.store[:peek_enabled] + track_query(data[:sql].strip, data[:binds], start, finish) + end + end + end + + def track_query(raw_query, bindings, start, finish) + query = Gitlab::Sherlock::Query.new(raw_query, start, finish) + ::PG::Connection.query_details << query.formatted_query + end + end + end +end -- cgit v1.2.1 From 406b351137f73e4d454b76f29b45376acc3c2694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 6 Jun 2017 21:24:08 +0200 Subject: Tweak Peek to display SQL queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 12 ++++++++++++ app/views/peek/views/_pg.html.haml | 15 +++++++++++++++ vendor/assets/javascripts/peek.js | 23 +++++++++++++++++++++-- vendor/assets/stylesheets/peek.scss | 32 ++++---------------------------- 4 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 app/views/peek/views/_pg.html.haml diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index 4ba23ea1a09..6af0242246f 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -2,3 +2,15 @@ import 'vendor/jquery.tipsy'; import 'vendor/peek'; import 'vendor/peek.performance_bar'; import 'vendor/peek.rblineprof'; + +(function() { + $(document).on('click', '#peek-show-queries', function(e) { + console.log('peek!'); + var $modal; + $modal = $('#modal-peek-pg-queries'); + if ($modal.length) { + $modal.modal('toggle'); + } + return e.preventDefault(); + }); +}).call(window); diff --git a/app/views/peek/views/_pg.html.haml b/app/views/peek/views/_pg.html.haml new file mode 100644 index 00000000000..e789e0c5cb5 --- /dev/null +++ b/app/views/peek/views/_pg.html.haml @@ -0,0 +1,15 @@ +%strong + %a#peek-show-queries{ href: '#' } + %span{ data: { defer_to: "#{view.defer_key}-duration" }}... + / + %span{ data: { defer_to: "#{view.defer_key}-calls" }}... + / + #modal-peek-pg-queries.modal{ tabindex: -1 } + .modal-dialog + #modal-peek-pg-queries-content.modal-content + .modal-header + %a.close{ href: "#", "data-dismiss" => "modal" } × + %h4 + SQL queries + .modal-body{ data: { defer_to: "#{view.defer_key}-queries" }}... +pg diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 2d5d05ca8e6..6a4da020b9d 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -15,10 +15,29 @@ requestId = null; return $('#peek').length; }; updatePerformanceBar = function(results) { - var key, label; + var key, label, data, table, html, tr, td; for (key in results.data) { for (label in results.data[key]) { - $("[data-defer-to=" + key + "-" + label + "]").text(results.data[key][label]); + data = results.data[key][label]; + console.log(data); + if (Array.isArray(data)) { + table = document.createElement('table'); + + for (var i = 0; i < data.length; i += 1) { + tr = document.createElement('tr'); + td = document.createElement('td'); + + td.appendChild(document.createTextNode(data[i])); + tr.appendChild(td); + table.appendChild(tr); + } + + $table = $(table).addClass('table'); + $("[data-defer-to=" + key + "-" + label + "]").html($table); + } + else { + $("[data-defer-to=" + key + "-" + label + "]").text(results.data[key][label]); + } } } return $(document).trigger('peek:render', [getRequestId(), results]); diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 4b2957c5575..0ceeb5b9241 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -7,7 +7,6 @@ height: 35px; line-height: 35px; color: #999; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); .hidden { display: none; @@ -86,33 +85,10 @@ } } -// .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; -// } -// } -// } +#modal-peek-pg-queries-content { + // background: #999; + color: #000; +} .tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; } .tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; } -- cgit v1.2.1 From 47054451da0a3aa80cae02c8a096338c23be7b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 09:57:21 +0200 Subject: Don't use Pygment,rb, use Rouge instead, and put peek-pg in the :postgres group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- Gemfile | 3 +- Gemfile.lock | 3 - app/controllers/application_controller.rb | 1 + config/initializers/peek.rb | 8 ++- lib/peek/rblineprof/custom_controller_helpers.rb | 91 ++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 lib/peek/rblineprof/custom_controller_helpers.rb diff --git a/Gemfile b/Gemfile index 7a5b32a447d..c78c1f2fc4c 100644 --- a/Gemfile +++ b/Gemfile @@ -270,9 +270,8 @@ gem 'peek-gc', '~> 0.0.2' gem 'peek-host', '~> 1.0.0' gem 'peek-mysql2', '~> 1.1.0', group: :mysql gem 'peek-performance_bar', '~> 1.2.1' -gem 'peek-pg', '~> 1.3.0' +gem 'peek-pg', '~> 1.3.0', group: :postgres gem 'peek-rblineprof', '~> 0.2.0' -gem 'pygments.rb', require: false gem 'peek-redis', '~> 1.2.0' gem 'peek-sidekiq', '~> 1.0.3' diff --git a/Gemfile.lock b/Gemfile.lock index 4c5c3db40c9..676cd977e37 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -608,8 +608,6 @@ GEM pry (~> 0.10) pry-rails (0.3.5) pry (>= 0.9.10) - pygments.rb (1.1.2) - multi_json (>= 1.0.0) pyu-ruby-sasl (0.0.3.3) rack (1.6.5) rack-accept (0.4.5) @@ -1049,7 +1047,6 @@ DEPENDENCIES prometheus-client-mmap (~> 0.7.0.beta5) pry-byebug (~> 3.4.1) pry-rails (~> 0.3.4) - pygments.rb rack-attack (~> 4.4.1) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 01b1462d5ec..d52fec19a82 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base include SentryHelper include WorkhorseHelper include EnforcesTwoFactorAuthentication + include Peek::Rblineprof::CustomControllerHelpers before_action :authenticate_user_from_private_token! before_action :authenticate_user_from_rss_token! diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 6cb0e711e48..a8669ddba97 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -2,7 +2,13 @@ Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Re Peek.into Peek::Views::Host Peek.into Peek::Views::PerformanceBar -Peek.into Gitlab::Database.mysql? ? Peek::Views::Mysql2 : Peek::Views::PG +if Gitlab::Database.mysql? + require 'peek-mysql' + Peek.into Peek::Views::Mysql2 +else + require 'peek-pg' + Peek.into Peek::Views::PG +end Peek.into Peek::Views::Redis Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb new file mode 100644 index 00000000000..4ea4ba45e38 --- /dev/null +++ b/lib/peek/rblineprof/custom_controller_helpers.rb @@ -0,0 +1,91 @@ +module Peek + module Rblineprof + module CustomControllerHelpers + extend ActiveSupport::Concern + + def pygmentize(file_name, code, lexer = nil) + if lexer.present? + Gitlab::Highlight.highlight(file_name, code) + else + "
    #{Rack::Utils.escape_html(code)}
    " + end + end + + def inject_rblineprof + ret = nil + profile = lineprof(rblineprof_profiler_regex) do + ret = yield + end + + if response.content_type =~ %r|text/html| + sort = params[:lineprofiler_sort] + mode = params[:lineprofiler_mode] || 'cpu' + min = (params[:lineprofiler_min] || 5).to_i * 1000 + summary = params[:lineprofiler_summary] + + # Sort each file by the longest calculated time + per_file = profile.map do |file, lines| + total, child, excl, total_cpu, child_cpu, excl_cpu = lines[0] + + wall = summary == 'exclusive' ? excl : total + cpu = summary == 'exclusive' ? excl_cpu : total_cpu + idle = summary == 'exclusive' ? (excl - excl_cpu) : (total - total_cpu) + + [ + file, lines, + wall, cpu, idle, + sort == 'idle' ? idle : sort == 'cpu' ? cpu : wall + ] + end.sort_by{ |a,b,c,d,e,f| -f } + + output = '' + per_file.each do |file_name, lines, file_wall, file_cpu, file_idle, file_sort| + + output << "
    " + + show_src = file_sort > min + tmpl = show_src ? "%s" : "%s" + + if mode == 'cpu' + output << sprintf("% 8.1fms + % 8.1fms #{tmpl}", file_cpu / 1000.0, file_idle / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) + else + output << sprintf("% 8.1fms #{tmpl}", file_wall/1000.0, file_name.sub(Rails.root.to_s + '/', '')) + end + + output << "
    " # .heading + + next unless show_src + + output << "
    " + code = [] + times = [] + File.readlines(file_name).each_with_index do |line, i| + code << line + wall, cpu, calls = lines[i + 1] + + if calls && calls > 0 + if mode == 'cpu' + idle = wall - cpu + times << sprintf("% 8.1fms + % 8.1fms (% 5d)", cpu / 1000.0, idle / 1000.0, calls) + else + times << sprintf("% 8.1fms (% 5d)", wall / 1000.0, calls) + end + else + times << ' ' + end + end + output << "
    #{times.join("\n")}
    " + # The following line was changed from + # https://github.com/peek/peek-rblineprof/blob/8d3b7a283a27de2f40abda45974516693d882258/lib/peek/rblineprof/controller_helpers.rb#L125 + output << "
    #{pygmentize(file_name, code.join, 'ruby')}
    " + output << "
    " # .data then .peek-rblineprof-file + end + + response.body += "
    #{output}
    ".html_safe + end + + ret + end + end + end +end -- cgit v1.2.1 From da3ad00cf15aa5d3c3724b412d56a0cba3a270b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 16:49:36 +0200 Subject: Replace Tipsy by Bootstrap's tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 1 - vendor/assets/javascripts/peek.js | 13 +---------- vendor/assets/javascripts/peek.performance_bar.js | 27 ++++++++--------------- vendor/assets/stylesheets/peek.scss | 24 -------------------- 4 files changed, 10 insertions(+), 55 deletions(-) diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index 6af0242246f..33ad9024591 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -1,4 +1,3 @@ -import 'vendor/jquery.tipsy'; import 'vendor/peek'; import 'vendor/peek.performance_bar'; import 'vendor/peek.rblineprof'; diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 6a4da020b9d..51d797ffd12 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -3,7 +3,7 @@ var requestId; requestId = null; (function($) { - var fetchRequestResults, getRequestId, initializeTipsy, peekEnabled, toggleBar, updatePerformanceBar; + var fetchRequestResults, getRequestId, peekEnabled, toggleBar, updatePerformanceBar; getRequestId = function() { if (requestId != null) { return requestId; @@ -42,16 +42,6 @@ requestId = null; } return $(document).trigger('peek:render', [getRequestId(), results]); }; - initializeTipsy = function() { - return $('#peek .peek-tooltip, #peek .tooltip').each(function() { - var el, gravity; - el = $(this); - gravity = el.hasClass('rightwards') || el.hasClass('leftwards') ? $.fn.tipsy.autoWE : $.fn.tipsy.autoNS; - return el.tipsy({ - gravity: gravity - }); - }); - }; toggleBar = function(event) { var wrapper; if ($(event.target).is(':input')) { @@ -80,7 +70,6 @@ requestId = null; }); }; $(document).on('keypress', toggleBar); - $(document).on('peek:update', initializeTipsy); $(document).on('peek:update', fetchRequestResults); $(document).on('pjax:end', function(event, xhr, options) { if (xhr != null) { diff --git a/vendor/assets/javascripts/peek.performance_bar.js b/vendor/assets/javascripts/peek.performance_bar.js index 3318e218890..6ed86dce2f2 100644 --- a/vendor/assets/javascripts/peek.performance_bar.js +++ b/vendor/assets/javascripts/peek.performance_bar.js @@ -85,17 +85,15 @@ PerformanceBar = (function() { width = this.mapH(time); title = name + ": " + (PerformanceBar.formatTime(time)); bar = $('
  • ', { - title: title, - "class": 'peek-tooltip' + 'data-title': title, + 'data-toggle': 'tooltip', + 'data-container': 'body' }); bar.css({ width: width + "px", left: left + "px", background: color }); - bar.tipsy({ - gravity: $.fn.tipsy.autoNS - }); return this.el.append(bar); }; @@ -114,12 +112,10 @@ renderPerformanceBar = function() { bar = new PerformanceBar; bar.render(time); span = $('', { - 'class': 'peek-tooltip', - title: 'Total navigation time for this page.' + 'data-toggle': 'tooltip', + 'data-title': 'Total navigation time for this page.', + 'data-container': 'body' }).text(PerformanceBar.formatTime(bar.total())); - span.tipsy({ - gravity: $.fn.tipsy.autoNS - }); return updateStatus(span); }; @@ -168,12 +164,10 @@ $(document).on('pjax:end page:load turbolinks:load', function(event, xhr) { tech = 'Turbolinks'; } span = $('', { - 'class': 'peek-tooltip', - title: tech + " navigation time" + 'data-toggle': 'tooltip', + 'data-title': tech + " navigation time", + 'data-container': 'body' }).text(PerformanceBar.formatTime(total)); - span.tipsy({ - gravity: $.fn.tipsy.autoNS - }); updateStatus(span); return ajaxStart = null; }, 0); @@ -186,6 +180,3 @@ $(function() { return $('#peek-view-performance-bar').remove(); } }); - -// --- -// generated by coffee-script 1.9.2 diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 0ceeb5b9241..344cf656bab 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -86,29 +86,5 @@ } #modal-peek-pg-queries-content { - // background: #999; color: #000; } - -.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; } - .tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; } - - /* Rounded corners */ - .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; } - - .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; } - - /* Rules to colour arrows */ - .tipsy-arrow-n { border-bottom-color: #000; } - .tipsy-arrow-s { border-top-color: #000; } - .tipsy-arrow-e { border-left-color: #000; } - .tipsy-arrow-w { border-right-color: #000; } - - .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; } - .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} - .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} - .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } - .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } - .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } - .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; } - .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; } -- cgit v1.2.1 From d16a3cf7fc350023da65a5080214765ade2e1aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 16:51:15 +0200 Subject: Fix replacement of Pygments with Rouge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 8 ++++++-- lib/peek/rblineprof/custom_controller_helpers.rb | 2 +- vendor/assets/javascripts/peek.rblineprof.js | 5 ----- vendor/assets/stylesheets/peek.scss | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 vendor/assets/javascripts/peek.rblineprof.js diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index 33ad9024591..06a07c19e09 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -1,15 +1,19 @@ import 'vendor/peek'; import 'vendor/peek.performance_bar'; -import 'vendor/peek.rblineprof'; (function() { $(document).on('click', '#peek-show-queries', function(e) { - console.log('peek!'); var $modal; + $('.peek-rblineprof-modal').hide(); $modal = $('#modal-peek-pg-queries'); if ($modal.length) { $modal.modal('toggle'); } return e.preventDefault(); }); + + $(document).on('click', '.js-lineprof-file', function(e) { + $(this).parents('.heading').next('div').toggle(); + return e.preventDefault(); + }); }).call(window); diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb index 4ea4ba45e38..796420971b5 100644 --- a/lib/peek/rblineprof/custom_controller_helpers.rb +++ b/lib/peek/rblineprof/custom_controller_helpers.rb @@ -77,7 +77,7 @@ module Peek output << "
    #{times.join("\n")}
    " # The following line was changed from # https://github.com/peek/peek-rblineprof/blob/8d3b7a283a27de2f40abda45974516693d882258/lib/peek/rblineprof/controller_helpers.rb#L125 - output << "
    #{pygmentize(file_name, code.join, 'ruby')}
    " + output << "
    #{pygmentize(file_name, code.join, 'ruby')}
    " output << "" # .data then .peek-rblineprof-file end diff --git a/vendor/assets/javascripts/peek.rblineprof.js b/vendor/assets/javascripts/peek.rblineprof.js deleted file mode 100644 index cad6e24d40e..00000000000 --- a/vendor/assets/javascripts/peek.rblineprof.js +++ /dev/null @@ -1,5 +0,0 @@ -$(document).on('click', '.js-lineprof-file', function(e) { - $(this).parents('.heading').next('div').toggle(); - e.preventDefault(); - return false; -}); diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 344cf656bab..b4b4f6d158f 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -1,6 +1,5 @@ //= require peek/views/performance_bar //= require peek/views/rblineprof -//= require peek/views/rblineprof/pygments #peek { background: #000; -- cgit v1.2.1 From 5f218eb5c3ceabb42c42109bb7fe7427bd72e12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 16:52:13 +0200 Subject: Improve UI of the performance bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/helpers/nav_helper.rb | 1 + app/views/peek/views/_pg.html.haml | 19 +++++++++---------- vendor/assets/stylesheets/peek.scss | 10 ++++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 88dfe78c90c..833d3c36b28 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -27,6 +27,7 @@ module NavHelper def nav_header_class class_name = '' class_name << " with-horizontal-nav" if defined?(nav) && nav + class_name << " with-peek" if peek_enabled? class_name end diff --git a/app/views/peek/views/_pg.html.haml b/app/views/peek/views/_pg.html.haml index e789e0c5cb5..7a1901eb7d2 100644 --- a/app/views/peek/views/_pg.html.haml +++ b/app/views/peek/views/_pg.html.haml @@ -1,15 +1,14 @@ %strong %a#peek-show-queries{ href: '#' } %span{ data: { defer_to: "#{view.defer_key}-duration" }}... - / + \/ %span{ data: { defer_to: "#{view.defer_key}-calls" }}... - / - #modal-peek-pg-queries.modal{ tabindex: -1 } - .modal-dialog - #modal-peek-pg-queries-content.modal-content - .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × - %h4 - SQL queries - .modal-body{ data: { defer_to: "#{view.defer_key}-queries" }}... +#modal-peek-pg-queries.modal{ tabindex: -1 } + .modal-dialog + #modal-peek-pg-queries-content.modal-content + .modal-header + %a.close{ href: "#", "data-dismiss" => "modal" } × + %h4 + SQL queries + .modal-body{ data: { defer_to: "#{view.defer_key}-queries" }}... pg diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index b4b4f6d158f..224ecc4245e 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -1,6 +1,10 @@ //= require peek/views/performance_bar //= require peek/views/rblineprof +header.navbar-gitlab.with-peek { + top: 35px; +} + #peek { background: #000; height: 35px; @@ -57,6 +61,12 @@ color: #fff; } + table { + strong { + color: #000; + } + } + .view { margin-right: 15px; float: left; -- cgit v1.2.1 From 651eb51ececf9c256416eabe87c062ef33f45502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 16:53:13 +0200 Subject: Display queries duration in performance bar queries modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- config/initializers/peek.rb | 42 +++++++--------------- .../performance_bar/peek_mysql_with_queries.rb | 36 ------------------- lib/gitlab/performance_bar/peek_pg_with_queries.rb | 36 ------------------- lib/gitlab/performance_bar/peek_query_tracker.rb | 38 ++++++++++++++++++++ vendor/assets/javascripts/peek.js | 22 +++++++----- 5 files changed, 64 insertions(+), 110 deletions(-) delete mode 100644 lib/gitlab/performance_bar/peek_mysql_with_queries.rb delete mode 100644 lib/gitlab/performance_bar/peek_pg_with_queries.rb create mode 100644 lib/gitlab/performance_bar/peek_query_tracker.rb diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index a8669ddba97..6c008e53af6 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -4,44 +4,26 @@ Peek.into Peek::Views::Host Peek.into Peek::Views::PerformanceBar if Gitlab::Database.mysql? require 'peek-mysql' - Peek.into Peek::Views::Mysql2 + PEEK_DB_CLIENT = ::Mysql2::Client + PEEK_DB_VIEW = Peek::Views::Mysql2 + Peek.into PEEK_DB_VIEW else require 'peek-pg' - Peek.into Peek::Views::PG + PEEK_DB_CLIENT = ::PG::Connection + PEEK_DB_VIEW = Peek::Views::PG + Peek.into PEEK_DB_VIEW end Peek.into Peek::Views::Redis Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::GC -if Gitlab::Database.mysql? - class Mysql2::Client - class << self - attr_accessor :query_details - end - self.query_details = Concurrent::Array.new - end - module Peek - module Views - class Mysql2 < View - prepend ::Gitlab::PerformanceBar::PeekMysqlWithQueries - end - end - end -else - class PG::Connection - class << self - attr_accessor :query_details - end - self.query_details = Concurrent::Array.new - end - - module Peek - module Views - class PG < View - prepend ::Gitlab::PerformanceBar::PeekPgWithQueries - end - end +class PEEK_DB_CLIENT + class << self + attr_accessor :query_details end + self.query_details = Concurrent::Array.new end + +PEEK_DB_VIEW.prepend ::Gitlab::PerformanceBar::PeekQueryTracker diff --git a/lib/gitlab/performance_bar/peek_mysql_with_queries.rb b/lib/gitlab/performance_bar/peek_mysql_with_queries.rb deleted file mode 100644 index ea577de5a69..00000000000 --- a/lib/gitlab/performance_bar/peek_mysql_with_queries.rb +++ /dev/null @@ -1,36 +0,0 @@ -# Inspired by https://github.com/peek/peek-mysql2/blob/master/lib/peek/views/mysql2.rb -module Gitlab - module PerformanceBar - module PeekMysqlWithQueries - def queries - ::Mysql2::Client.query_details - end - - def results - super.merge(queries: queries) - end - - private - - def setup_subscribers - super - - # Reset each counter when a new request starts - before_request do - ::Mysql2::Client.query_details = [] - end - - subscribe('sql.active_record') do |_, start, finish, _, data| - if RequestStore.active? && RequestStore.store[:peek_enabled] - track_query(data[:sql].strip, data[:binds], start, finish) - end - end - end - - def track_query(raw_query, bindings, start, finish) - query = Gitlab::Sherlock::Query.new(raw_query, start, finish) - ::Mysql2::Client.query_details << query.formatted_query - end - end - end -end diff --git a/lib/gitlab/performance_bar/peek_pg_with_queries.rb b/lib/gitlab/performance_bar/peek_pg_with_queries.rb deleted file mode 100644 index 6e9cb593d36..00000000000 --- a/lib/gitlab/performance_bar/peek_pg_with_queries.rb +++ /dev/null @@ -1,36 +0,0 @@ -# Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb -module Gitlab - module PerformanceBar - module PeekPgWithQueries - def queries - ::PG::Connection.query_details - end - - def results - super.merge(queries: queries) - end - - private - - def setup_subscribers - super - - # Reset each counter when a new request starts - before_request do - ::PG::Connection.query_details = [] - end - - subscribe('sql.active_record') do |_, start, finish, _, data| - if RequestStore.active? && RequestStore.store[:peek_enabled] - track_query(data[:sql].strip, data[:binds], start, finish) - end - end - end - - def track_query(raw_query, bindings, start, finish) - query = Gitlab::Sherlock::Query.new(raw_query, start, finish) - ::PG::Connection.query_details << query.formatted_query - end - end - end -end diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb new file mode 100644 index 00000000000..479396f6664 --- /dev/null +++ b/lib/gitlab/performance_bar/peek_query_tracker.rb @@ -0,0 +1,38 @@ +# Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb +module Gitlab + module PerformanceBar + module PeekQueryTracker + def sorted_queries + PEEK_DB_CLIENT.query_details. + sort { |a, b| b[:duration] <=> a[:duration] } + end + + def results + super.merge(queries: sorted_queries) + end + + private + + def setup_subscribers + super + + # Reset each counter when a new request starts + before_request do + PEEK_DB_CLIENT.query_details = [] + end + + subscribe('sql.active_record') do |_, start, finish, _, data| + if RequestStore.active? && RequestStore.store[:peek_enabled] + track_query(data[:sql].strip, data[:binds], start, finish) + end + end + end + + def track_query(raw_query, bindings, start, finish) + query = Gitlab::Sherlock::Query.new(raw_query, start, finish) + query_info = { duration: query.duration.round(4), sql: query.formatted_query } + PEEK_DB_CLIENT.query_details << query_info + end + end + end +end diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 51d797ffd12..0aec31ee2cb 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -15,25 +15,31 @@ requestId = null; return $('#peek').length; }; updatePerformanceBar = function(results) { - var key, label, data, table, html, tr, td; + var key, label, data, table, html, tr, duration_td, sql_td, strong; for (key in results.data) { for (label in results.data[key]) { data = results.data[key][label]; - console.log(data); - if (Array.isArray(data)) { + if (label == 'queries') { table = document.createElement('table'); for (var i = 0; i < data.length; i += 1) { tr = document.createElement('tr'); - td = document.createElement('td'); + duration_td = document.createElement('td'); + sql_td = document.createElement('td'); + strong = document.createElement('strong'); + + strong.append(data[i]['duration'] + 'ms'); + duration_td.appendChild(strong); + tr.appendChild(duration_td); + + sql_td.appendChild(document.createTextNode(data[i]['sql'])); + tr.appendChild(sql_td); - td.appendChild(document.createTextNode(data[i])); - tr.appendChild(td); table.appendChild(tr); } - $table = $(table).addClass('table'); - $("[data-defer-to=" + key + "-" + label + "]").html($table); + table.className = 'table'; + $("[data-defer-to=" + key + "-" + label + "]").html(table); } else { $("[data-defer-to=" + key + "-" + label + "]").text(results.data[key][label]); -- cgit v1.2.1 From f45094a485f6378949acf3ac2d705020e4d7b126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 17:20:09 +0200 Subject: Small adjustments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/controllers/application_controller.rb | 2 + config/application.rb | 1 + config/initializers/peek.rb | 1 - vendor/assets/javascripts/jquery.tipsy.js | 258 ------------------------------ 4 files changed, 3 insertions(+), 259 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.tipsy.js diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d52fec19a82..7c1a4759cf2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -74,6 +74,8 @@ class ApplicationController < ActionController::Base else RequestStore.store[:peek_enabled] = cookies[:perf_bar_enabled].present? end + else + cookies[:perf_bar_enabled].present? end end diff --git a/config/application.rb b/config/application.rb index 8bbecf3ed0f..31f975d538b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -104,6 +104,7 @@ module Gitlab config.assets.precompile << "mailers/*.css" config.assets.precompile << "katex.css" config.assets.precompile << "katex.js" + config.assets.precompile << "peek.js" config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "peek.css" config.assets.precompile << "lib/ace.js" diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 6c008e53af6..429960b7c3e 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -18,7 +18,6 @@ Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::GC - class PEEK_DB_CLIENT class << self attr_accessor :query_details diff --git a/vendor/assets/javascripts/jquery.tipsy.js b/vendor/assets/javascripts/jquery.tipsy.js deleted file mode 100644 index d9fced24b60..00000000000 --- a/vendor/assets/javascripts/jquery.tipsy.js +++ /dev/null @@ -1,258 +0,0 @@ -// tipsy, facebook style tooltips for jquery -// version 1.0.0a -// (c) 2008-2010 jason frame [jason@onehackoranother.com] -// released under the MIT license - -(function($) { - - function maybeCall(thing, ctx) { - return (typeof thing == 'function') ? (thing.call(ctx)) : thing; - }; - - function isElementInDOM(ele) { - while (ele = ele.parentNode) { - if (ele == document) return true; - } - return false; - }; - - function Tipsy(element, options) { - this.$element = $(element); - this.options = options; - this.enabled = true; - this.fixTitle(); - }; - - Tipsy.prototype = { - show: function() { - var title = this.getTitle(); - if (title && this.enabled) { - var $tip = this.tip(); - - $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); - $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity - $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body); - - var pos = $.extend({}, this.$element.offset(), { - width: this.$element[0].offsetWidth, - height: this.$element[0].offsetHeight - }); - - var actualWidth = $tip[0].offsetWidth, - actualHeight = $tip[0].offsetHeight, - gravity = maybeCall(this.options.gravity, this.$element[0]); - - var tp; - switch (gravity.charAt(0)) { - case 'n': - tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; - break; - case 's': - tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; - break; - case 'e': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; - break; - case 'w': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; - break; - } - - if (gravity.length == 2) { - if (gravity.charAt(1) == 'w') { - tp.left = pos.left + pos.width / 2 - 15; - } else { - tp.left = pos.left + pos.width / 2 - actualWidth + 15; - } - } - - $tip.css(tp).addClass('tipsy-' + gravity); - $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0); - if (this.options.className) { - $tip.addClass(maybeCall(this.options.className, this.$element[0])); - } - - if (this.options.fade) { - $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); - } else { - $tip.css({visibility: 'visible', opacity: this.options.opacity}); - } - } - }, - - hide: function() { - if (this.options.fade) { - this.tip().stop().fadeOut(function() { $(this).remove(); }); - } else { - this.tip().remove(); - } - }, - - fixTitle: function() { - var $e = this.$element; - if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') { - $e.attr('original-title', $e.attr('title') || '').removeAttr('title'); - } - }, - - getTitle: function() { - var title, $e = this.$element, o = this.options; - this.fixTitle(); - var title, o = this.options; - if (typeof o.title == 'string') { - title = $e.attr(o.title == 'title' ? 'original-title' : o.title); - } else if (typeof o.title == 'function') { - title = o.title.call($e[0]); - } - title = ('' + title).replace(/(^\s*|\s*$)/, ""); - return title || o.fallback; - }, - - tip: function() { - if (!this.$tip) { - this.$tip = $('
    ').html('
    '); - this.$tip.data('tipsy-pointee', this.$element[0]); - } - return this.$tip; - }, - - validate: function() { - if (!this.$element[0].parentNode) { - this.hide(); - this.$element = null; - this.options = null; - } - }, - - enable: function() { this.enabled = true; }, - disable: function() { this.enabled = false; }, - toggleEnabled: function() { this.enabled = !this.enabled; } - }; - - $.fn.tipsy = function(options) { - - if (options === true) { - return this.data('tipsy'); - } else if (typeof options == 'string') { - var tipsy = this.data('tipsy'); - if (tipsy) tipsy[options](); - return this; - } - - options = $.extend({}, $.fn.tipsy.defaults, options); - - function get(ele) { - var tipsy = $.data(ele, 'tipsy'); - if (!tipsy) { - tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); - $.data(ele, 'tipsy', tipsy); - } - return tipsy; - } - - function enter() { - var tipsy = get(this); - tipsy.hoverState = 'in'; - if (options.delayIn == 0) { - tipsy.show(); - } else { - tipsy.fixTitle(); - setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); - } - }; - - function leave() { - var tipsy = get(this); - tipsy.hoverState = 'out'; - if (options.delayOut == 0) { - tipsy.hide(); - } else { - setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut); - } - }; - - if (!options.live) this.each(function() { get(this); }); - - if (options.trigger != 'manual') { - var binder = options.live ? 'live' : 'bind', - eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', - eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; - this[binder](eventIn, enter)[binder](eventOut, leave); - } - - return this; - - }; - - $.fn.tipsy.defaults = { - className: null, - delayIn: 0, - delayOut: 0, - fade: false, - fallback: '', - gravity: 'n', - html: false, - live: false, - offset: 0, - opacity: 0.8, - title: 'title', - trigger: 'hover' - }; - - $.fn.tipsy.revalidate = function() { - $('.tipsy').each(function() { - var pointee = $.data(this, 'tipsy-pointee'); - if (!pointee || !isElementInDOM(pointee)) { - $(this).remove(); - } - }); - }; - - // Overwrite this method to provide options on a per-element basis. - // For example, you could store the gravity in a 'tipsy-gravity' attribute: - // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); - // (remember - do not modify 'options' in place!) - $.fn.tipsy.elementOptions = function(ele, options) { - return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; - }; - - $.fn.tipsy.autoNS = function() { - return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; - }; - - $.fn.tipsy.autoWE = function() { - return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; - }; - - /** - * yields a closure of the supplied parameters, producing a function that takes - * no arguments and is suitable for use as an autogravity function like so: - * - * @param margin (int) - distance from the viewable region edge that an - * element should be before setting its tooltip's gravity to be away - * from that edge. - * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer - * if there are no viewable region edges effecting the tooltip's - * gravity. It will try to vary from this minimally, for example, - * if 'sw' is preferred and an element is near the right viewable - * region edge, but not the top edge, it will set the gravity for - * that element's tooltip to be 'se', preserving the southern - * component. - */ - $.fn.tipsy.autoBounds = function(margin, prefer) { - return function() { - var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)}, - boundTop = $(document).scrollTop() + margin, - boundLeft = $(document).scrollLeft() + margin, - $this = $(this); - - if ($this.offset().top < boundTop) dir.ns = 'n'; - if ($this.offset().left < boundLeft) dir.ew = 'w'; - if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e'; - if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's'; - - return dir.ns + (dir.ew ? dir.ew : ''); - } - }; - -})(jQuery); -- cgit v1.2.1 From 55631e3d52dd2e16b69b02ffc99207b60a22989e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 17:23:02 +0200 Subject: Add CHANGELOG entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- changelogs/unreleased/29010-perf-bar.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/29010-perf-bar.yml diff --git a/changelogs/unreleased/29010-perf-bar.yml b/changelogs/unreleased/29010-perf-bar.yml new file mode 100644 index 00000000000..920b57ed130 --- /dev/null +++ b/changelogs/unreleased/29010-perf-bar.yml @@ -0,0 +1,4 @@ +--- +title: Allow to enable a performance bar with the keyboard shortcut +merge_request: 11439 +author: -- cgit v1.2.1 From e85914532970d61d53f025f3ff0d85891bab2637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 19:55:07 +0200 Subject: Fix Rubocop offenses, improve SQL duration format and changelog entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 27 ++++++++++----------- app/assets/javascripts/shortcuts.js | 5 ++-- changelogs/unreleased/29010-perf-bar.yml | 2 +- config/initializers/peek.rb | 6 ++--- lib/gitlab/performance_bar/peek_query_tracker.rb | 3 ++- lib/peek/rblineprof/custom_controller_helpers.rb | 30 +++++++++++++++++------- vendor/assets/javascripts/peek.js | 30 +++++++----------------- vendor/assets/stylesheets/peek.scss | 13 ++++------ 8 files changed, 53 insertions(+), 63 deletions(-) diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index 06a07c19e09..60f8bdd14c0 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -1,19 +1,16 @@ import 'vendor/peek'; import 'vendor/peek.performance_bar'; -(function() { - $(document).on('click', '#peek-show-queries', function(e) { - var $modal; - $('.peek-rblineprof-modal').hide(); - $modal = $('#modal-peek-pg-queries'); - if ($modal.length) { - $modal.modal('toggle'); - } - return e.preventDefault(); - }); +$(document).on('click', '#peek-show-queries', function(e) { + e.preventDefault(); + $('.peek-rblineprof-modal').hide(); + let $modal = $('#modal-peek-pg-queries'); + if ($modal.length) { + $modal.modal('toggle'); + } +}); - $(document).on('click', '.js-lineprof-file', function(e) { - $(this).parents('.heading').next('div').toggle(); - return e.preventDefault(); - }); -}).call(window); +$(document).on('click', '.js-lineprof-file', function(e) { + e.preventDefault(); + $(this).parents('.heading').next('div').toggle(); +}); diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index 2c7698eb174..09cb914e884 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -60,11 +60,10 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; e.preventDefault(); if (Cookies.get('perf_bar_enabled') === 'true') { Cookies.remove('perf_bar_enabled', { path: '/' }); - } - else { + } else { Cookies.set('perf_bar_enabled', true, { path: '/' }); } - return gl.utils.refreshCurrentPage(); + gl.utils.refreshCurrentPage(); }; Shortcuts.prototype.toggleMarkdownPreview = function(e) { diff --git a/changelogs/unreleased/29010-perf-bar.yml b/changelogs/unreleased/29010-perf-bar.yml index 920b57ed130..f4167e5562f 100644 --- a/changelogs/unreleased/29010-perf-bar.yml +++ b/changelogs/unreleased/29010-perf-bar.yml @@ -1,4 +1,4 @@ --- -title: Allow to enable a performance bar with the keyboard shortcut +title: Add an optional performance bar to view performance metrics for the current page merge_request: 11439 author: diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 429960b7c3e..c88ae4b62d2 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -3,16 +3,15 @@ Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Re Peek.into Peek::Views::Host Peek.into Peek::Views::PerformanceBar if Gitlab::Database.mysql? - require 'peek-mysql' + require 'peek-mysql2' PEEK_DB_CLIENT = ::Mysql2::Client PEEK_DB_VIEW = Peek::Views::Mysql2 - Peek.into PEEK_DB_VIEW else require 'peek-pg' PEEK_DB_CLIENT = ::PG::Connection PEEK_DB_VIEW = Peek::Views::PG - Peek.into PEEK_DB_VIEW end +Peek.into PEEK_DB_VIEW Peek.into Peek::Views::Redis Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof @@ -25,4 +24,5 @@ class PEEK_DB_CLIENT self.query_details = Concurrent::Array.new end +# rubocop:disable Style/ClassAndModuleCamelCase PEEK_DB_VIEW.prepend ::Gitlab::PerformanceBar::PeekQueryTracker diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb index 479396f6664..7ab80f5ee0f 100644 --- a/lib/gitlab/performance_bar/peek_query_tracker.rb +++ b/lib/gitlab/performance_bar/peek_query_tracker.rb @@ -30,7 +30,8 @@ module Gitlab def track_query(raw_query, bindings, start, finish) query = Gitlab::Sherlock::Query.new(raw_query, start, finish) - query_info = { duration: query.duration.round(4), sql: query.formatted_query } + query_info = { duration: '%.3f' % query.duration, sql: query.formatted_query } + PEEK_DB_CLIENT.query_details << query_info end end diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb index 796420971b5..2ecda58c669 100644 --- a/lib/peek/rblineprof/custom_controller_helpers.rb +++ b/lib/peek/rblineprof/custom_controller_helpers.rb @@ -11,6 +11,7 @@ module Peek end end + # rubocop:disable Metrics/AbcSize def inject_rblineprof ret = nil profile = lineprof(rblineprof_profiler_regex) do @@ -25,32 +26,43 @@ module Peek # Sort each file by the longest calculated time per_file = profile.map do |file, lines| - total, child, excl, total_cpu, child_cpu, excl_cpu = lines[0] + total, _child, excl, total_cpu, _child_cpu, excl_cpu = lines[0] wall = summary == 'exclusive' ? excl : total cpu = summary == 'exclusive' ? excl_cpu : total_cpu idle = summary == 'exclusive' ? (excl - excl_cpu) : (total - total_cpu) + sort_method = + case sort + when 'idle' + idle + when 'cpu' + cpu + else + wall + end + [ file, lines, wall, cpu, idle, - sort == 'idle' ? idle : sort == 'cpu' ? cpu : wall + sort_method ] - end.sort_by{ |a,b,c,d,e,f| -f } + end + per_file = per_file.sort_by { |_a, _b, _c, _d, _e, f| -f } output = '' per_file.each do |file_name, lines, file_wall, file_cpu, file_idle, file_sort| - output << "
    " show_src = file_sort > min tmpl = show_src ? "%s" : "%s" - if mode == 'cpu' - output << sprintf("% 8.1fms + % 8.1fms #{tmpl}", file_cpu / 1000.0, file_idle / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) - else - output << sprintf("% 8.1fms #{tmpl}", file_wall/1000.0, file_name.sub(Rails.root.to_s + '/', '')) - end + output << + if mode == 'cpu' + sprintf("% 8.1fms + % 8.1fms #{tmpl}", file_cpu / 1000.0, file_idle / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) + else + sprintf("% 8.1fms #{tmpl}", file_wall / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) + end output << "
    " # .heading diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 0aec31ee2cb..4db076d259a 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -1,6 +1,4 @@ -var requestId; - -requestId = null; +let requestId = null; (function($) { var fetchRequestResults, getRequestId, peekEnabled, toggleBar, updatePerformanceBar; @@ -16,9 +14,11 @@ requestId = null; }; updatePerformanceBar = function(results) { var key, label, data, table, html, tr, duration_td, sql_td, strong; - for (key in results.data) { - for (label in results.data[key]) { + + Object.keys(results.data).forEach((key) => { + Object.keys(results.data[key]).forEach((label) => { data = results.data[key][label]; + if (label == 'queries') { table = document.createElement('table'); @@ -40,12 +40,11 @@ requestId = null; table.className = 'table'; $("[data-defer-to=" + key + "-" + label + "]").html(table); - } - else { + } else { $("[data-defer-to=" + key + "-" + label + "]").text(results.data[key][label]); } - } - } + }); + }); return $(document).trigger('peek:render', [getRequestId(), results]); }; toggleBar = function(event) { @@ -77,19 +76,6 @@ requestId = null; }; $(document).on('keypress', toggleBar); $(document).on('peek:update', fetchRequestResults); - $(document).on('pjax:end', function(event, xhr, options) { - if (xhr != null) { - requestId = xhr.getResponseHeader('X-Request-Id'); - } - if (peekEnabled()) { - return $(this).trigger('peek:update'); - } - }); - $(document).on('page:change turbolinks:load', function() { - if (peekEnabled()) { - return $(this).trigger('peek:update'); - } - }); return $(function() { if (peekEnabled()) { return $(this).trigger('peek:update'); diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 224ecc4245e..4977487d3ae 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -6,16 +6,11 @@ header.navbar-gitlab.with-peek { } #peek { - background: #000; + background: $black; height: 35px; line-height: 35px; color: #999; - .hidden { - display: none; - visibility: visible; - } - &.disabled { display: none; } @@ -58,12 +53,12 @@ header.navbar-gitlab.with-peek { } strong { - color: #fff; + color: $white-light; } table { strong { - color: #000; + color: $black; } } @@ -95,5 +90,5 @@ header.navbar-gitlab.with-peek { } #modal-peek-pg-queries-content { - color: #000; + color: $black; } -- cgit v1.2.1 From add21d409f65fd9ea8cb6b253f876322c2770612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 20:04:08 +0200 Subject: Add `p b` to the shortcuts documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/views/help/_shortcuts.html.haml | 2 +- doc/workflow/shortcuts.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 42017228908..331d1181220 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -30,7 +30,7 @@ %tr %td.shortcut .key p b - %td Enable the Performance Bar + %td Show/hide the Performance Bar %tr %td.shortcut .key ? diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index c5b7488be69..87416008e98 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -6,7 +6,10 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | Keyboard Shortcut | Description | | ----------------- | ----------- | +| n | Main navigation | | s | Focus search | +| f | Focus filter | +| p b | Show/hide the Performance Bar | | ? | Show/hide this dialog | | + shift + p | Toggle markdown preview | | | Edit last comment (when focused on an empty textarea) | -- cgit v1.2.1 From 54c04f53fddc36d62a3d3d4125c9fe205138fc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 21:45:03 +0200 Subject: Fix spec failures and add a feature flag for the performance bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/performance_bar.rb | 2 +- spec/models/commit_spec.rb | 2 +- vendor/assets/stylesheets/peek.scss | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb index 3324fec94d4..163a40ad306 100644 --- a/lib/gitlab/performance_bar.rb +++ b/lib/gitlab/performance_bar.rb @@ -1,7 +1,7 @@ module Gitlab module PerformanceBar def self.enabled? - ENV["PERFORMANCE_BAR"] == '1' + Feature.enabled?('gitlab_performance_bar') end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 140fd2720bf..3905240f48d 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -21,7 +21,7 @@ describe Commit, models: true do it 'caches the author' do user = create(:user, email: commit.author_email) - expect(RequestStore).to receive(:active?).twice.and_return(true) + expect(RequestStore).to receive(:active?).and_return(true) expect_any_instance_of(Commit).to receive(:find_author_by_any_email).and_call_original expect(commit.author).to eq(user) diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 4977487d3ae..3508f2e2450 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -6,8 +6,13 @@ header.navbar-gitlab.with-peek { } #peek { - background: $black; + position: fixed; + top: 0; + left: 0; + width: 100%; height: 35px; + z-index: 400; + background: #000; line-height: 35px; color: #999; @@ -53,12 +58,12 @@ header.navbar-gitlab.with-peek { } strong { - color: $white-light; + color: #fff; } table { strong { - color: $black; + color: #000; } } @@ -90,5 +95,5 @@ header.navbar-gitlab.with-peek { } #modal-peek-pg-queries-content { - color: $black; + color: #000; } -- cgit v1.2.1 From af0c69170695f43b15db9c93ae2f2b8ebaaa96b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 22:21:05 +0200 Subject: Improve the JS for the `p b` shortcut and fix its CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/shortcuts.js | 7 ++++--- vendor/assets/stylesheets/peek.scss | 5 ----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index 09cb914e884..a4a7f3fa944 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -58,10 +58,11 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; Shortcuts.prototype.onTogglePerfBar = function(e) { e.preventDefault(); - if (Cookies.get('perf_bar_enabled') === 'true') { - Cookies.remove('perf_bar_enabled', { path: '/' }); + const performanceBarCookieName = 'perf_bar_enabled'; + if (Cookies.get(performanceBarCookieName) === 'true') { + Cookies.remove(performanceBarCookieName, { path: '/' }); } else { - Cookies.set('perf_bar_enabled', true, { path: '/' }); + Cookies.set(performanceBarCookieName, true, { path: '/' }); } gl.utils.refreshCurrentPage(); }; diff --git a/vendor/assets/stylesheets/peek.scss b/vendor/assets/stylesheets/peek.scss index 3508f2e2450..f1845fb9044 100644 --- a/vendor/assets/stylesheets/peek.scss +++ b/vendor/assets/stylesheets/peek.scss @@ -6,12 +6,7 @@ header.navbar-gitlab.with-peek { } #peek { - position: fixed; - top: 0; - left: 0; - width: 100%; height: 35px; - z-index: 400; background: #000; line-height: 35px; color: #999; -- cgit v1.2.1 From b173ae6b48323b22f5650e97cd797fa9c49e6cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Jun 2017 23:51:13 +0200 Subject: Fix linting, route, and specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 6 +++--- app/controllers/application_controller.rb | 2 +- app/views/peek/views/_pg.html.haml | 6 +++--- config/initializers/peek.rb | 2 +- config/routes.rb | 2 +- spec/models/commit_spec.rb | 2 +- vendor/assets/javascripts/peek.js | 10 ++-------- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index 60f8bdd14c0..cdc6a54f4f5 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -1,16 +1,16 @@ import 'vendor/peek'; import 'vendor/peek.performance_bar'; -$(document).on('click', '#peek-show-queries', function(e) { +$(document).on('click', '#peek-show-queries', (e) => { e.preventDefault(); $('.peek-rblineprof-modal').hide(); - let $modal = $('#modal-peek-pg-queries'); + const $modal = $('#modal-peek-pg-queries'); if ($modal.length) { $modal.modal('toggle'); } }); -$(document).on('click', '.js-lineprof-file', function(e) { +$(document).on('click', '.js-lineprof-file', (e) => { e.preventDefault(); $(this).parents('.heading').next('div').toggle(); }); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7c1a4759cf2..91694ebcd1d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -19,7 +19,7 @@ class ApplicationController < ActionController::Base before_action :ldap_security_check before_action :sentry_context before_action :default_headers - before_action :add_gon_variables, unless: -> { request.path.start_with?('/peek') } + before_action :add_gon_variables, unless: -> { request.path.start_with?('/-/peek') } before_action :configure_permitted_parameters, if: :devise_controller? before_action :require_email, unless: :devise_controller? diff --git a/app/views/peek/views/_pg.html.haml b/app/views/peek/views/_pg.html.haml index 7a1901eb7d2..e38c07a6097 100644 --- a/app/views/peek/views/_pg.html.haml +++ b/app/views/peek/views/_pg.html.haml @@ -1,8 +1,8 @@ %strong %a#peek-show-queries{ href: '#' } - %span{ data: { defer_to: "#{view.defer_key}-duration" }}... + %span{ data: { defer_to: "#{view.defer_key}-duration" } }... \/ - %span{ data: { defer_to: "#{view.defer_key}-calls" }}... + %span{ data: { defer_to: "#{view.defer_key}-calls" } }... #modal-peek-pg-queries.modal{ tabindex: -1 } .modal-dialog #modal-peek-pg-queries-content.modal-content @@ -10,5 +10,5 @@ %a.close{ href: "#", "data-dismiss" => "modal" } × %h4 SQL queries - .modal-body{ data: { defer_to: "#{view.defer_key}-queries" }}... + .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... pg diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index c88ae4b62d2..c1c65cedccf 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -17,6 +17,7 @@ Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::GC +# rubocop:disable Style/ClassAndModuleCamelCase class PEEK_DB_CLIENT class << self attr_accessor :query_details @@ -24,5 +25,4 @@ class PEEK_DB_CLIENT self.query_details = Concurrent::Array.new end -# rubocop:disable Style/ClassAndModuleCamelCase PEEK_DB_VIEW.prepend ::Gitlab::PerformanceBar::PeekQueryTracker diff --git a/config/routes.rb b/config/routes.rb index 9a117711083..4fd6cb5d439 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,7 +11,6 @@ Rails.application.routes.draw do post :toggle_award_emoji, on: :member end - mount Peek::Railtie => '/peek' draw :sherlock draw :development draw :ci @@ -43,6 +42,7 @@ Rails.application.routes.draw do get 'liveness' => 'health#liveness' get 'readiness' => 'health#readiness' resources :metrics, only: [:index] + mount Peek::Railtie => '/peek' end # Koding route diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 3905240f48d..ba247dcc5cf 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -20,8 +20,8 @@ describe Commit, models: true do end it 'caches the author' do + allow(RequestStore).to receive(:active?).and_return(true) user = create(:user, email: commit.author_email) - expect(RequestStore).to receive(:active?).and_return(true) expect_any_instance_of(Commit).to receive(:find_author_by_any_email).and_call_original expect(commit.author).to eq(user) diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 4db076d259a..427a8a3efad 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -1,13 +1,7 @@ -let requestId = null; - (function($) { var fetchRequestResults, getRequestId, peekEnabled, toggleBar, updatePerformanceBar; getRequestId = function() { - if (requestId != null) { - return requestId; - } else { - return $('#peek').data('request-id'); - } + return $('#peek').data('request-id'); }; peekEnabled = function() { return $('#peek').length; @@ -64,7 +58,7 @@ let requestId = null; } }; fetchRequestResults = function() { - return $.ajax('/peek/results', { + return $.ajax('/-/peek/results', { data: { request_id: getRequestId() }, -- cgit v1.2.1 From 8a2d536ac3178c18a08cc107453c23c459b2cef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 8 Jun 2017 10:39:32 +0200 Subject: Fix vendor/peek.js so that it can be bundled with Webpack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- config/application.rb | 1 - vendor/assets/javascripts/peek.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/application.rb b/config/application.rb index 31f975d538b..8bbecf3ed0f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -104,7 +104,6 @@ module Gitlab config.assets.precompile << "mailers/*.css" config.assets.precompile << "katex.css" config.assets.precompile << "katex.js" - config.assets.precompile << "peek.js" config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "peek.css" config.assets.precompile << "lib/ace.js" diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 427a8a3efad..f7e77de34ff 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -9,8 +9,8 @@ updatePerformanceBar = function(results) { var key, label, data, table, html, tr, duration_td, sql_td, strong; - Object.keys(results.data).forEach((key) => { - Object.keys(results.data[key]).forEach((label) => { + Object.keys(results.data).forEach(function(key) { + Object.keys(results.data[key]).forEach(function(label) { data = results.data[key][label]; if (label == 'queries') { -- cgit v1.2.1 From 058aeb1ce7e45baf7ebb16a435924554501712ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 8 Jun 2017 14:59:55 +0200 Subject: Add a feature spec for the performance bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../user_can_display_performance_bar_spec.rb | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 spec/features/user_can_display_performance_bar_spec.rb diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb new file mode 100644 index 00000000000..c2842255b86 --- /dev/null +++ b/spec/features/user_can_display_performance_bar_spec.rb @@ -0,0 +1,81 @@ +require 'rails_helper' + +describe 'User can display performacne bar', :js do + shared_examples 'performance bar is disabled' do + it 'does not show the performance bar by default' do + expect(page).not_to have_css('#peek') + end + + context 'when user press `pb`' do + before do + find('body').native.send_keys('pb') + end + + it 'does not show the performance bar by default' do + expect(page).not_to have_css('#peek') + end + end + end + + shared_examples 'performance bar is enabled' do + it 'does not show the performance bar by default' do + expect(page).not_to have_css('#peek') + end + + context 'when user press `pb`' do + before do + find('body').native.send_keys('pb') + end + + it 'does not show the performance bar by default' do + expect(page).not_to have_css('#peek') + end + end + end + + context 'when user is logged-out' do + before do + visit root_path + end + + context 'when the gitlab_performance_bar feature is disabled' do + before do + Feature.disable('gitlab_performance_bar') + end + + it_behaves_like 'performance bar is disabled' + end + + context 'when the gitlab_performance_bar feature is enabled' do + before do + Feature.enable('gitlab_performance_bar') + end + + it_behaves_like 'performance bar is disabled' + end + end + + context 'when user is logged-in' do + before do + login_as :user + + visit root_path + end + + context 'when the gitlab_performance_bar feature is disabled' do + before do + Feature.disable('gitlab_performance_bar') + end + + it_behaves_like 'performance bar is disabled' + end + + context 'when the gitlab_performance_bar feature is enabled' do + before do + Feature.enable('gitlab_performance_bar') + end + + it_behaves_like 'performance bar is enabled' + end + end +end -- cgit v1.2.1 From d1b4576a79660d0f3360616ec746a06ed0ce62bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 8 Jun 2017 18:09:08 +0200 Subject: Ensure peek-performance_bar doesn't break existing functionalities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/views/peek/views/_mysql2.html.haml | 14 ++++++++++++++ config/initializers/peek.rb | 4 ++++ .../peek_performance_bar_with_rack_body.rb | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 app/views/peek/views/_mysql2.html.haml create mode 100644 lib/gitlab/performance_bar/peek_performance_bar_with_rack_body.rb diff --git a/app/views/peek/views/_mysql2.html.haml b/app/views/peek/views/_mysql2.html.haml new file mode 100644 index 00000000000..5a58c719c73 --- /dev/null +++ b/app/views/peek/views/_mysql2.html.haml @@ -0,0 +1,14 @@ +%strong + %a#peek-show-queries{ href: '#' } + %span{ data: { defer_to: "#{view.defer_key}-duration" } }... + \/ + %span{ data: { defer_to: "#{view.defer_key}-calls" } }... +#modal-peek-pg-queries.modal{ tabindex: -1 } + .modal-dialog + #modal-peek-pg-queries-content.modal-content + .modal-header + %a.close{ href: "#", "data-dismiss" => "modal" } × + %h4 + SQL queries + .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... +mysql diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index c1c65cedccf..65432caac2a 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -26,3 +26,7 @@ class PEEK_DB_CLIENT end PEEK_DB_VIEW.prepend ::Gitlab::PerformanceBar::PeekQueryTracker + +class Peek::Views::PerformanceBar::ProcessUtilization + prepend ::Gitlab::PerformanceBar::PeekPerformanceBarWithRackBody +end diff --git a/lib/gitlab/performance_bar/peek_performance_bar_with_rack_body.rb b/lib/gitlab/performance_bar/peek_performance_bar_with_rack_body.rb new file mode 100644 index 00000000000..d939a6ea18d --- /dev/null +++ b/lib/gitlab/performance_bar/peek_performance_bar_with_rack_body.rb @@ -0,0 +1,22 @@ +# This solves a bug with a X-Senfile header that wouldn't be set properly, see +# https://github.com/peek/peek-performance_bar/pull/27 +module Gitlab + module PerformanceBar + module PeekPerformanceBarWithRackBody + 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 -- cgit v1.2.1 From ef4ccb7e2676bf0fc3bf17c795b4ab0e0edc0821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 8 Jun 2017 18:49:11 +0200 Subject: Fix peek-rblineprof JS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/javascripts/peek.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/peek.js b/app/assets/javascripts/peek.js index cdc6a54f4f5..de1a99fa3bd 100644 --- a/app/assets/javascripts/peek.js +++ b/app/assets/javascripts/peek.js @@ -12,5 +12,5 @@ $(document).on('click', '#peek-show-queries', (e) => { $(document).on('click', '.js-lineprof-file', (e) => { e.preventDefault(); - $(this).parents('.heading').next('div').toggle(); + $(e.target).parents('.peek-rblineprof-file').find('.data').toggle(); }); -- cgit v1.2.1 From be0a949a004a34015977bdbcfe24e3173cd3aba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 9 Jun 2017 10:31:38 +0200 Subject: DRYed peek-pg/mysql2 views and update peek-rblineprof monkey-patch file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/views/peek/views/_mysql2.html.haml | 16 ++--------- app/views/peek/views/_pg.html.haml | 16 ++--------- app/views/peek/views/_sql.html.haml | 13 +++++++++ lib/peek/rblineprof/custom_controller_helpers.rb | 35 ++++++++++-------------- 4 files changed, 33 insertions(+), 47 deletions(-) create mode 100644 app/views/peek/views/_sql.html.haml diff --git a/app/views/peek/views/_mysql2.html.haml b/app/views/peek/views/_mysql2.html.haml index 5a58c719c73..ac811a10ef5 100644 --- a/app/views/peek/views/_mysql2.html.haml +++ b/app/views/peek/views/_mysql2.html.haml @@ -1,14 +1,4 @@ -%strong - %a#peek-show-queries{ href: '#' } - %span{ data: { defer_to: "#{view.defer_key}-duration" } }... - \/ - %span{ data: { defer_to: "#{view.defer_key}-calls" } }... -#modal-peek-pg-queries.modal{ tabindex: -1 } - .modal-dialog - #modal-peek-pg-queries-content.modal-content - .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × - %h4 - SQL queries - .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... +- local_assigns.fetch(:view) + += render 'peek/views/sql', view: view mysql diff --git a/app/views/peek/views/_pg.html.haml b/app/views/peek/views/_pg.html.haml index e38c07a6097..ee94c2f3274 100644 --- a/app/views/peek/views/_pg.html.haml +++ b/app/views/peek/views/_pg.html.haml @@ -1,14 +1,4 @@ -%strong - %a#peek-show-queries{ href: '#' } - %span{ data: { defer_to: "#{view.defer_key}-duration" } }... - \/ - %span{ data: { defer_to: "#{view.defer_key}-calls" } }... -#modal-peek-pg-queries.modal{ tabindex: -1 } - .modal-dialog - #modal-peek-pg-queries-content.modal-content - .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × - %h4 - SQL queries - .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... +- local_assigns.fetch(:view) + += render 'peek/views/sql', view: view pg diff --git a/app/views/peek/views/_sql.html.haml b/app/views/peek/views/_sql.html.haml new file mode 100644 index 00000000000..16fc010f66f --- /dev/null +++ b/app/views/peek/views/_sql.html.haml @@ -0,0 +1,13 @@ +%strong + %a#peek-show-queries{ href: '#' } + %span{ data: { defer_to: "#{view.defer_key}-duration" } }... + \/ + %span{ data: { defer_to: "#{view.defer_key}-calls" } }... +#modal-peek-pg-queries.modal{ tabindex: -1 } + .modal-dialog + #modal-peek-pg-queries-content.modal-content + .modal-header + %a.close{ href: "#", "data-dismiss" => "modal" } × + %h4 + SQL queries + .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb index 2ecda58c669..99f9c2c9b04 100644 --- a/lib/peek/rblineprof/custom_controller_helpers.rb +++ b/lib/peek/rblineprof/custom_controller_helpers.rb @@ -3,6 +3,8 @@ module Peek module CustomControllerHelpers extend ActiveSupport::Concern + # This will become useless once https://github.com/peek/peek-rblineprof/pull/5 + # is merged def pygmentize(file_name, code, lexer = nil) if lexer.present? Gitlab::Highlight.highlight(file_name, code) @@ -11,7 +13,7 @@ module Peek end end - # rubocop:disable Metrics/AbcSize + # rubocop:disable all def inject_rblineprof ret = nil profile = lineprof(rblineprof_profiler_regex) do @@ -26,43 +28,32 @@ module Peek # Sort each file by the longest calculated time per_file = profile.map do |file, lines| - total, _child, excl, total_cpu, _child_cpu, excl_cpu = lines[0] + total, child, excl, total_cpu, child_cpu, excl_cpu = lines[0] wall = summary == 'exclusive' ? excl : total cpu = summary == 'exclusive' ? excl_cpu : total_cpu idle = summary == 'exclusive' ? (excl - excl_cpu) : (total - total_cpu) - sort_method = - case sort - when 'idle' - idle - when 'cpu' - cpu - else - wall - end - [ file, lines, wall, cpu, idle, - sort_method + sort == 'idle' ? idle : sort == 'cpu' ? cpu : wall ] - end - per_file = per_file.sort_by { |_a, _b, _c, _d, _e, f| -f } + end.sort_by{ |a,b,c,d,e,f| -f } output = '' per_file.each do |file_name, lines, file_wall, file_cpu, file_idle, file_sort| + output << "
    " show_src = file_sort > min tmpl = show_src ? "%s" : "%s" - output << - if mode == 'cpu' - sprintf("% 8.1fms + % 8.1fms #{tmpl}", file_cpu / 1000.0, file_idle / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) - else - sprintf("% 8.1fms #{tmpl}", file_wall / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) - end + if mode == 'cpu' + output << sprintf("% 8.1fms + % 8.1fms #{tmpl}", file_cpu / 1000.0, file_idle / 1000.0, file_name.sub(Rails.root.to_s + '/', '')) + else + output << sprintf("% 8.1fms #{tmpl}", file_wall/1000.0, file_name.sub(Rails.root.to_s + '/', '')) + end output << "
    " # .heading @@ -89,6 +80,8 @@ module Peek output << "
    #{times.join("\n")}
    " # The following line was changed from # https://github.com/peek/peek-rblineprof/blob/8d3b7a283a27de2f40abda45974516693d882258/lib/peek/rblineprof/controller_helpers.rb#L125 + # This will become useless once https://github.com/peek/peek-rblineprof/pull/16 + # is merged and is implemented. output << "
    #{pygmentize(file_name, code.join, 'ruby')}
    " output << "
    " # .data then .peek-rblineprof-file end -- cgit v1.2.1