class StackifyRubyAPM::Normalizers::ActiveRecord::SqlNormalizer
@api private
Public Class Methods
StackifyRubyAPM::Normalizers::Normalizer::new
# File lib/stackify_apm/normalizers/active_record.rb, line 15 def initialize(*args) super(*args) end
Public Instance Methods
# File lib/stackify_apm/normalizers/active_record.rb, line 19 def normalize(_transaction, _name, payload) return :skip if %w[SCHEMA CACHE].include?(payload[:name]) statement = query_variables(payload) check_prepared_stmt(statement, payload) name = payload[:sql] || payload[:name] || 'Default' context = Span::Context.new(statement) type = format('db.%s.sql', lookup_adapter(payload) || 'unknown').freeze [name, type, context] end
Private Instance Methods
# File lib/stackify_apm/normalizers/active_record.rb, line 110 def check_prepared_stmt(statement, payload) if StackifyRubyAPM.agent.config.prefix_enabled case get_profiler(lookup_adapter(payload)) when 'generic', 'mysql', 'sqlite', 'oracle', 'db2' check_prepared_stmt_by_placeholder(payload[:sql].include?('?'), statement, payload) when 'postgresql' check_prepared_stmt_by_placeholder(!!payload[:sql].match(/\$\d/), statement, payload) end end end
Ideally the application doesn't connect to the database during boot, but sometimes it does. In case it did, we want to empty out the connection pools so that a non-database-using process (e.g. a master process in a forking server model) doesn't retain a needless connection. If it was needed, the incremental cost of reestablishing this connection is trivial: the rest of the pool would need to be populated anyway.
Reference: github.com/rails/rails/blob/main/activerecord/lib/active_record/railtie.rb#L253
Miko: Considering we are getting the connection method, it is retrieving connection from the connection pool Connection Method: lib/active_record/connection_handling.rb#L264 Retrieve Connection: lib/active_record/connection_handling.rb#L309 Handler Retrieve Connection: lib/active_record/connection_adapters/abstract/connection_pool.rb#L1111
# File lib/stackify_apm/normalizers/active_record.rb, line 59 def lookup_adapter(payload) connection = nil if (payload.key?(:connection)) connection = payload[:connection] elsif ::ActiveRecord::Base.connection_pool.instance_variable_defined?(:@reserved_connections) and payload.key?(:connection_id) connection_id = payload[:connection_id] # Connection ID here is the object_id of the connection object connections = ::ActiveRecord::Base.connection_pool.instance_variable_get(:@reserved_connections) # Lets check the reserved connections if ( (connections.class != nil and connections.respond_to?(:class) and (connections.class.to_s == 'ThreadSafe::Cache' or connections.class.to_s == 'Hash') ) and connections.size() ) connections.each_value do |val| if val.object_id == connection_id connection = val break end end end end if (connection.nil?) return 'generic' end if (connection.respond_to?(:adapter_name) && connection.adapter_name.nil?) return 'generic' end connection.adapter_name.downcase rescue StandardError => error debug '[SqlNormalizer] lookup_adapter err: ' + error.inspect.to_s nil end
# File lib/stackify_apm/normalizers/active_record.rb, line 95 def lookup_adapter_config config = nil if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('6.1') config = ::ActiveRecord::Base.connection_db_config else config = ::ActiveRecord::Base.connection_config end if (config != nil && config.respond_to(:to_h)) config.to_h end rescue StandardError => error debug '[SqlNormalizer] lookup_adapter_config err: ' + error.inspect.to_s nil end
# File lib/stackify_apm/normalizers/active_record.rb, line 33 def query_variables(payload) adapter_config = lookup_adapter_config props = get_common_db_properties props[:PROVIDER] = get_profiler(lookup_adapter(payload)) props[:SQL] = payload[:sql] if adapter_config props[:URL] = "#{adapter_config[:host]}:#{adapter_config[:port]}" end props end