首页 > 解决方案 > 动态覆盖 Rails 中所有 Logger 输出的目标文件

问题描述

我正在将 Apartment gem 用于多租户 Rails 5.2 应用程序。我不确定这对我的问题是否重要,但只是给出一些背景信息。

有没有办法覆盖 Rails 记录器并将每个日志条目重定向到基于正在使用的租户(数据库)的文件?

思考...有没有一种方法可以在 Logger 中进行monkeypatch 来动态更改写入的文件?

示例:我希望当天的每条错误消息都指向一个文件。因此,在一周结束时,将有 7 个动态生成的文件用于每个特定日期发生的错误。

另一个例子:在你写任何服务器日志消息之前,检查它是否在下午 1 点之前。如果在下午 1 点之前将其写入 /log/before_1.log ... 如果在下午 1 点之后将其写入 /log/after_1.log

愚蠢的例子......但我想要在写入任何日志行之前进行那种动态控制。

谢谢!

标签: ruby-on-railsapartment-gem

解决方案


通常,记录器通常是为每个服务器(或每个环境)配置的,而公寓为每个请求设置租户——这意味着实际上它并不能很好地工作。

您可以通过分配Rails.logger给记录器实例来随时设置记录器。

Rails.logger = Logger.new(Rails.root.join('log/foo.log'), File::APPEND)

# or for multiple loggers
Rails.logger.extend(Logger.new(Rails.root.join('log/foo.log'), File::APPEND))

然而它并不是那么简单——你不能只是把它扔进去ApplicationController并认为一切都是笨拙的——它会被称为迟到,大多数条目都包含重要的东西,比如请求或在控制器之前弹出的任何错误将在默认日志中结束。

你可以做的是编写一个自定义的中间件来切换日志:

# app/middleware/tenant_logger.rb
class TenantLogger
  def initialize app
    @app = app
  end

  def call(env)
    file_name = "#{Appartment::Tenant.current}.log"
    Rails.logger = Logger.new(Rails.root.join('log', file_name), File::APPEND)
    @app.call(env)
  end
end

并将其挂载在中间件堆栈中的“电梯”之后:

Rails.application.config.middleware.insert_after Apartment::Elevators::Subdomain, TenantLogger

然而,由于这在中间件堆栈中非常低,您仍然会错过很多由中间件记录的重要信息,例如Rails::Rack::Logger.

按照 Rails 指南的建议,使用带有单个文件的标记记录器是一个更好的解决方案。


推荐阅读