You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
8.9 KiB
285 lines
8.9 KiB
class SqlPatches
|
|
|
|
def self.patched?
|
|
@patched
|
|
end
|
|
|
|
def self.patched=(val)
|
|
@patched = val
|
|
end
|
|
|
|
def self.class_exists?(name)
|
|
eval(name + ".class").to_s.eql?('Class')
|
|
rescue NameError
|
|
false
|
|
end
|
|
|
|
def self.module_exists?(name)
|
|
eval(name + ".class").to_s.eql?('Module')
|
|
rescue NameError
|
|
false
|
|
end
|
|
end
|
|
|
|
# The best kind of instrumentation is in the actual db provider, however we don't want to double instrument
|
|
if SqlPatches.class_exists? "Mysql2::Client"
|
|
|
|
class Mysql2::Result
|
|
alias_method :each_without_profiling, :each
|
|
def each(*args, &blk)
|
|
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
|
|
|
start = Time.now
|
|
result = each_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
|
|
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
|
result
|
|
end
|
|
end
|
|
|
|
class Mysql2::Client
|
|
alias_method :query_without_profiling, :query
|
|
def query(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return query_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = query_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
|
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
|
|
|
result
|
|
|
|
end
|
|
end
|
|
|
|
SqlPatches.patched = true
|
|
end
|
|
|
|
|
|
# PG patches, keep in mind exec and async_exec have a exec{|r| } semantics that is yet to be implemented
|
|
if SqlPatches.class_exists? "PG::Result"
|
|
|
|
class PG::Result
|
|
alias_method :each_without_profiling, :each
|
|
alias_method :values_without_profiling, :values
|
|
|
|
def values(*args, &blk)
|
|
return values_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
|
|
|
start = Time.now
|
|
result = values_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
|
|
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
|
result
|
|
end
|
|
|
|
def each(*args, &blk)
|
|
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
|
|
|
start = Time.now
|
|
result = each_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
|
|
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
|
result
|
|
end
|
|
end
|
|
|
|
class PG::Connection
|
|
alias_method :exec_without_profiling, :exec
|
|
alias_method :async_exec_without_profiling, :async_exec
|
|
alias_method :exec_prepared_without_profiling, :exec_prepared
|
|
alias_method :send_query_prepared_without_profiling, :send_query_prepared
|
|
alias_method :prepare_without_profiling, :prepare
|
|
|
|
def prepare(*args,&blk)
|
|
# we have no choice but to do this here,
|
|
# if we do the check for profiling first, our cache may miss critical stuff
|
|
|
|
@prepare_map ||= {}
|
|
@prepare_map[args[0]] = args[1]
|
|
# dont leak more than 10k ever
|
|
@prepare_map = {} if @prepare_map.length > 1000
|
|
|
|
current = ::Rack::MiniProfiler.current
|
|
return prepare_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
prepare_without_profiling(*args,&blk)
|
|
end
|
|
|
|
def exec(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return exec_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = exec_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
|
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
|
|
|
result
|
|
end
|
|
|
|
def exec_prepared(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return exec_prepared_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = exec_prepared_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
mapped = args[0]
|
|
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
|
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
|
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
|
|
|
result
|
|
end
|
|
|
|
def send_query_prepared(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return send_query_prepared_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = send_query_prepared_without_profiling(*args,&blk)
|
|
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
mapped = args[0]
|
|
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
|
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
|
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
|
|
|
result
|
|
end
|
|
|
|
def async_exec(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return exec_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = exec_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
|
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
|
|
|
result
|
|
end
|
|
|
|
alias_method :query, :exec
|
|
end
|
|
|
|
SqlPatches.patched = true
|
|
end
|
|
|
|
|
|
# Mongoid 3 patches
|
|
if SqlPatches.class_exists?("Moped::Node")
|
|
class Moped::Node
|
|
alias_method :process_without_profiling, :process
|
|
def process(*args,&blk)
|
|
current = ::Rack::MiniProfiler.current
|
|
return process_without_profiling(*args,&blk) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = process_without_profiling(*args,&blk)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
::Rack::MiniProfiler.record_sql(args[0].log_inspect, elapsed_time)
|
|
|
|
result
|
|
end
|
|
end
|
|
end
|
|
|
|
if SqlPatches.class_exists?("RSolr::Connection") && RSolr::VERSION[0] != "0" # requires at least v1.0.0
|
|
class RSolr::Connection
|
|
alias_method :execute_without_profiling, :execute
|
|
def execute_with_profiling(client, request_context)
|
|
current = ::Rack::MiniProfiler.current
|
|
return execute_without_profiling(client, request_context) unless current && current.measure
|
|
|
|
start = Time.now
|
|
result = execute_without_profiling(client, request_context)
|
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
|
|
|
data = "#{request_context[:method].upcase} #{request_context[:uri]}"
|
|
if request_context[:method] == :post and request_context[:data]
|
|
if request_context[:headers].include?("Content-Type") and request_context[:headers]["Content-Type"] == "text/xml"
|
|
# it's xml, unescaping isn't needed
|
|
data << "\n#{request_context[:data]}"
|
|
else
|
|
data << "\n#{Rack::Utils.unescape(request_context[:data])}"
|
|
end
|
|
end
|
|
::Rack::MiniProfiler.record_sql(data, elapsed_time)
|
|
|
|
result
|
|
end
|
|
alias_method :execute, :execute_with_profiling
|
|
end
|
|
end
|
|
|
|
|
|
# Fallback for sequel
|
|
if SqlPatches.class_exists?("Sequel::Database") && !SqlPatches.patched?
|
|
module Sequel
|
|
class Database
|
|
alias_method :log_duration_original, :log_duration
|
|
def log_duration(duration, message)
|
|
# `duration` will be in seconds, but we need it in milliseconds for internal consistency.
|
|
::Rack::MiniProfiler.record_sql(message, duration * 1000)
|
|
log_duration_original(duration, message)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
## based off https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/active_record.rb
|
|
## fallback for alls sorts of weird dbs
|
|
if SqlPatches.module_exists?('ActiveRecord') && !SqlPatches.patched?
|
|
module Rack
|
|
class MiniProfiler
|
|
module ActiveRecordInstrumentation
|
|
def self.included(instrumented_class)
|
|
instrumented_class.class_eval do
|
|
unless instrumented_class.method_defined?(:log_without_miniprofiler)
|
|
alias_method :log_without_miniprofiler, :log
|
|
alias_method :log, :log_with_miniprofiler
|
|
protected :log
|
|
end
|
|
end
|
|
end
|
|
|
|
def log_with_miniprofiler(*args, &block)
|
|
current = ::Rack::MiniProfiler.current
|
|
return log_without_miniprofiler(*args, &block) unless current && current.measure
|
|
|
|
sql, name, binds = args
|
|
t0 = Time.now
|
|
rval = log_without_miniprofiler(*args, &block)
|
|
|
|
# Don't log schema queries if the option is set
|
|
return rval if Rack::MiniProfiler.config.skip_schema_queries and name =~ /SCHEMA/
|
|
|
|
elapsed_time = ((Time.now - t0).to_f * 1000).round(1)
|
|
Rack::MiniProfiler.record_sql(sql, elapsed_time)
|
|
rval
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.insert_instrumentation
|
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
|
include ::Rack::MiniProfiler::ActiveRecordInstrumentation
|
|
end
|
|
end
|
|
|
|
if defined?(::Rails) && !SqlPatches.patched?
|
|
insert_instrumentation
|
|
end
|
|
end
|
|
end
|