Last active
October 27, 2022 10:33
-
-
Save eguchi-ken/b13d908d0182d3ec3ec467b042932199 to your computer and use it in GitHub Desktop.
rails のログに含まれる SQL を分析します
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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