Logging activity within a Rails application

I wanted to add a tool to log users’ activity in my current application. I wanted something simple, and decided that the solution was to combine two techniques:

One: making session data available at the model and observer level. An article by Geoff Lane gave me the information I needed to be able to achieve that.

Two: use an observer to catch Model call backs and trigger an action in response. The Rails api documentation gave me the information I needed on creating an observer.

Here is a simple example as to how I was able to add an activity logger using these two techniques.

First I created a new module:

module MakeUserAvailableViaThread
  def user_in_thread
    Thread.current[:user]
  end

  def self.user_in_thread=(user)
    Thread.current[:user] = user
  end
end

This was based on Geoff Lane’s UserInfo module. I made two changes: I used a more descriptive module name; and I changed the method name because current_user was already used in the controller by my authentication system (‘restful_authentication’). The later stopped me logging in successfully until I spotted the problem.

I then added these two lines to my ApplicationController:

  include MakeUserAvailableViaThread
  before_filter :pass_current_user_to_thread_where_it_can_be_seen_by_observer

Together with this private method:

  def pass_current_user_to_thread_where_it_can_be_seen_by_observer
    MakeUserAvailableViaThread.user_in_thread = self.current_user
  end

So very similar to Geoff Lane’s code except with more descriptive method names.

I then needed to add the observer:

class ActivityLogger < ActiveRecord::Observer
  observe :account, :license
 
  include MakeUserAvailableViaThread

  def after_update(record)
    puts("HEY! something changed: #{user_in_thread.full_name if user_in_thread} altered #{record.class.to_s} #{record.id}")
  end

  def after_create(record)
    puts("HEY! something changed: #{user_in_thread.full_name if user_in_thread} created #{record.class.to_s} #{record.id}")
  end
end

However, nothing happened until I added this line to config/environment.rb:

config.active_record.observers = :activity_logger

Note that config.active_record.observers only gets called at application start, so you may need to stop and start your application before the observer starts working.

I now have a system I can extend to log activity. As shown above, only changes to accounts and licenses are logged, and the log just outputs to the console at the moment. However, now I have the mechanism, it is easy to create a new ActivityLog model and save the log entries to that in whatever format I fancy.

This entry was posted in Ruby. Bookmark the permalink.