gitlab-org--gitlab-foss/lib/gitlab/performance_bar/stats.rb

94 lines
2.7 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module PerformanceBar
# This class fetches Peek stats stored in redis and logs them in a
# structured log (so these can be then analyzed in Kibana)
class Stats
IGNORED_BACKTRACE_LOCATIONS = %w[
ee/lib/ee/peek
lib/peek
lib/gitlab/database
lib/gitlab/gitaly_client.rb
lib/gitlab/gitaly_client/call.rb
lib/gitlab/instrumentation/redis_interceptor.rb
].freeze
def initialize(redis)
@redis = redis
end
def process(id)
data = request(id)
return unless data
log_queries(id, data, 'active-record')
log_queries(id, data, 'gitaly')
log_queries(id, data, 'redis')
rescue StandardError => e
logger.error(message: "failed to process request id #{id}: #{e.message}")
end
private
def request(id)
# Peek gem stores request data under peek:requests:request_id key
json_data = @redis.get("peek:requests:#{id}")
raise "No data for #{id}" if json_data.nil?
Gitlab::Json.parse(json_data)
end
def log_queries(id, data, type)
queries_by_location(data, type).each do |location, queries|
next unless location
duration = queries.sum { |query| query['duration'].to_f }
log_info = {
method_path: "#{location[:filename]}:#{location[:method]}",
filename: location[:filename],
query_type: type,
request_id: id,
count: queries.count,
duration_ms: duration
}
logger.info(log_info)
end
end
def queries_by_location(data, type)
return [] unless queries = data.dig('data', type, 'details')
queries.group_by do |query|
parse_backtrace(query['backtrace'])
end
end
def parse_backtrace(backtrace)
return unless backtrace_row = find_caller(backtrace)
return unless match = /(?<filename>.*):(?<filenum>\d+):in `(?<method>.*)'/.match(backtrace_row)
{
filename: match[:filename],
# filenum may change quite frequently with every change in the file,
# because the intention is to aggregate these queries, we group
# them rather by method name which should not change so frequently
# filenum: match[:filenum].to_i,
method: match[:method]
}
end
def find_caller(backtrace)
backtrace.find do |line|
!line.start_with?(*IGNORED_BACKTRACE_LOCATIONS)
end
end
def logger
@logger ||= Gitlab::PerformanceBar::Logger.build
end
end
end
end