Configure mongrel, rails logger per-port

Courtenay : November 14th, 2006

If you run a pack of mongrels you'll probably want to log per-listener, rather than dumping them into one file. This makes it more difficult to find one particular request, but easier because you can be sure that log lines are in order and consecutive. Previously the accepted (and easy way) to do this was to use the pid.

config/environments/production.rb

  config.logger = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}.#{Process.pid}.log")
.. creating files like log/production.23321.log. Assuming your mongrels are also logging their pid files as with cluster::start (mongrel.3000.log with the pid embedded) you can tail the log for a particular port by

tail -f production.`cat mongrel.3000.pid`.log 
This works well enough but if you have some leaky processes, you'll be killing them and thereby it'll be difficult to find log files. Also, it's a little difficult to type :). There are two ways to get around this. We need to find the mongrel port number somehow. Since I couldn't immediately discover the way to do this, we hacked at it until it worked. 1. Introspect the object space. Turns out there's a few Mongrel::HttpServer objects floating around.

config/environments/production.rb
  ObjectSpace.each_object(Mongrel::HttpServer) { |i| @port = i.port }
  raise "Port could not be introspected!" unless @port and @port.to_i > 0
  config.logger = Logger.new File.expand_path(RAILS_ROOT+"/log/#{ENV['RAILS_ENV']}.#{@port}.log")

  
On my app this took <5ms to run, and it only gets run once on startup. 2. Modify mongrel/rails so it exposes the port. Ezra suggested this one. (I haven't tested it) The last line is the new one. You'd add the @_port in the config string above rather than calling ObjectSpace

  mongrel/lib/mongrel/rails

      # Attempts to resolve the request as follows:
      #
      # * If the requested exact PATH_INFO exists as a file then serve it.
      # * If it exists at PATH_INFO+".html" exists then serve that.
      # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
      def process(request, response)
        if response.socket.closed?
          return
        end
        
        request.instance_variable_set "@_port", @port
(Thanks to Evan Webb, Ezra Zygmuntowicz and Wilson Bilkovich for help with this one)

5 Responses to “Configure mongrel, rails logger per-port”

  1. Tyler Kovacs Says:

    Another approach to consider – I recently released a plugin called custom_benchmarks that allows you to inject arbitrary content into the log file. With the plugin install, adding the PID to the summary line of production.log is as simple as adding one line to your application.rb:

    class ApplicationController < ActionController::Base custom_benchmark {|runtime| ” | PID: #{$$}” } ... end

    (where the process ID is available through $$)

    The resulting log line would look something like this:

    Finished JsonController#index in 0.40306 (2 reqs/sec) | Rendering: 0.05003 (12%) | DB: 0.04571 (11%) | Search: 0.16429,1 (40%) | Time: 1163542055 | memcache: 0.00023 (0%) | PID: 22426 | 200 OK [http://www.zvents.com/welcome/index]

    As you can see, I’m using the plugin to record other info like search and memcache latency.

    Check it out here if you’re interested: http://blog.zvents.com/2006/10/31/rails-plugin-custom-benchmarks

  2. Sam Aaron Says:

    Why don’t you just ask Zed nicely ;-)

  3. Bodhi Says:

    I’m a bit late to the party, but just a quick note to say thanks for this, I was looking for a way to add the port number to the filename and you solved it quite nicely.

    One thing though, the object-space solution will break script/console unless you change it to something like

     ObjectSpace.each_object(Mongrel::HttpServer) { |i| @port = i.port } if self.class.const_defined?(&#8220;Mongrel&#8221;)
    
  4. Bodhi Says:

    Oops, spoke too soon… Mongrel isn’t defined in that scope.

  5. Phil Says:

    Yeah, this will make migrations and console asplode. Here's what I'm using: http://pastie.caboo.se/169850

    This includes rotation.

Leave a Reply

I am a human (check this)

Remember: escape your underscores \_ and indent code at least 4 spaces or incur the wrath of smartypants.