Module: ActiveRecord::QueryLogs
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Defined in: | activerecord/lib/active_record/query_logs.rb, activerecord/lib/active_record/query_logs_formatter.rb |
Overview
Automatically append comments to SQL queries with runtime information tags. This can be used to trace troublesome SQL statements back to the application code that generated these statements.
Query logs can be enabled via Rails configuration in config/application.rb
or an initializer:
config.active_record. = true
By default the name of the application, the name and action of the controller, or the name of the job are logged. The default format is / SQLCommenter. The tags shown in a query comment can be configured via Rails configuration:
config.active_record. = [ :application, :controller, :action, :job ]
Active Record defines default tags available for use:
-
application
-
pid
-
socket
-
db_host
-
database
-
source_location
Action Controller adds default tags when loaded:
-
controller
-
action
-
namespaced_controller
Active Job adds default tags when loaded:
-
job
New comment tags can be defined by adding them in a ::Hash
to the tags ::Array
. Tags can have dynamic content by setting a Proc
or lambda value in the ::Hash
, and can reference any value stored by Rails in the context
object. ::ActiveSupport::CurrentAttributes
can be used to store application values. Tags with nil
values are omitted from the query comment.
Escaping is performed on the string returned, however untrusted user input should not be used.
Example:
config.active_record. = [
:namespaced_controller,
:action,
:job,
{
request_id: ->(context) { context[:controller]&.request&.request_id },
job_id: ->(context) { context[:job]&.job_id },
tenant_id: -> { Current.tenant&.id },
static: "value",
},
]
By default the name of the application, the name and action of the controller, or the name of the job are logged using the / SQLCommenter format. This can be changed via config.active_record.query_log_tags_format
Tag comments can be prepended to the query:
ActiveRecord::QueryLogs.prepend_comment = true
For applications where the content will not change during the lifetime of the request or job execution, the tags can be cached for reuse in every query:
config.active_record. = true
Class Attribute Summary
- .cache_query_log_tags rw
- .prepend_comment rw
- .taggings rw
- .tags rw
- .tags_formatter rw
Class Method Summary
-
.update_formatter(format)
Updates the formatter to be what the passed in format is.
-
.comment(connection)
private
Returns an SQL comment
::String
containing the query log tags. - .escape_sql_comment(content) private
- .formatter private
- .tag_content(connection) private
- .uncached_comment(connection) private
- .call(sql, connection) Internal use only
- .clear_cache Internal use only
-
.query_source_location
Internal use only
See additional method definition at line 113.
Class Attribute Details
.cache_query_log_tags (rw)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 78
mattr_accessor :, instance_accessor: false, default: false
.prepend_comment (rw)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 77
mattr_accessor :prepend_comment, instance_accessor: false, default: false
.taggings (rw)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 75
mattr_accessor :taggings, instance_accessor: false, default: {}
.tags (rw)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 76
mattr_accessor :, instance_accessor: false, default: [ :application ]
.tags_formatter (rw)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 79
mattr_accessor :, instance_accessor: false
Class Method Details
.call(sql, connection)
.clear_cache
# File 'activerecord/lib/active_record/query_logs.rb', line 95
def clear_cache # :nodoc: self.cached_comment = nil end
.comment(connection) (private)
Returns an SQL comment ::String
containing the query log tags. Sets and returns a cached comment if .cache_query_log_tags is true
.
# File 'activerecord/lib/active_record/query_logs.rb', line 131
def comment(connection) if self.cached_comment ||= uncached_comment(connection) else uncached_comment(connection) end end
.escape_sql_comment(content) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 151
def escape_sql_comment(content) # Sanitize a string to appear within a SQL comment # For compatibility, this also surrounding "/*+", "/*", and "*/" # characters, possibly with single surrounding space. # Then follows that by replacing any internal "*/" or "/ *" with # "* /" or "/ *" comment = content.to_s.dup comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "") comment.gsub!("*/", "* /") comment.gsub!("/*", "/ *") comment end
.formatter (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 139
def formatter self. || self.update_formatter(:legacy) end
.query_source_location
See additional method definition at line 113.
# File 'activerecord/lib/active_record/query_logs.rb', line 121
def query_source_location # :nodoc: Thread.each_caller_location do |location| frame = LogSubscriber.backtrace_cleaner.clean_frame(location) return frame if frame end nil end
.tag_content(connection) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 164
def tag_content(connection) context = ActiveSupport::ExecutionContext.to_h context[:connection] ||= connection pairs = .flat_map { |i| [*i] }.filter_map do |tag| key, handler = tag handler ||= taggings[key] val = if handler.nil? context[key] elsif handler.respond_to?(:call) if handler.arity == 0 handler.call else handler.call(context) end else handler end [key, val] unless val.nil? end self.formatter.format(pairs) end
.uncached_comment(connection) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 143
def uncached_comment(connection) content = tag_content(connection) if content.present? "/*#{escape_sql_comment(content)}*/" end end
.update_formatter(format)
Updates the formatter to be what the passed in format is.
# File 'activerecord/lib/active_record/query_logs.rb', line 100
def update_formatter(format) self. = case format when :legacy LegacyFormatter.new when :sqlcommenter SQLCommenter.new else raise ArgumentError, "Formatter is unsupported: #{formatter}" end end