Redirect logger output for a specific controller in Rails 3

后端 未结 3 1032
日久生厌
日久生厌 2021-02-09 14:40

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

相关标签:
3条回答
  • 2021-02-09 14:59

    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.

    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

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

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

    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.)

    0 讨论(0)
  • 2021-02-09 15:04

    To add on top of @messanjah's answer, you can overwrite these loggers: 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.

    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.

    0 讨论(0)
  • 2021-02-09 15:10

    Have you tried prepending an 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
    
    0 讨论(0)
提交回复
热议问题