【问题标题】:Redirect logger output for a specific controller in Rails 4在 Rails 4 中为特定控制器重定向记录器输出
【发布时间】:2016-04-28 10:37:14
【问题描述】:

我已经根据上一个问题 Redirect logger output for a specific controller in Rails 3 中的答案为 Rails 3 构建了一个解决方案。它工作得很好但是现在我正在尝试将相同的基于中间件的解决方案应用于 Rails 4 项目,但仍然存在一些差异相同的解决方案。

Rails 3 解决方案:

module MyApp
  class LoggerMiddleware

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

    def initialize(app)
      @app = app
      @logger = 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

在上面:

Rails 3 getter for Rails.logger = Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log)

Rails 3 setter 用于 Rails.logger(设置为 @my_logger)= Rails::logger.instance_variable_get(:@logger).instance_variable_set(:@log,@my_logger)

我马上注意到的一件事是,在 Rails 4 中,上面的 Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log) 返回 nil。

检查 Rails 4 控制台,我确实看到 Rails.instance_variable_get(:@logger) 返回 #<ActiveSupport::Logger:0x007f84ff503a08 ....>

我尝试将 getter 替换为 Rails.instance_variable_get(:@logger) 并将 setter 替换为 Rails.instance_variable_set(:@logger,my_logger) 并且它似乎几乎可以工作。活动的第一部分,“已开始...”进入新的日志文件,但之后的所有内容都进入默认日志文件(中间件更改之前的 Rails.logger)。

Rails.instance_variable_get(:@logger) 不是 Rails 4 中与 Rails 3 Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log) 对应的最低级别,用于获取/设置 Rails.logger,或者在我之后的过程中稍后会有其他东西设置后覆盖它的中间件。

有什么线索吗?

更新:

为了澄清,上面发布的解决方案在 Rails 3 中按预期工作。它在特殊环境中可能存在或不存在的任何限制(例如,如果是这种情况,解决方案可能无法在线程服务器环境中工作)都可以此时并没有遇到障碍,因此这些相同的限制在 Rails 4 解决方案中也可以解决这个问题。

【问题讨论】:

  • Afaik 相同的记录器实例在线程之间共享,因此如果您使用线程服务器,或者希望将来有机会这样做,请确保在处理期间不要切换并行请求的输出的“特殊控制器”(Puma + C Ruby 线程现在是首选 Heroku 部署策略,因此争论不仅仅是学术性的)
  • 嗨@bbozo,感谢您的评论。目前我们并没有故意对线程服务器做任何事情。是否有可能默认情况下在某处设置而我不知道?如果我们决定按照您所说的那样实现“线程服务器”,您会建议对这种方法进行哪些修改以便获得相同的结果?
  • 嗨@Streamline,Rails 4 应用程序默认是线程化的。这一变化是在 Rails 4 中引入的。在 Rails 3 中,您必须使用config.threadsafe! 显式启用多线程支持。我假设您的 Rails 4 应用程序运行并发线程,这些线程以不确定的方式覆盖 Rails.logger 设置。这可以解释它在一段时间后停止工作。

标签: ruby-on-rails ruby-on-rails-4 logging rack


【解决方案1】:

在对 Rails 代码进行了一天的修改之后,我想我已经找到了一个hack-ish 解决您的问题的方法。

您需要编辑call方法和initialize方法如下:

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

def call(env)
  if env['PATH_INFO'] =~ /api\/v.*\/reports.*/
    Rails.instance_variable_set(:@logger, @reports_api_controller_logger)
    ActionController::Base.logger = @reports_api_controller_logger
    ActiveRecord::Base.logger = @reports_api_controller_logger
    ActionView::Base.logger = @reports_api_controller_logger
  else
    Rails.instance_variable_set(:@logger, @logger)
    ActionController::Base.logger = @logger
    ActiveRecord::Base.logger = @logger
    ActionView::Base.logger = @logger
  end
  @app.call(env)
end

这确实是一个 hack-ish 解决方案,并根据您的需要修改正则表达式。

如果这对你不起作用,请告诉我。

