I've built a solution based on the answer in my previous question Redirect logger output for a specific controller in Rails 3 for Rails 3. It works great however now I am trying to apply the same middleware based solution to a Rails 4 project but there are some differences keeping the same solution from working.
The Rails 3 solution:
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
in the above:
Rails 3 getter for Rails.logger = Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log)
Rails 3 setter for Rails.logger (setting to @my_logger) = Rails::logger.instance_variable_get(:@logger).instance_variable_set(:@log,@my_logger)
One thing I've noticed right away is that in Rails 4 the above Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log)
returns nil.
Checking in Rails 4 console, I do see that Rails.instance_variable_get(:@logger)
returns #<ActiveSupport::Logger:0x007f84ff503a08 ....>
I've tried replacing the getter with Rails.instance_variable_get(:@logger)
and the setter with Rails.instance_variable_set(:@logger,my_logger)
and it almost seems to work. The first part of the activity, the "Started..." goes to the new log file but everything after that goes to the default log file (the Rails.logger before the middleware changed it).
Either Rails.instance_variable_get(:@logger)
is not the lowest level equivalent in Rails 4 to the Rails 3 Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log)
for getting/setting the Rails.logger or there is something else later on in the process after my middleware that is overwriting this after I set it.
Any clues?
Update:
To clarify, the solution posted above works as expected in Rails 3. Any limitations it may or may not have in special environments (such as if the solution may not work in threading server environments if that is the case) are ok at this point and not experienced as obstacles at this time therefore these same limitations are also ok in a Rails 4 solution for this question.
The approach of using a different logging class for 1 controller has the downside that it won't work on a threading server which is in fashion these days not just with JRuby, but MRI too thanks to Heroku
Only idea I have so far after giving it a week of thought is to pipe the logs to syslog and use syslog's facilities to split them into separate files.
It would still require patching the
Logger
to include strings that would allow for the splitting (such as adding a formatted lined of from the#caller
trace to the log files).Plan B would be to introduce my own logger and simply log what I need in that controller. You could easily dump params and response for example if that's enough
After hacking around the Rails code for a day, I think I have found a hack-ish solution to your problem.
You need to edit the
call
method andinitialize
method as follow:This is really a hack-ish solution and modify the regex according to your needs.
Tell me if this doesn't work for you.
I'm not sure if you need to approach the solution in the same way as you had done in Rails 3. It seems there may be other ways to accomplish your end goal. Have you considered any of these approaches or gems?
https://github.com/TwP/logging
How to log something in Rails in an independent log file?
Sometimes trying something completely different than what you've already done can be helpful IMHO.
This is a bit of guess, but this may be why your getter setters are not working as expected: https://github.com/rails/rails/commit/6329d9fa8b2f86a178151be264cccdb805bfaaac
Regarding Jagjot's solution and needing to set base log for each of the MVC Action Classes, Rails 4 sets these separately by default which would in tern seem to provide more flexibility out of the box. http://guides.rubyonrails.org/configuring.html#initializers