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

时间:2021-07-13 22:01:15

We want to have a collection of controllers where we route logger output from all actions and downstream methods to a separate log file. This is a Rails 3 project. In Rails 2 we did this by redefining the "logger" method but in Rails 3, the way to log is using "Rails.logger". I tried putting

我们希望有一组控制器,我们将记录器输出从所有操作和下游方法路由到单独的日志文件。这是一个Rails 3项目。在Rails 2中,我们通过重新定义“logger”方法来实现这一点,但在Rails 3中,记录的方式是使用“Rails.logger”。我试过推杆

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

at the top of the controller, but only the specific cases where Rails.logger is used specifically in the action is getting sent to the specified log file, all of the default log output for the controller is still routing to the main log file.

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

How can we route all log output for a specific controller to a specific log file to include all default controller output?

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

By default controller output, I am referring to all of the messages that start with the very beginning of the inbound request

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

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"}}

and all log output that might follow from the inbound request and outbound response in the controller, etc.

以及控制器中的入站请求和出站响应可能跟随的所有日志输出。

Basically I want all logging for the reports controller to be in the reports_controller.log and I don't want any messages for traffic to the reports controller to show up in the main log (i.e. production.log if in production)

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

Update:

Thanks to @mudasobwa's help with his answer and chat, I was able to solve this using middleware as his answer depicts (although I had to change my insert_before to be before Rails::Rack::Logger)

感谢@ mudasobwa对他的回答和聊天的帮助,我能够使用中间件解决这个问题,因为他的答案描述了(尽管我必须将我的insert_before更改为Rails :: Rack :: Logger之前)

the revised answer from him that solved it for me is below and it lives in config/initializers/logger_middleware.rb

他解决了我的修改后的答案如下,它存在于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

3 个解决方案

#1


3  

The reason that not all the stuff is redirected by controller filter, is that these “Started...” etc are written by rack middleware, that is executed before the controller was even instantiated.

不是所有的东西都被控制器过滤器重定向的原因是这些“Started ......”等是由机架中间件编写的,这是在控制器被实例化之前执行的。

So, to grab and redirect everything related to some condition, one should interfere deeper. Below is a [possibly incomplete] example on how to hack into the pipeline.

因此,要抓住并重定向与某些条件相关的所有内容,应该更深入地干涉。以下是关于如何入侵管道的[可能不完整]示例。

Define the middleware to switch loggers

定义中间件以切换记录器

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

Add an initializer somewhere in config/initializers/my_logger.rb

在config / initializers / my_logger.rb中的某处添加初始化程序

Rails.application.middleware.insert_before \
    Rails::Rack::Logger, MyApp::MyMiddleware

Please note, that Rails’ logger is a nested beast:

请注意,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=...

One might want to set a specific formatter on the logger, or even filter messages using regular expression there (though it should not be considered a good practice.)

有人可能想在记录器上设置特定的格式化程序,甚至在那里使用正则表达式过滤消息(尽管它不应被视为一种好的做法。)

#2


5  

Have you tried prepending an around_filter?

你有没有尝试过pre_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

#3


0  

To add on top of @messanjah's answer, you can overwrite these loggers: Rails::logger, ActionView::Base.logger, ActionController::Base.logger, ActiveRecord::Base.logger.

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

By suppressing these loggers you get rid of most messages. But as @mudasobwa pointed Started ... messages are written before controller.

通过抑制这些记录器,您可以摆脱大多数消息。但正如@mudasobwa指出的那样......消息是在控制器之前写的。

If you don't want to use a middleware you can always use the hammer and monkeypatch Rails::Rack::Logger to use your logger aplication wide. Or you can check for path and reject certain messages there.

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

#1


3  

The reason that not all the stuff is redirected by controller filter, is that these “Started...” etc are written by rack middleware, that is executed before the controller was even instantiated.

不是所有的东西都被控制器过滤器重定向的原因是这些“Started ......”等是由机架中间件编写的,这是在控制器被实例化之前执行的。

So, to grab and redirect everything related to some condition, one should interfere deeper. Below is a [possibly incomplete] example on how to hack into the pipeline.

因此,要抓住并重定向与某些条件相关的所有内容,应该更深入地干涉。以下是关于如何入侵管道的[可能不完整]示例。

Define the middleware to switch loggers

定义中间件以切换记录器

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

Add an initializer somewhere in config/initializers/my_logger.rb

在config / initializers / my_logger.rb中的某处添加初始化程序

Rails.application.middleware.insert_before \
    Rails::Rack::Logger, MyApp::MyMiddleware

Please note, that Rails’ logger is a nested beast:

请注意,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=...

One might want to set a specific formatter on the logger, or even filter messages using regular expression there (though it should not be considered a good practice.)

有人可能想在记录器上设置特定的格式化程序,甚至在那里使用正则表达式过滤消息(尽管它不应被视为一种好的做法。)

#2


5  

Have you tried prepending an around_filter?

你有没有尝试过pre_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

#3


0  

To add on top of @messanjah's answer, you can overwrite these loggers: Rails::logger, ActionView::Base.logger, ActionController::Base.logger, ActiveRecord::Base.logger.

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

By suppressing these loggers you get rid of most messages. But as @mudasobwa pointed Started ... messages are written before controller.

通过抑制这些记录器,您可以摆脱大多数消息。但正如@mudasobwa指出的那样......消息是在控制器之前写的。

If you don't want to use a middleware you can always use the hammer and monkeypatch Rails::Rack::Logger to use your logger aplication wide. Or you can check for path and reject certain messages there.

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