Database Logging in Ruby on Rails

Rohit Sharma 2010-08-19
Database Logging in Ruby on Rails

Hello Friends,

Logging is a very important feature if you are creating an applicaton which will be accessed and edited by multiple users. Basically logging can be done in the following two ways:

  1. Log files
  2. Database Logging

and both of these mechanisms are supported by Rails. If you want to do logging using the first mechanism, you can simply use the Logger class. Also this mechanism is quite easy to implement. As the details get stored in the default environment’s .log file. You can also change the file in which you want to store the logs. You can refer the documentation of the Logger class.

Well anyways I am going to show you the other way around. Let’s see how do we log the changes made to the system in a database. First of all, we need a model, in which we will save the changes that take place in the system. You can create the model by using the command:-

ruby script/generate model log_data

this will generate some files as below:

exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/log_data.rb
create  test/unit/log_data_test.rb
create  test/fixtures/log_datas.yml
exists  db/migrate
create  db/migrate/20100819114624_create_log_datas.rb

As we need to log data for the complete application we need a polymorphic association between this model and all the rest of the models in the app directory. You can do this by taking the help of polymorphic associations.

And now, as the final step of our preparation phase, we need to declare a method in the log_data model which will actually store the data in the database. Well, I have tracked the following things

  1. user id
  2. action that is being performed
  3. new content in json format
  4. previous content in json format

You can also store the IP address of the machine from where the data is being altered, the time for which the user was logged in and many more as per your requirements. You can create the method as below:

class LogData  < ActiveRecord::Base
    def self.start_logging(current_user, data, action, new_content ={}, old_content ={})
        # data => the model which is being created, updated or deleted
        @log_data = data.log_datas.build(:user_id => user, :action => action, :new_content => new_content.to_json, : old_content => old_content.to_json)
        @log_data.save
    end
end

And now you just need to call this method. In each of your create, delete and update actions. In your book_controller.rb, create and destroy action you can use this piece of code:-

class BooksController < ApplicationController
    def create
        @book = Book.new(params[:book])
        if @book.save
            LogData.start_logging(current_user,@book,params[:action],params[:book],"No previous content")
        end
    end

    def destroy
        @book = Book.find(params[:id])
        LogData.start_logging(current_user,@book,params[:action],"Content Deleted",@book)
        @book.destroy
    end
end

And for the update action we might need the previous content as well as the new content. This can be done in the model by using the before_update callback like this in your book.rb.

class Book < ActiveRecord::Base
    before_save :log_data

    def log_data
        book = self.class.find(:first, self.id)
        if book.attributes != self.attributes
            LogData.start_logging(User.current,self,"update",self.attributes,event.attributes)
        end
    end
end

You can get the User.current by using the Thread class provided by Rails. A detailed version of this is available here.

And now you are all set to log your user activity in the database instead of using the primitive log files mechanism. If anyone has any suggestions, those people are heartly welcome. Hope you found this article helpful.