Using Mongodb for CodeIgniter Logs

May 18, 2010

Anyone who has ever worked in a production environment knows that your users will find errors you have not even dreamed of. They will find your broken links, they will find your missing files, and yes they will find every database connectivity issue you hoped would never occur. Thankfully we are all great developers and sysadmins so we log everything we possibly can to find and remove these errors.

This is great at first but after a while log files become enormous and eat up lots of disk space. The problem multiplies when you start getting into a clustered environment (more than 1 server). You now have to read & maintain logs on 2+ machines which will most likely lead to neglecting your error fixing duties. Fortunately MongoDB recently hit the scene.

MongoDB

MongoDB is a scalable, high-performance, open source, document-oriented database. It's ability to accept huge amounts of data very quickly make it perfect for application logging. It has built in support for auto-sharding & replication so when your application takes off mongodb will scale with you. It uses a JSON based storage system meaning you can give the database an object (from any programming language with support) and when you pull it back out of the database it will be the exact same as when you put it in. No conversion, no hassle, no bullshit.

Capped Collections

Mongo uses things called Collections which are essentially tables in mysql. The main difference is these collections are schemaless. I can have 10 fields in one document (row) and 500 in another and they will play nice inside the collection. This allows a huge amount of flexibility to the developer.

These collections also have the ability to be "Capped". This means I can set a max document count of 5000 and only the most recent 5000 documents will be kept. The older documents are deleted as new ones come in. This is awesome for logging because you will never have to rotate your logs in fear that they will become too large and occupy all of your file system space.

Use this command to cap a collection (in a mongo shell):

db.createCollection("my_collection", {capped:true, max:5000})

More Information on Capped Collections

Use in CodeIgniter

By default CodeIgniter writes to log files in /system/logs/ . I have created a class that overwrites that default functionality and sends all logs to a mongo db collection. Adding this library to all of your servers will allow all of those servers to write to the same mongo collection. I have also added some more data to the logs that is helpful in a clustered environment such as the server name, server ip, request uri, and a few more. Just place the MY_Log.php in your application/libraries directory and codeigniter should handle the rest.

You can view and fork the class on Github: https://github.com/tomschlick/codeigniter-mongo-logs/

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class MY_Log extends CI_Log
{
    var $mongo;
    
    
    function __construct()
    {
        $this->mongo = new Mongo("localhost");
        parent::CI_Log();
    }
    
    function write_log($level = 'error', $msg, $php_error = FALSE)
    {       
        if ($this->_enabled === FALSE)
        {
            return FALSE;
        }
    
        $level = strtoupper($level);
        
        if ( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
        {
            return FALSE;
        }
        
        $db = $this->mongo->logs->system_codeigniter;
        $output = $db->insert(array(
        
        // Server Info
        'server_name'   => $_SERVER['SERVER_NAME'],
        'server_ip'     => (isset($_SERVER['SERVER_ADDR'])) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0',
        'domain'        => (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : '',
        
        //User Info
        'user_agent'    => (!empty($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT'] : '',
        'ip_address'    => (!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : '',
        'uri'           => (!empty($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '',
        'query_string'  => (!empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '',
        
        'timestamp'     => date($this->_date_fmt),
        'message'       => $msg,
        'level'         => $level,
        ));
        
                
        return $output;
    }

}
/* End of file MY_Log.php */
/* Location: ./application/libraries/MY_Log.php */

Feel free to modify data inserted to fit your specific needs. If you find any bugs or have suggestions you can comment below or raise an issue here.

Monitor your DNS Zones with ZoneWatcher

Be alerted of DNS record changes moments after they happen, not from upset customers.

ZoneWatcher screenshot