Module: ActiveRecord::QueryLogs
Relationships & Source Files | |
Namespace Children | |
Modules:
| |
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 Internal use only
- .prepend_comment rw Internal use only
- .taggings rw Internal use only
- .taggings=(taggings) rw Internal use only
- .tags rw Internal use only
- .tags=(tags) rw Internal use only
- .tags_formatter rw Internal use only
- .tags_formatter=(format) rw Internal use only
Class Method Summary
- .build_handler(name, handler = nil) private
-
.comment(connection)
private
Returns an SQL comment
::String
containing the query log tags. - .escape_sql_comment(content) private
- .rebuild_handlers 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
Class Attribute Details
.cache_query_log_tags (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 115
attr_accessor :prepend_comment, : # :nodoc:
.prepend_comment (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 115
attr_accessor :prepend_comment, : # :nodoc:
.taggings (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 114
attr_reader :, :taggings, : # :nodoc:
.taggings=(taggings) (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 117
def taggings=(taggings) # :nodoc: @taggings = taggings.freeze @handlers = rebuild_handlers end
.tags (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 114
attr_reader :, :taggings, : # :nodoc:
.tags=(tags) (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 122
def ( ) # :nodoc: @tags = .freeze @handlers = rebuild_handlers end
.tags_formatter (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 114
attr_reader :, :taggings, : # :nodoc:
.tags_formatter=(format) (rw)
# File 'activerecord/lib/active_record/query_logs.rb', line 127
def (format) # :nodoc: @formatter = case format when :legacy LegacyFormatter when :sqlcommenter SQLCommenter else raise ArgumentError, "Formatter is unsupported: #{format}" end @tags_formatter = format end
Class Method Details
.build_handler(name, handler = nil) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 180
def build_handler(name, handler = nil) handler ||= @taggings[name] if handler.nil? GetKeyHandler.new(name) elsif handler.respond_to?(:call) if handler.arity == 0 ZeroArityHandler.new(handler) else handler end else IdentityHandler.new(handler) end end
.call(sql, connection)
.clear_cache
# File 'activerecord/lib/active_record/query_logs.rb', line 151
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 197
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 213
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
.query_source_location
# File 'activerecord/lib/active_record/query_logs.rb', line 155
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
.rebuild_handlers (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 166
def rebuild_handlers handlers = [] @tags.each do |i| if i.is_a?(Hash) i.each do |k, v| handlers << [k, build_handler(k, v)] end else handlers << [i, build_handler(i)] end end handlers.sort_by! { |(key, _)| key.to_s } end
.tag_content(connection) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 226
def tag_content(connection) context = ActiveSupport::ExecutionContext.to_h context[:connection] ||= connection pairs = @handlers.filter_map do |(key, handler)| val = handler.call(context) @formatter.format(key, val) unless val.nil? end @formatter.join(pairs) end
.uncached_comment(connection) (private)
[ GitHub ]# File 'activerecord/lib/active_record/query_logs.rb', line 205
def uncached_comment(connection) content = tag_content(connection) if content.present? "/*#{escape_sql_comment(content)}*/" end end