Creating an Audit Log in Rails
Someone just asked how to track changes via an audit log in Rails. They wanted more than just model observers however—they wanted to track the logged in user and some other session related data. I recently did something just like this.
Create an AuditLog model with the following schema:
create_table "audit_logs", :force => true do |t| t.string "author" t.string "summary" t.text "changes" t.datetime "created_at" t.datetime "updated_at" end
Setup the AuditLog model as below. This simply adds some code to not log a record if nothing was actually changed (convenient if your show view is also your edit view and your users hit ‘update’ to go back).
class AuditLog < ActiveRecord::Base attr_accessor :force serialize :changes before_create :skip_unchanged def skip_unchanged return false if read_attribute(:changes).blank? && force != true return true end private :skip_unchanged end
Create the TrackChanges module and put it into RAILS_ROOT/lib/track_changes.rb:
module TrackChanges def self.included(base) base.after_save :track_changes end # # # def track_changes @tracked_changes = self.changes.reject{|k,v| ['created_at', 'updated_at'].include?(k.to_s)} end private :track_changes # # # def tracked_changes @tracked_changes end end
In all of the models that you want to track, put the following line after any existing before filters:
include TrackChanges # must follow any before filters
Now, anywhere you want (in a controller’s create() or update() methods, in a method called via script/runner) you could do something like this (pretend we’re in a update method):
#... @logged_in_user = User.find(....) @something = Something.find(params[:id]) if @something.update_attributes(params[:something]) AuditLog.create(:author => @logged_in_user.login, :summary => "created some thing '#{@something.name}'", :changes => @something.tracked_changes) #... end #...
One nice thing about not tying the audit log directly to a user (although you certainly could) is that system scripts can set an author of “system” or some other moniker that clearly indicates it’s a system process.
All that’s left is to build out a UI to review the records and purge old ones…