diff options
author | Douwe Maan <douwe@gitlab.com> | 2016-07-26 22:07:15 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2016-07-26 22:07:15 +0000 |
commit | 95efb6f1163b7c2c40d03ddd834016905fc45b50 (patch) | |
tree | 6208c2482149bd5215903af6271064be26d15875 /lib | |
parent | 74e17ed9eda95646d8defd495962be9dd3771eac (diff) | |
parent | ef8d9c269a8383ba2e3766b13b44b655e0588609 (diff) | |
download | gitlab-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.rb | 19 | ||||
-rw-r--r-- | lib/gitlab/request_profiler/middleware.rb | 47 | ||||
-rw-r--r-- | lib/gitlab/request_profiler/profile.rb | 43 |
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 |