Rails 3 中特定控制器的重定向记录器输出

Redirect logger output for a specific controller in Rails 3

我们想要一个控制器集合,我们将所有操作和下游方法的记录器输出路由到一个单独的日志文件。这是一个 Rails 3 项目。在 Rails 2 中,我们通过重新定义 "logger" 方法来做到这一点,但在 Rails 3 中,记录的方式是使用 "Rails.logger"。我试着把

Rails::logger = Logger.new(File.join(Rails.root, 'log', "reports_controller.log"), 10, 1000000) 

在控制器的顶部,但只有特定情况下 Rails.logger 在操作中专门使用才会发送到指定的日志文件,控制器的所有默认日志输出仍然是路由到主日志文件。

我们如何将特定控制器的所有日志输出路由到特定日志文件以包括所有默认控制器输出?

默认控制器输出,我指的是所有从入站请求开始的消息

Started POST "/api/v1/reports.json" for 10.XXX.XX.XX at 2015-03-07 01:30:22 +0000
Processing by Api::V1::ReportsController#create as JSON
  Parameters: {"report"=>{"cloud_file_path"=>"report.zip", "company_id"=>nil, "created_at"=>"2015-03-07T01:30:17Z", "type_id"=>"2", "updated_at"=>"2015-03-07T01:30:17Z", "master"=>"1"}}

以及控制器中的入站请求和出站响应等可能遵循的所有日志输出

基本上,我希望报告控制器的所有日志记录都在 reports_controller.log 中,并且我不希望报告控制器的任何流量消息显示在主日志中(即 production.log 如果在生产中)

更新:

感谢@mudasobwa 的回答和聊天帮助,我能够使用中间件解决这个问题,正如他的回答所描述的那样(尽管我不得不将我的 insert_before 更改为 Rails::Rack::Logger 之前)

下面是他为我解决的修改后的答案,它位于 config/initializers/logger_middleware.rb

module MyApp
  class LoggerMiddleware

    REPORTS_API_CONTROLLER_PATH = %r|\A/api/v.*/reports/.*|
    REPORTS_API_CONTROLLER_LOGFILE = "reports_controller.log"

    def initialize(app)
      @app, @logger = app, Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log)
      @reports_api_controller_logger = Logger.new(Rails.root.join('log', REPORTS_API_CONTROLLER_LOGFILE), 10, 1000000)
    end

    def call(env)
      Rails::logger
           .instance_variable_get(:@logger)
           .instance_variable_set(:@log,
               case env['PATH_INFO']
               when REPORTS_API_CONTROLLER_PATH then
                 @reports_api_controller_logger
               else
                 @logger
               end
           )
      @app.call(env)
    end
  end
end

Rails.application.middleware.insert_before Rails::Rack::Logger, MyApp::LoggerMiddleware

您是否尝试过在前面加上 around_filter?

class MyController < ApplicationController
  prepend_around_filter :set_logger

  private

  def set_logger
    old_logger = Rails::logger
    Rails::logger = Logger.new(Rails.root.join('log', "reports_controller.log"), 10, 1000000) 
    yield
    Rails.logger = old_logger
  end
end

之所以不是所有的东西都被controller filter重定向,是因为这些“Started...”等是由rack middleware写的,被执行控制器甚至被实例化之前。

因此,要抓取并重定向一切与某种情况相关的内容,应该进行更深入的干预。下面是一个关于如何侵入管道的 [可能不完整] 示例。

定义切换记录器的中间件

module MyApp
  class MyMiddleware

    def initialize(app)
      @app, @logger = app, Rails.logger
                                .instance_variable_get(:@logger)
                                .instance_variable_get(:@log)
      @my_logger = Logger.new('reports_controller.log', ...)
    end

    def call(env)
      # env['action_dispatch.logger'].inspect
      #⇒ <TaggedLogging... @logger=#<BufferedLogger...> @log_dest=...>

      # here we do not have a controller name
      Rails.logger
           .instance_variable_get(:@logger)
           .instance_variable_set(:@log,
               case env['PATH_INFO'] # or PATH_INFO, or whatever
               when %r|\A/api/v1/| then @my_logger
               else @logger
               end
           )

      @app.call(env)
    end
  end
end

config/initializers/my_logger.rb

的某处添加初始化器
Rails.application.middleware.insert_before \
    Rails::Rack::Logger, MyApp::MyMiddleware

请注意,Rails' 记录器是一个嵌套的野兽:

Rails::logger
#⇒ #<ActiveSupport::TaggedLogging:0x000000020f5ad0 @logger=...
Rails::logger.instance_variable_get(:@logger)
#⇒ #<ActiveSupport::BufferedLogger:0x000000020f6188 @log_dest=...
Rails::logger.instance_variable_get(:@logger)
             .instance_variable_get(:@log)
#⇒ #<Logger:0x000000020f6138 @progname=nil, @level=0, @default_formatter=...

人们可能想要在记录器上设置一个特定的 formatter,甚至 在那里使用正则表达式过滤消息(尽管这不应该被认为是一种好的做法。 )

要在@messanjah 的回答之上添加,您可以覆盖这些记录器: Rails::logger, ActionView::Base.logger, ActionController::Base.logger, ActiveRecord::Base.logger.

通过抑制这些记录器,您可以删除大部分消息。但正如@mudasobwa 指出的 Started ... 消息是在控制器之前写入的。

如果您不想使用中间件,您可以随时使用 hammer 和 monkeypatch Rails::Rack::Logger 来广泛使用您的记录器应用程序。或者您可以检查路径并在那里拒绝某些消息。