我正在try 启用verbose query logging in Rails,但ActiveRecord::LogSubscriber的行为异常,这导致详细的查询日志(log)记录不起作用.

这个问题似乎起源于这里:https://github.com/rails/rails/blob/v6.0.0/activerecord/lib/active_record/log_subscriber.rb#L113

如果Iinspectlocations变量,我可以看到它does包含以下内容:

[
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/log_subscriber.rb:100:in `debug'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/log_subscriber.rb:45:in `sql'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/subscriber.rb:145:in `finish'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/log_subscriber.rb:107:in `finish'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/notifications/fanout.rb:160:in `finish'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/notifications/fanout.rb:62:in `block in finish'",
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/notifications/fanout.rb:62:in `each'",
  ...
]

但是,一旦它通过了clean方法,它就是一个空array.所以这就是:

backtrace_cleaner.clean(locations.lazy).first

返回nil.因此,line if source上的source赋值是nil,这意味着line 107上的if source的赋值结果为FALSE,并且不记录任何内容.


更新

我将这两个puts加到ActiveRecord::LogSubscriber中:

def log_query_source
  source = extract_query_source_location(caller)

  puts "-----\n-\n-\nCALLERS: #{caller.inspect}\n-\n-\n-----"

  if source
    logger.debug("  ↳ #{source}")
  end
end

def extract_query_source_location(locations)
  puts "-----\n-\n-\nCLEANER: #{backtrace_cleaner.instance_values}\n-\n-\n-----"

  backtrace_cleaner.clean(locations.lazy).first
end

以帮助收集有关实际注册为调用方的内容以及回溯清除程序为过滤和静默建立了哪些内容的一些上下文.

以下是调用者的输出(我只是包含了足够的内容来显示我们的应用程序中列出了源):

[
  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/log_subscriber.rb:100:in `debug'",
  
  ...

  "/Users/jeff/Development/lead-simple/LeadSimple/components/api/app/graph/api/loaders/association_loader.rb:47:in `preload_association'",
  "/Users/jeff/Development/lead-simple/LeadSimple/components/api/app/graph/api/loaders/association_loader.rb:34:in `perform'",

  ...

  "/Users/jeff/Development/lead-simple/LeadSimple/components/api/app/controllers/api/graphql_controller.rb:16:in `execute'",

  ...

  "/Users/jeff/Development/lead-simple/LeadSimple/components/api/lib/api/graphql_reloader.rb:18:in `call'",

  ...

  "/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.6.5/lib/puma/thread_pool.rb:147:in `block in spawn_thread'"
]

下面是回溯清除程序的输出:

{
  "silencers"=>[
    #<Proc:0x0000000108343330@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/backtrace_cleaner.rb:101>, 
    #<Proc:0x00000001083432e0@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/backtrace_cleaner.rb:105>, 
    #<Proc:0x00000001083431c8@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.0/lib/rails/backtrace_cleaner.rb:19>
  ], 
  "filters"=>[
    #<Proc:0x0000000108343380@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/backtrace_cleaner.rb:97>, 
    #<Proc:0x0000000108343240@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.0/lib/rails/backtrace_cleaner.rb:16>, 
    #<Proc:0x0000000108343218@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.0/lib/rails/backtrace_cleaner.rb:17>, 
    #<Proc:0x00000001083431f0@/Users/jeff/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.0/lib/rails/backtrace_cleaner.rb:18>
  ], 
  "root"=>"/Users/jeff/Development/lead-simple/LeadSimple/"
}

正如您所看到的,我们的应用程序代码没有任何特定的东西被静默(甚至过滤).但是,还有"root"实例值,这非常奇怪,因为如果您查看ActiveRecord::BacktraceCleanersource code,则只定义了@filters@silencers两个实例变量.

我猜这里涉及到这一点,因为将值设置为"root"的路径将删除我们的应用程序级跟踪(如果用作消音器).问题是,我不知道这是从哪里来的.

我能猜到的唯一一件事是,我们的一个gem重载了ActiveSupport::BacktraceCleaner以添加@root实例变量,并在此基础上增加了一些静默层.‘_(ツ)_/’

推荐答案

Rails控制台始终禁用详细日志(log)记录

您是通过查看Rails服务器正在记录的内容还是通过查看Rails控制台中的活动来测试详细日志(log)记录?This line将始终关闭在控制台中执行的活动的详细查询日志(log)记录:

