diff --git a/lib/method_profiler.rb b/lib/method_profiler.rb index 3e4c841419b..fd6588c6bc7 100644 --- a/lib/method_profiler.rb +++ b/lib/method_profiler.rb @@ -38,6 +38,50 @@ class MethodProfiler klass.class_eval patches end + def self.patch_with_debug_sql(klass, methods, name, no_recurse: false) + patches = methods.map do |method_name| + + recurse_protection = "" + if no_recurse + recurse_protection = <<~RUBY + return #{method_name}__mp_unpatched_debug_sql(*args, &blk) if @mp_recurse_protect_#{method_name} + @mp_recurse_protect_#{method_name} = true + RUBY + end + + <<~RUBY + unless defined?(#{method_name}__mp_unpatched_debug_sql) + alias_method :#{method_name}__mp_unpatched_debug_sql, :#{method_name} + def #{method_name}(*args, &blk) + #{recurse_protection} + + query = args[0] + should_filter = #{@@instrumentation_debug_sql_filter_transactions} && + (query == "COMMIT" || query == "BEGIN" || query == "ROLLBACK") + if !should_filter + STDERR.puts "debugsql (sql): " + query + end + + begin + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + #{method_name}__mp_unpatched_debug_sql(*args, &blk) + ensure + duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start + + if !should_filter + STDERR.puts "debugsql (sec): " + duration.round(3).to_s + end + + #{"@mp_recurse_protect_#{method_name} = false" if no_recurse} + end + end + end + RUBY + end.join("\n") + + klass.class_eval patches + end + def self.transfer result = Thread.current[:_method_profiler] Thread.current[:_method_profiler] = nil @@ -64,6 +108,24 @@ class MethodProfiler data end + ## + # This is almost the same as ensure_discourse_instrumentation! but should not + # be used in production. This logs all SQL queries run and their durations + # between start and stop. + # + # filter_transactions - When true, we do not record timings of transaction + # related commits (BEGIN, COMMIT, ROLLBACK) + def self.output_sql_to_stderr!(filter_transactions: false) + Rails.logger.warn("Stop! This instrumentation is not intended for use in production outside of debugging scenarios. Please be sure you know what you are doing when enabling this instrumentation.") + @@instrumentation_debug_sql_filter_transactions = filter_transactions + @@instrumentation_setup_debug_sql ||= begin + MethodProfiler.patch_with_debug_sql(PG::Connection, [ + :exec, :async_exec, :exec_prepared, :send_query_prepared, :query, :exec_params + ], :sql) + true + end + end + def self.ensure_discourse_instrumentation! @@instrumentation_setup ||= begin MethodProfiler.patch(PG::Connection, [