Перенаправить вывод регистратора для конкретного контроллера в Rails 4

Я построил решение, основанное на ответе в предыдущем вопросе Перенаправить вывод журнала для конкретного контроллера в 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 для 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 ....>

Я попытался заменить геттер на 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, или есть что-то еще в этом процессе после моего промежуточного программного обеспечения, которое перезаписывает это после того, как я установите его.

Любые подсказки?

Update:

Чтобы уточнить, решение, опубликованное выше, работает как ожидается в Rails 3. Любые ограничения, которые он может или не может иметь в специальных средах (например, если решение может не работать в средах с серверами потоков, если это так), в порядке этот момент и не испытывал в это время препятствия, поэтому эти же ограничения также одобрены в решении Rails 4 для этого вопроса.

Ответы

Ответ 1

После взлома кода Rails в течение дня, я думаю, что нашел решение хак-иша для вашей проблемы.

Вам нужно отредактировать метод 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 и модифицирует регулярное выражение в соответствии с вашими потребностями.

Скажите, если это не сработает для вас.

Ответ 2

Я не уверен, что вам нужно подойти к решению так же, как в Rails 3. Кажется, что могут быть другие способы достижения конечной цели. Вы рассматривали любой из этих подходов или драгоценных камней?

https://github.com/TwP/logging

Как зарегистрировать что-то в Rails в независимом файле журнала?

Иногда попробовать что-то совершенно иное, чем то, что вы уже сделали, может быть полезно ИМХО.

Это немного догадаться, но это может быть поэтому, почему ваши настройки getter работают не так, как ожидалось: https://github.com/rails/rails/commit/6329d9fa8b2f86a178151be264cccdb805bfaaac

В отношении решения Jagjot и необходимости устанавливать базовый журнал для каждого из классов действий MVC, Rails 4 устанавливает их отдельно по умолчанию, что, по-видимому, обеспечит большую гибкость из коробки. http://guides.rubyonrails.org/configuring.html#initializers

Ответ 3

Подход к использованию другого класса ведения журнала для 1 контроллера имеет недостаток, что он не будет работать на потоковом сервере, который в моде в эти дни не только с JRuby, но и с MRI благодаря Heroku

Единственная идея, которую я имею до сих пор, давая ему неделю размышлений, - это подключить журналы к syslog и использовать средства syslog, чтобы разделить их на отдельные файлы.

По-прежнему потребуется патч Logger включить строки, которые позволят расщепить (например, добавление отформатированных из трассировки #caller в файлы журнала).

План B будет представлять мой собственный регистратор и просто записывать то, что мне нужно в этом контроллере. Вы можете легко сбросить параметры и ответ, например, если это достаточно