module ActiveRecord::QueryLogs

Active Record Query Logs

Automatically tag SQL queries with runtime information.

Default tags available for use:

_Action Controller and Active Job tags are also defined when used in Rails:_

The tags used in a query can be configured directly:

ActiveRecord::QueryLogs.tags = [ :application, :controller, :action, :job ]

or via Rails configuration:

config.active_record.query_log_tags = [ :application, :controller, :action, :job ]

To add new comment tags, add a hash to the tags array containing the keys and values you want to add to the comment. Dynamic content can be created by setting a proc or lambda value in a hash, and can reference any value stored in the context object.

Escaping is performed on the string returned, however untrusted user input should not be used.

Example:

tags = [
  :application,
  {
    custom_tag: ->(context) { context[:controller]&.controller_name },
    custom_value: -> { Custom.value },
  }
]
ActiveRecord::QueryLogs.tags = tags

The QueryLogs context can be manipulated via the ActiveSupport::ExecutionContext.set method.

Temporary updates limited to the execution of a block:

ActiveSupport::ExecutionContext.set(foo: Bar.new) do
  posts = Post.all
end

Direct updates to a context value:

ActiveSupport::ExecutionContext[:foo] = Bar.new

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:

ActiveRecord::QueryLogs.cache_query_log_tags = true

This option can be set during application configuration or in a Rails initializer:

config.active_record.cache_query_log_tags = true

Private Class Methods

comment() click to toggle source

Returns an SQL comment String containing the query log tags. Sets and returns a cached comment if cache_query_log_tags is true.

# File lib/active_record/query_logs.rb, line 98
def comment
  if cache_query_log_tags
    self.cached_comment ||= uncached_comment
  else
    uncached_comment
  end
end
escape_sql_comment(content) click to toggle source
# File lib/active_record/query_logs.rb, line 113
def escape_sql_comment(content)
  # Sanitize a string to appear within a SQL comment
  # For compatibility, this also surrounding "/*+", "/*", and "*/"
  # charcacters, 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
tag_content() click to toggle source
# File lib/active_record/query_logs.rb, line 126
def tag_content
  context = ActiveSupport::ExecutionContext.to_h

  tags.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.join(",")
end
uncached_comment() click to toggle source
# File lib/active_record/query_logs.rb, line 106
def uncached_comment
  content = tag_content
  if content.present?
    "/*#{escape_sql_comment(content)}*/"
  end
end