# ActiveRecord::Railtie
console do |app|
  require "active_record/railties/console_sandbox" if app.sandbox?
  require "active_record/base"
  unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
    console = ActiveSupport::Logger.new(STDERR)
    Rails.logger.extend ActiveSupport::Logger.broadcast console
  end
  ActiveRecord::Base.verbose_query_logs = false  # <--- HERE
end

Here's the commit that added that line.提交消息提供了更多详细信息,并参考this issue.这是故意的,这样做是为了减少Rails控制台中的噪音.

如果要在Rails控制台中重新打开详细日志(log)记录,则必须使用以下命令手动重置该标志:

ActiveRecord::Base.verbose_query_logs = true

或者,你可以在应用程序的初始化器中添加这个选项,以便在控制台启动时将其打开:

# config/initializers/verbose_logging.rb
Rails.application.console do
  ActiveRecord::Base.verbose_query_logs = true
end

请注意,Rails Guide建议不要在生产中打开它,因为内存使用,所以您可能希望在该环境中关闭它,这取决于您运行生产控制台的可用资源.

在记录之前清除回溯

当进行详细的日志(log)记录时,Rails将try 删除标准的Ruby代码引用和gem引用,以便显示应用程序中的源代码位置.这是使用ActiveRecord::LogSubscriber.backtrace_cleaner中的清洗器来完成的,默认情况下,它是从ActiveSupport::BacktraceCleaner继承的Rails::BacktraceCleaner.

要测试Rails::BacktraceCleaner是否意外地清除了您的代码,您可以执行以下操作:

# Get the source location for a class method somewhere in your app
path = MyClass.method(:my_class_method).source_location.first

# Run it through the BacktraceCleaner
backtrace_cleaner = Rails::BacktraceCleaner.new
backtrace_cleaner.clean([path])

如果返回[],您可能需要判断是否将应用程序代码放入了RbConfig::CONFIG["rubylibdir"]文件夹.BacktraceCleaner将假定那里的代码是标准Ruby库的一部分,并将其从回溯中移除.

Rails::BacktraceCleaner还排除了在appconfiglibtest子目录中找不到的代码.已经做了here次了.

你的root人说它是:

/Users/jeff/Development/lead-simple/LeadSimple/

然后它下面的子文件夹是:

/Users/jeff/Development/lead-simple/LeadSimple/components/...

Rails将从回溯中清除这一点.您可以使用类似以下内容进行判断:

test = ['/Users/jeff/Development/lead-simple/LeadSimple/components/api/app/graph/api/loaders/association_loader.rb']
backtrace_cleaner = Rails::BacktraceCleaner.new

# Filtering will drop off everything before the root
filtered = backtrace_cleaner.send(:filter_backtrace, [path])
 => ["components/api/app/graph/api/loaders/association_loader.rb"]

# Silencing will drop off paths that don't start with app, config, lib, or test
backtrace_cleaner.send(:silence, filtered)
 => []

要避免这种情况,您可以:

  • 将代码组织到不同的子文件夹中,或者
  • 覆盖Rails::BacktraceCleaner,或
  • 在初始值设定项中重新定义Rails::BacktraceCleaner::APP_DIRS_PATTERN

最后一个选项是最简单的,但在应用程序启动时会抛出already initialized constant个错误.

Ruby-on-rails相关问答推荐

Rails 7.1解决冲突的Zeitwerk Inflection规则

创建大哈希的 Ruby 方法

我如何在不在 gemfile 中的 rake 任务中要求 gem?

在 Datatable Ajax 调用中通过 Ajax 更新 div

如何启动 rails 控制台并专门使用测试数据库?

如何在 to_json 中获取回形针图像的 url

Rails 4.1 Mailer 预览和设计自定义邮箱

Rails 发现获取 ActiveRecord::RecordNotFound

如何在我的 rails 应用程序中测试 ActiveRecord::RecordNotFound?

使用 Devise 令牌登录,这是内置的吗?

在公共 Rails 应用程序中将敏感数据存储在哪里?

为什么我要使用 unicorn 或 thin 而不是 WEBrick 用于开发目的?

Rails Active Admin css 与 Twitter Bootstrap css 冲突

Ruby gem 命名约定

如何在 Rails 中测试助手?

Vagrant上的Rails 4.2服务器端口转发不起作用

rake 任务中的 def 块

Ruby 1.87 与 1.92 Date.parse

您可以在弹性 beantalk 环境中运行 rails 控制台或 rake 命令吗?

简单的 rails rake 任务拒绝运行并出现错误不知道如何构建任务,为什么?