Hoopla!

now with extra whiz-bang!

Hoopla!

Exempt Rails Controllers from logging; A refactoring

June 07, 2008 · 1 comment

I’ve got a couple Rails controllers that just serve static content like graphics and script assets. In production the content is all cached so it bypasses the controller but when I’m running locally I’ve got pages and pages of garbage filling up my development log.

The first attempt at silencing the whining controllers used an around_filter that looked something like this:
1
2
3
4
5
6
7
8
9

class GraphicsController < ApplicationController
  around_filter :no_logging

  protected
    def no_logging
      logger.silence { yield }
    end
end

which is great, compact, easy to encapsulate, etc. But it doesn’t work. Debugging told me I needed to use a different logger object and put this code in a different place. I share this next piece of code not out of pride but as an example that I, like Adam Keys, am a student of the art of abysmal failure. Here’s my original implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# in ./lib/exempt_controllers_from_logging_entirely.rb

module ExemptControllersFromLoggingEntirely
  
  @@controllers_to_exclude = %w(graphics styles scripts)
  
  def self.included(base)
    base.class_eval do
      
      def process_with_no_logging(request, *args)
        if @@controllers_to_exclude.any? {|controller| request.parameters[:controller] == controller }
          logger.silence { process_without_no_logging(request, *args) }
        else
          process_without_no_logging(request, *args)
        end
      end
      
      alias_method_chain :process, :no_logging
      
    end
  end
end

ActionController::Base.send :include, ExemptControllersFromLoggingEntirely

Yay! It’s HUGE! And it uses all the unnecessary complexities of alias_method_chain and self.included and, oh joy, the controllers that use it are specified RIGHT IN THE CODE.

Sadly, this code worked fine. Ruby lacks an ugly-code warning system that tells me when I’m writing something unwieldly.

Here’s the refactored version:
1
2
3
4
5
6
7
8
9
10
11

# in ./lib/exempted_from_logging.rb
module ExemptedFromLogging
  def process(request, *args)
    logger.silence { super }
  end
end
## Usage:
## class GraphicsController < ApplicationController
##   include ExemptedFromLogging
## end

Yeah.

Lesson?

  • If you’re just overwriting a single method like this put it in a module that calls super
  • If you’re bypassing a method entirely then you can think about aliasing or overwriting the original
  • Avoid using self.included and self.inherited unless you’ve got something complex to do like adding both instance and class methods to an object
  • Remember to revisit your old code
  • Blog your failures for fun and profit humility

Tags:·······

1 response so far ↓

  • 1 Ted // Jun 07, 2008 at 05:39 PM

    you might wanna consider triggering that wrapper based on environment, or an environmental flag – it might be an expensive operation in a production environment. complete speculation, but something to consider.

Leave a Comment