Top Topic ?

Logging into CouchDb using Zend Framework

Posted in php by Ladislav Prskavec on September 10, 2010

Logging in web applications are common task. Mostly i’m using logging into files. When you can some statistics data is better use database for logs. You can use RBMS (MySQL, PostgreSQL) but is problem with changing schema. NoSQL database as CouchDb can be solution. Here is my implementation in Zend Framework.

For writing into database i use log writer:

class App_Log_Writer_CouchDb extends Zend_Log_Writer_Abstract
{
    /**
     * Db
     * @var Phly_Couch
     */
    private $_db;
    /**
     * CouchDb host localhost default
     * @var string
     */
    private $_host;
    /**
     * Couchdb port 3184 default
     * @var int
     */
    private $_port;
    /**
     *
     * @param array $params
     * @return void
     */
    public function __construct($dbname, $options = null)
    {
        if (is_null($options)) {
            $options['host'] = "localhost";
            $options['port'] = "5984";
            $options['db'] = $dbname;
        }
        $this->_db = new Phly_Couch($options);
    }

    static public function factory($config)
    {
        $config = self::_parseConfig($config);
        return $config;
    }
    /**
     * @param array $event
     * @return void
     */
    protected function _write($event)
    {
        // action body
        $doc = new Phly_Couch_Document($event);
        $result = $this->_db->docSave($doc);
        // return array ok, id, rev
        $info = $result->getInfo();
        if (!$info['ok']) {
            throw new Zend_Log_Exception("Error in save to CouchDb");
        }
    }

}

In application ini have to set libraries and options for connect to CouchDb database, you can make yours on http://couchone.com or use local instalation. More info for instalation you can find in Couch Db wiki.

autoloaderNamespaces.phly = "Phly_"
autoloaderNamespaces.app = "App_"
; Options for CouchDb
couchdb.host = prskavec.couchone.com
couchdb.port = 80
couchdb.db = "test-log"

in indexController i have two action, indexAction for display logs and logAction for creating some dummy log records.

class IndexController extends Zend_Controller_Action
{
    protected $_config;

    public function preDispatch()  
    {
            $this->_config = new Zend_Config_Ini('../application/configs/'.
                    'application.ini', APPLICATION_ENV);
    }

    public function indexAction()
    {
         $db = new Phly_Couch($this->_config->couchdb);

         $this->view->form = $form = new App_Form_Filter();
         if ($this->getRequest()->isPost()) {
             $formData = $this->getRequest()->getPost();
             if ($form->isValid($formData)) {
                 $filterValue = $form->getValue('filter');
                 $dateFrom = $form->getValue('datefrom');
                 $dateTo = $form->getValue('dateto');
                 if (empty($filterValue) || $filterValue===0) $filterValue = null;
                 if (empty($dateFrom)) $dateFrom = null;
                 if (empty($dateTo)) $dataTo = null;
                 if (!is_null($dateFrom) && !is_null($dateTo)) {
                 $result = $db->view('logger','log_by_timestamp', array($dateFrom, $dateTo), array("db"=>$this->_config->couchdb->db));
                 } else {
                 $result = $db->view('logger','log_by_prior', $filterValue, array("db"=>$this->_config->couchdb->db));
                 }

             } else {
             $form->populate($formData);
             }
         } else {
            $result = $db->view('logger','log_by_prior', null, array("db"=>$this->_config->couchdb->db));         
         }         
         $this->view->docs = $result->toArray();
         $this->view->messages = $this->_helper->flashMessenger->getMessages();
        
         $logger = new Zend_Log();
         $r = new ReflectionClass($logger);
         $this->view->priorities = array_flip($r->getConstants());

    }

    public function logAction()
    {
          $id = $this->_request->getParam('id', 0);  

          $logger = new Zend_Log();
          $format = '%timestamp% %priorityName% (%priority%): '.
            '[%module%] [%controller%] %message%';                                                                                                
          $formatter = new Zend_Log_Formatter_Simple($format);
          
          $writer = new App_Log_Writer_CouchDb($this->_config->couchdb->db, $this->_config->couchdb);
          $writer->setFormatter($formatter);

          $logger->addWriter($writer);
          $logger->setEventItem('module', $this->getRequest()->getModuleName());
          $logger->setEventItem('controller', $this->getRequest()->getControllerName());                        
          $logger->log("Testovani chyba", $id);
          $this->_helper->flashMessenger->addMessage('Log item saved');
          $this->_helper->redirector('index');
    }


}

In controller I call view in CouchDb database and this use parametr log level or time interval for display logs. For manage views in CouchDb can use Futon.

In controller i create two views.

First for priority, save as: logger/log_by_prior

function(doc) {
  if (doc.priority) {
     emit(doc.priority, [doc.priorityName, doc.timestamp, doc.message, doc.module, doc.controller]);
  }
}

Second for timestamp, save as: logger/log_by_timestamp

function(doc) {
  if (doc.timestamp) {
     emit(doc.timestamp, [doc.priorityName, doc.message, doc.module, doc.controller]);
  }
}

Whole example you can find on my github. In example using CouchDb library make by Matthew Weier O’Phinney and I make some improvement for actual version CouchDb 1.0.1.

I think this is useful solution for many applications.

Tagged with: