如何使 rails 库中的登录像在模型和控制器中一样工作

How to make logging in rails libs works like in models and controllers

到目前为止,我一直使用“puts”将自定义日志记录信息添加到我的代码中。但现在有点痛苦。例如,当我 运行 rspec 时,我对用 puts 添加的所有冗长内容都不感兴趣。所以我安装了“logging and logging-rails gem”,因为它的安装速度非常快,令人满意。

当我从模型和控制器调用记录器时效果很好,但当我在库中使用记录器时效果不佳。我得到那个错误:NameError - undefined local variable or method `logger' for CustomLib:Class.

我成功的最简单的事情是调用 'Rails.logger' 而不是 'logger'。但是在我的日志文件中,引用该行的 class 将是 'Rails' 但我想要 'CustomLib'。对于模型和控制器,显示正确的 class 名称,无需我自己的任何干预。

  # config/environnement/test.rb

  # Set the logging destination(s)
  config.log_to = %w[stdout]
  config.log_level = :info

  # Show the logging configuration on STDOUT
  config.show_log_configuration = false
  # lib/custom_lib.rb
  class CustomLib
    def initialize
      Rails.logger.info 'foo'
    end
  end

当我使用或测试我的自定义库时 class 我会得到: [2019-06-21T16:26:41] 信息 Rails : foo

相反,我希望看到: [2019-06-21T16:26:41] 信息自定义库:foo

我有点迷失在 rails 中的所有日志法力 gem 中,我不知道接下来要尝试什么才能实现该目标...

编辑

当我在“logger.info 'foo'”行之前放置一个 byebug 并通过 'step' 进入它时,如果我是否在 model/controller 或自定义库。

# In custom lib, step enters this file "gems/logging-2.2.2/lib/logging/logger.rb"
# And Rails.logger returns an object like this one beloow
Logging::Logger:0x000055a2182f8f40
  @name="Rails",
  @parent=#<Logging::RootLogger:0x000055a2182e7ee8 
    @name="root",
    @level=1>,

# In model/controller, step enters this file "gems/logging-rails-0.6.0/lib/logging/rails/mixin.rb"
# And Rails.logger returns an object like this one beloow
Logging::Logger:0x0000557aed75d7b8
  @name="Controller",
  @parent=#<Logging::RootLogger:0x0000557aedfcf630
    @name="root",
    @level=0>,

日志行中的 Railsthe progname of the logger。您可以创建一个新的记录器,将 progname 设置为 'CustomLib'with (say) Rails.logger.clone.tap {|l| l.progname = 'CustomLib' },但这并不是 progname 的真正目的,它是指定 程序,不是class.

的名称

您可以改为在日志行中包含 class 名称:

Rails.logger.info "[#{self.class}] - some message"

或者,您可以更努力地定义自己的格式化程序。这是一个包装现有记录器的便捷方法:

class ClassNameFormatter
  def self.wrap_logger(klass, logger)
    logger.clone.tap { |l| l.formatter = new(klass, logger.formatter) }
  end

  def initialize(klass, formatter=nil)
    @klass = klass
    @formatter = formatter ||= Logger::Formatter.new
  end

  def call(severity, timestamp, progname, msg)
    @formatter.call severity, timestamp, progname, "[#{@klass.name}] - #{msg}"
  end
end

使用方法:

class CustomLib
  def initialize
    @logger = ClassNameFormatter.wrap_logger self.class, Rails.logger
  end

  def call
    @logger.info 'test log please ignore'
  end
end

最后我找到了一个更好的方法,我只需要在我想要该行为的模块顶部包含 Logging.globally:

# lib/custom_lib.rb
class CustomLib
  include Logging.globally
 
  def initialize
    logger.info 'foo'
  end
end