【讨论】:

  • 完美运行!! :D
  • 嗨@jagiot-singh,看起来很有希望,因为我在几分钟前对我的Rails 4 应用程序进行了有限设置的简短测试,它似乎正在工作。我想知道任何人都可以解释为什么在 Rails 4 中我们似乎需要从 Rails 3 倒退一步,并且需要分别设置每个 Base 记录器而不是全部使用与 Rails 3 中相同的记录器?或者是否还有一个地方它都从同一个来源继承而我们只是还没有找到它?本周晚些时候,当我有机会完整设置我的 Rails 4 应用程序时,我将进行更多调查,看看它是否按预期工作。
  • @Streamline Rails 3 默认继承了 Log4r 或默认的 Ruby 记录器,但它与 Rails 4 不同。Rails 4 在M(ActiveRecord)V(ActionView)C(ActionController) 中的每个部分都有不同的记录器。我尝试覆盖 ActiveSupport::LogSubscriber 的记录器,它似乎被上述所有类继承。但这种方法并不成功。因此,我手动修改了每个记录器以实现结果。
  • 要保留标签,请使用@reports_api_controller_logger = ActiveSupport::TaggedLogging.new(Logger.newRails.root.join('log', REPORTS_API_CONTROLLER_LOGFILE),10, 1000000)))
【解决方案2】:

我不确定您是否需要像在 Rails 3 中那样采用相同的方法来解决问题。看来可能还有其他方法可以实现您的最终目标。您是否考虑过这些方法或宝石?

https://github.com/TwP/logging

How to log something in Rails in an independent log file?

有时尝试与您已经做过的完全不同的事情可能会有所帮助,恕我直言。

这有点猜测,但这可能是您的 getter setter 无法按预期工作的原因:https://github.com/rails/rails/commit/6329d9fa8b2f86a178151be264cccdb805bfaaac

关于 Jagjot 的解决方案以及需要为每个 MVC 操作类设置基本日志,Rails 4 默认情况下单独设置这些,这似乎提供了更多开箱即用的灵活性。 http://guides.rubyonrails.org/configuring.html#initializers

【讨论】:

    【解决方案3】:

    为 1 个控制器使用不同的日志记录类的方法有一个缺点,它不能在当今流行的线程服务器上工作,不仅仅是 JRuby,MRI 也是 thanks to Heroku

    经过一周的思考,我目前唯一的想法是将日志通过管道传输到 syslog 并使用 syslog 的工具将它们拆分为单独的文件。

    它仍然需要修补 Logger 以包含允许拆分的字符串(例如将来自 #caller 跟踪的格式化行添加到日志文件中)。

    B 计划是引入我自己的记录器,并在该控制器中简单地记录我需要的内容。例如,如果足够的话,您可以轻松转储参数和响应

    【讨论】:

    • 嗨@bbozo,据我所知,您所描述的处理线程服务器的问题还不是我面临的问题。在这一点上,对于这个特定的 SO 问题,我希望能够在 Rails 4 中做我们今天已经在我们的服务器上的 Rails 3 中做的同样的事情,不管线程服务器可能带来的新问题,除非你说这个线程服务器是 Rails 4 的一个组成部分,独立于我们的服务器设置,它不是我们在 Rails 3 中看到的......?我们不使用 Heroku/Puma。我们在 Docker 容器中的 Ubuntu 服务器上使用 Nginx/Passenger。
    • 上面提到的您的 B 计划,您将如何执行“介绍我自己的记录器”部分,因为无论线程的差异如何,这似乎是我面临的挑战的一个可能(尽管更加激烈)的解决方案服务器是否是一个问题。你会通过删除 Rails::Rack::Logger 并在堆栈中插入你自己的版本来用你自己的中间件替换 Rails::Rack::Logger 中间件吗?是否可以只修补 Rails::Rack::Logger 而不用完全不同的东西替换整个中间件,以便更新可以与您的更改一起使用?
    • 不,我只是在初始化脚本中打开一个文件并在控制器中输出到它:)
    • 我在 Rails 代码中挖掘了一下,什么也没找到,我等了一周才得到其他答案,但什么也没来,所以我猜那里没有太多选择
    猜你喜欢
    • 2015-05-03
    • 2019-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多