title: How to create a full Audit log in Rails for every table?
body: <p>We recently began a compliance push at our company and are required to keep a full history of changes to our data which is currently managed in a Rails application.  We've been given the OK to simply push something descriptive for every action to a log file, which is a fairly unobtrusive way to go.</p>
  
  <p>My inclination is to do something like this in <code>ApplicationController</code>:</p>
  
  <pre><code>around_filter :set_logger_username
  
  def set_logger_username
    Thread.current["username"] = current_user.login || "guest"
    yield
    Thread.current["username"] = nil
  end
  </code></pre>
  
  <p>Then create an observer that looks something like this:</p>
  
  <pre><code>class AuditObserver &lt; ActiveRecord::Observer
    observe ... #all models that need to be observed
  
    def after_create(auditable)
      AUDIT_LOG.info "[#{username}][ADD][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
    end
  
    def before_update(auditable)
      AUDIT_LOG.info "[#{username}][MOD][#{auditable.class.name}][#{auditable.id}]:#{auditable.changed.inspect}"
    end
  
    def before_destroy(auditable)
      AUDIT_LOG.info "[#{username}][DEL][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
    end
  
    def username
      (Thread.current['username'] || "UNKNOWN").ljust(30)
    end
  end
  </code></pre>
  
  <p>and in general this works <em>great</em>, but it fails when using the "magic" <code>&lt;association&gt;_ids</code> method that is tacked to has_many :through => associations.</p>
  
  <p>For instance:</p>
  
  <pre><code># model
  class MyModel
    has_many :runway_models, :dependent =&gt; :destroy
    has_many :runways, :through =&gt; :runway_models
  end
  
  #controller
  class MyModelController &lt; ApplicationController
  
    # ...
  
    # params =&gt; {:my_model =&gt; {:runways_ids =&gt; ['1', '2', '3', '5', '8']}}
  
    def update
      respond_to do |format|
        if @my_model.update_attributes(params[:my_model])
          flash[:notice] = 'My Model was successfully updated.'
          format.html { redirect_to(@my_model) }
          format.xml  { head :ok }
        else
          format.html { render :action =&gt; "edit" }
          format.xml  { render :xml =&gt; @my_model.errors, :status =&gt; :unprocessable_entity }
        end
      end
    end
  
    # ...
  end
  </code></pre>
  
  <p>This will end up triggering the <code>after_create</code> when new <code>Runway</code> records are associated, but will not trigger the <code>before_destroy</code> when a <code>RunwayModel</code> is deleted.</p>
  
  <p>My question is...
     Is there a way to make it work so that it will observe those changes (and/or potentially other deletes)?<br>
     Is there a better solution that is still relatively unobtrusive? </p>