Skip to content

Instantly share code, notes, and snippets.

@eguchi-ken
Last active October 27, 2022 10:33
Show Gist options
  • Save eguchi-ken/b13d908d0182d3ec3ec467b042932199 to your computer and use it in GitHub Desktop.
Save eguchi-ken/b13d908d0182d3ec3ec467b042932199 to your computer and use it in GitHub Desktop.
rails のログに含まれる SQL を分析します
# frozen_string_literal: true
Query = Struct.new(:type, :raw_sql, :model_name, :duration, keyword_init: true) do
def abstract_sql
raw_sql.gsub(/\d+/, 'N')
end
def unknown?
type.nil? && model_name.nil?
end
def label
if unknown?
['Unknown', 'Unknown']
else
[model_name, type]
end
end
end
class Line
def initialize(string)
@string = string.strip
end
def to_query
try_template_query || try_another_query
end
private
def try_template_query
match_data = @string.match(/(\w+) (Load|Exists\?|Update|Create) \(([\d.])+ms\)(.+)/)
_, model_name, query_type, duration, raw_sql = match_data.to_a
if query_type
Query.new(model_name: model_name, type: query_type, raw_sql: raw_sql, duration: duration.to_f)
end
end
def try_another_query
match_data = @string.match(/(.*) \(([\d.])+ms\)(.+)/)
_, _, duration, raw_sql = match_data.to_a
if duration
Query.new(raw_sql: raw_sql, duration: duration.to_f)
end
end
end
class LogProfiler
def initialize(raw_message)
@raw_message = raw_message
@queries = init_queries
end
def print
puts("| MODEL NAME | TYPE | DURATION | COUNT |")
puts("|------------------------------------------|------------|----------|--------|")
puts(table)
puts("Query total time: #{query_total_time}ms")
puts("Unkown Query Samples:")
puts(unknown_query_samples.join("\n\n"))
end
private
def query_total_time
@queries.sum(&:duration)
end
def unknown_query_samples
@queries
.filter(&:unknown?)
.map(&:abstract_sql)
.tally
.map { |sql, count| "#{count}\t#{sql}" }
end
def table
group = @queries.group_by { |query| query.label }
result = group.map { |(model_name, type), queries| [model_name, type, queries.sum(&:duration), queries.count] }
result = result.sort_by { |item| - item[-2] }
result.map do |model_name, type, duration, count|
format("| %40s | %10s | %8s | %6s |", model_name, type, duration, count)
end
end
def init_queries
@raw_message.each_line.filter_map { Line.new(_1).to_query }
end
end
log = File.read(ARGV[0])
profiler = LogProfiler.new(log)
profiler.print
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment