summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-07-26 22:07:15 +0000
committerDouwe Maan <douwe@gitlab.com>2016-07-26 22:07:15 +0000
commit95efb6f1163b7c2c40d03ddd834016905fc45b50 (patch)
tree6208c2482149bd5215903af6271064be26d15875 /lib
parent74e17ed9eda95646d8defd495962be9dd3771eac (diff)
parentef8d9c269a8383ba2e3766b13b44b655e0588609 (diff)
downloadgitlab-ce-95efb6f1163b7c2c40d03ddd834016905fc45b50.tar.gz
Merge branch 'feature/profile-requests-conditionally' into 'master'
Return request profiling info when a header is passed ## What does this MR do? It allows returning profiling info (instead of actual content) when a certain header is passed ## Why was this MR needed? To facilitate having a performance overview of certain requests. ## What are the relevant issue numbers? https://gitlab.com/gitlab-com/infrastructure/issues/211 ## Does this MR meet the acceptance criteria? - [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added - [ ] ~~[Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)~~ - [ ] ~~API support added~~ - ~~Tests~~ - [ ] ~~Added for this feature/bug~~ - [ ] ~~All builds are passing~~ - [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) - [x] Branch has no merge conflicts with `master` (if you do - rebase it please) - [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) See merge request !5281
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/request_profiler.rb19
-rw-r--r--lib/gitlab/request_profiler/middleware.rb47
-rw-r--r--lib/gitlab/request_profiler/profile.rb43
3 files changed, 109 insertions, 0 deletions
diff --git a/lib/gitlab/request_profiler.rb b/lib/gitlab/request_profiler.rb
new file mode 100644
index 00000000000..8130e55351e
--- /dev/null
+++ b/lib/gitlab/request_profiler.rb
@@ -0,0 +1,19 @@
+require 'fileutils'
+
+module Gitlab
+ module RequestProfiler
+ PROFILES_DIR = "#{Gitlab.config.shared.path}/tmp/requests_profiles"
+
+ def profile_token
+ Rails.cache.fetch('profile-token') do
+ Devise.friendly_token
+ end
+ end
+ module_function :profile_token
+
+ def remove_all_profiles
+ FileUtils.rm_rf(PROFILES_DIR)
+ end
+ module_function :remove_all_profiles
+ end
+end
diff --git a/lib/gitlab/request_profiler/middleware.rb b/lib/gitlab/request_profiler/middleware.rb
new file mode 100644
index 00000000000..8da8b754975
--- /dev/null
+++ b/lib/gitlab/request_profiler/middleware.rb
@@ -0,0 +1,47 @@
+require 'ruby-prof'
+
+module Gitlab
+ module RequestProfiler
+ class Middleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if profile?(env)
+ call_with_profiling(env)
+ else
+ @app.call(env)
+ end
+ end
+
+ def profile?(env)
+ header_token = env['HTTP_X_PROFILE_TOKEN']
+ return unless header_token.present?
+
+ profile_token = RequestProfiler.profile_token
+ return unless profile_token.present?
+
+ header_token == profile_token
+ end
+
+ def call_with_profiling(env)
+ ret = nil
+ result = RubyProf::Profile.profile do
+ ret = @app.call(env)
+ end
+
+ printer = RubyProf::CallStackPrinter.new(result)
+ file_name = "#{env['PATH_INFO'].tr('/', '|')}_#{Time.current.to_i}.html"
+ file_path = "#{PROFILES_DIR}/#{file_name}"
+
+ FileUtils.mkdir_p(PROFILES_DIR)
+ File.open(file_path, 'wb') do |file|
+ printer.print(file)
+ end
+
+ ret
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/request_profiler/profile.rb b/lib/gitlab/request_profiler/profile.rb
new file mode 100644
index 00000000000..f89d56903ef
--- /dev/null
+++ b/lib/gitlab/request_profiler/profile.rb
@@ -0,0 +1,43 @@
+module Gitlab
+ module RequestProfiler
+ class Profile
+ attr_reader :name, :time, :request_path
+
+ alias_method :to_param, :name
+
+ def self.all
+ Dir["#{PROFILES_DIR}/*.html"].map do |path|
+ new(File.basename(path))
+ end
+ end
+
+ def self.find(name)
+ name_dup = name.dup
+ name_dup << '.html' unless name.end_with?('.html')
+
+ file_path = "#{PROFILES_DIR}/#{name_dup}"
+ return unless File.exist?(file_path)
+
+ new(name_dup)
+ end
+
+ def initialize(name)
+ @name = name
+
+ set_attributes
+ end
+
+ def content
+ File.read("#{PROFILES_DIR}/#{name}")
+ end
+
+ private
+
+ def set_attributes
+ _, path, timestamp = name.split(/(.*)_(\d+)\.html$/)
+ @request_path = path.tr('|', '/')
+ @time = Time.at(timestamp.to_i).utc
+ end
+ end
+ end
+end