summaryrefslogtreecommitdiff
path: root/lib/gitlab/metrics
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /lib/gitlab/metrics
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
downloadgitlab-ce-859a6fb938bb9ee2a317c46dfa4fcc1af49608f0.tar.gz
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'lib/gitlab/metrics')
-rw-r--r--lib/gitlab/metrics/exporter/web_exporter.rb4
-rw-r--r--lib/gitlab/metrics/methods.rb2
-rw-r--r--lib/gitlab/metrics/rack_middleware.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/external_http.rb99
-rw-r--r--lib/gitlab/metrics/subscribers/rack_attack.rb91
5 files changed, 196 insertions, 2 deletions
diff --git a/lib/gitlab/metrics/exporter/web_exporter.rb b/lib/gitlab/metrics/exporter/web_exporter.rb
index b6a27d8556a..558454eaa1c 100644
--- a/lib/gitlab/metrics/exporter/web_exporter.rb
+++ b/lib/gitlab/metrics/exporter/web_exporter.rb
@@ -12,6 +12,10 @@ module Gitlab
Gitlab::HealthChecks::Result.new(
'web_exporter', exporter.running)
end
+
+ def available?
+ true
+ end
end
attr_reader :running
diff --git a/lib/gitlab/metrics/methods.rb b/lib/gitlab/metrics/methods.rb
index 3100450bc00..8ddd76ad7ae 100644
--- a/lib/gitlab/metrics/methods.rb
+++ b/lib/gitlab/metrics/methods.rb
@@ -39,7 +39,7 @@ module Gitlab
options.evaluate(&block)
if disabled_by_feature(options)
- synchronized_cache_fill(name) { NullMetric.instance }
+ synchronized_cache_fill(name) { ::Gitlab::Metrics::NullMetric.instance }
else
synchronized_cache_fill(name) { build_metric!(type, name, options) }
end
diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb
index a6884ea6983..f7e53bf545b 100644
--- a/lib/gitlab/metrics/rack_middleware.rb
+++ b/lib/gitlab/metrics/rack_middleware.rb
@@ -10,7 +10,7 @@ module Gitlab
# env - A Hash containing Rack environment details.
def call(env)
- trans = WebTransaction.new(env)
+ trans = Gitlab::Metrics::WebTransaction.new(env)
begin
retval = trans.run { @app.call(env) }
diff --git a/lib/gitlab/metrics/subscribers/external_http.rb b/lib/gitlab/metrics/subscribers/external_http.rb
new file mode 100644
index 00000000000..94c5d965200
--- /dev/null
+++ b/lib/gitlab/metrics/subscribers/external_http.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module Subscribers
+ # Class for tracking the total time spent in external HTTP
+ # See more at https://gitlab.com/gitlab-org/labkit-ruby/-/blob/v0.14.0/lib/gitlab-labkit.rb#L18
+ class ExternalHttp < ActiveSupport::Subscriber
+ attach_to :external_http
+
+ DEFAULT_STATUS_CODE = 'undefined'
+
+ DETAIL_STORE = :external_http_detail_store
+ COUNTER = :external_http_count
+ DURATION = :external_http_duration_s
+
+ KNOWN_PAYLOAD_KEYS = [COUNTER, DURATION].freeze
+
+ def self.detail_store
+ ::Gitlab::SafeRequestStore[DETAIL_STORE] ||= []
+ end
+
+ def self.duration
+ Gitlab::SafeRequestStore[DURATION].to_f
+ end
+
+ def self.request_count
+ Gitlab::SafeRequestStore[COUNTER].to_i
+ end
+
+ def self.payload
+ {
+ COUNTER => request_count,
+ DURATION => duration
+ }
+ end
+
+ def request(event)
+ payload = event.payload
+ add_to_detail_store(payload)
+ add_to_request_store(payload)
+ expose_metrics(payload)
+ end
+
+ private
+
+ def current_transaction
+ ::Gitlab::Metrics::Transaction.current
+ end
+
+ def add_to_detail_store(payload)
+ return unless Gitlab::PerformanceBar.enabled_for_request?
+
+ self.class.detail_store << {
+ duration: payload[:duration],
+ scheme: payload[:scheme],
+ method: payload[:method],
+ host: payload[:host],
+ port: payload[:port],
+ path: payload[:path],
+ query: payload[:query],
+ code: payload[:code],
+ exception_object: payload[:exception_object],
+ backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller)
+ }
+ end
+
+ def add_to_request_store(payload)
+ return unless Gitlab::SafeRequestStore.active?
+
+ Gitlab::SafeRequestStore[COUNTER] = Gitlab::SafeRequestStore[COUNTER].to_i + 1
+ Gitlab::SafeRequestStore[DURATION] = Gitlab::SafeRequestStore[DURATION].to_f + payload[:duration].to_f
+ end
+
+ def expose_metrics(payload)
+ return unless current_transaction
+
+ labels = { method: payload[:method], code: payload[:code] || DEFAULT_STATUS_CODE }
+
+ current_transaction.increment(:gitlab_external_http_total, 1, labels) do
+ docstring 'External HTTP calls'
+ label_keys labels.keys
+ end
+
+ current_transaction.observe(:gitlab_external_http_duration_seconds, payload[:duration]) do
+ docstring 'External HTTP time'
+ buckets [0.001, 0.01, 0.1, 1.0, 2.0, 5.0]
+ end
+
+ if payload[:exception_object].present?
+ current_transaction.increment(:gitlab_external_http_exception_total, 1) do
+ docstring 'External HTTP exceptions'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/subscribers/rack_attack.rb b/lib/gitlab/metrics/subscribers/rack_attack.rb
new file mode 100644
index 00000000000..2791a39fb16
--- /dev/null
+++ b/lib/gitlab/metrics/subscribers/rack_attack.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module Subscribers
+ # - Adds logging for all Rack Attack blocks and throttling events.
+ # - Instrument the cache operations of RackAttack to use in structured
+ # logs. Two fields are exposed:
+ # + rack_attack_redis_count: the number of redis calls triggered by
+ # RackAttack in a request.
+ # + rack_attack_redis_duration_s: the total duration of all redis calls
+ # triggered by RackAttack in a request.
+ class RackAttack < ActiveSupport::Subscriber
+ attach_to 'rack_attack'
+
+ INSTRUMENTATION_STORE_KEY = :rack_attack_instrumentation
+
+ THROTTLES_WITH_USER_INFORMATION = [
+ :throttle_authenticated_api,
+ :throttle_authenticated_web,
+ :throttle_authenticated_protected_paths_api,
+ :throttle_authenticated_protected_paths_web
+ ].freeze
+
+ PAYLOAD_KEYS = [
+ :rack_attack_redis_count,
+ :rack_attack_redis_duration_s
+ ].freeze
+
+ def self.payload
+ Gitlab::SafeRequestStore[INSTRUMENTATION_STORE_KEY] ||= {
+ rack_attack_redis_count: 0,
+ rack_attack_redis_duration_s: 0.0
+ }
+ end
+
+ def redis(event)
+ self.class.payload[:rack_attack_redis_count] += 1
+ self.class.payload[:rack_attack_redis_duration_s] += event.duration.to_f / 1000
+ end
+
+ def safelist(event)
+ req = event.payload[:request]
+ Gitlab::Instrumentation::Throttle.safelist = req.env['rack.attack.matched']
+ end
+
+ def throttle(event)
+ log_into_auth_logger(event)
+ end
+
+ def blocklist(event)
+ log_into_auth_logger(event)
+ end
+
+ def track(event)
+ log_into_auth_logger(event)
+ end
+
+ private
+
+ def log_into_auth_logger(event)
+ req = event.payload[:request]
+ rack_attack_info = {
+ message: 'Rack_Attack',
+ env: req.env['rack.attack.match_type'],
+ remote_ip: req.ip,
+ request_method: req.request_method,
+ path: req.fullpath,
+ matched: req.env['rack.attack.matched']
+ }
+
+ if THROTTLES_WITH_USER_INFORMATION.include? req.env['rack.attack.matched'].to_sym
+ user_id = req.env['rack.attack.match_discriminator']
+ user = User.find_by(id: user_id) # rubocop:disable CodeReuse/ActiveRecord
+
+ rack_attack_info[:user_id] = user_id
+ rack_attack_info['meta.user'] = user.username unless user.nil?
+ end
+
+ Gitlab::InstrumentationHelper.add_instrumentation_data(rack_attack_info)
+
+ logger.error(rack_attack_info)
+ end
+
+ def logger
+ Gitlab::AuthLogger
+ end
+ end
+ end
+ end
+end