I’ve been playing a lot with CakePHP lately, so here’s yet another article on it. For one of my latest projects I was interested in logging authentication results (using the Auth component) into a database table. Perhaps not the most suitable way of logging, but since it’s for a low volume back-end, it was a quick and dirty working solution.
There is some coverage on this topic in the Cookbook, but it’s far from complete. So, for any people who are looking to achieve the same, I’ll tell you how I managed to get it working in the end.
First of all, we need to create a database table that will hold the log lines. In my case, I just simply followed the Cake naming conventions and created a table called logs, with some fields to hold the info:
CREATE TABLE IF NOT EXISTS `logs` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `type` VARCHAR(20) NOT NULL, `time` datetime NOT NULL, `message` text NOT NULL, PRIMARY KEY (`id`) );
Next, we need to create the Log model. Since there won’t be any user input into the logs table, a very simple empty model will be sufficient for our needs. No need to specify relations or validation rules. Just create an app/models/log.php file like this:
<?php class Log extends AppModel { var $name = 'Log'; } ?>
Now that the table and model are in place, we need to create a class that will save the data into our table. As stated in the book, you can add this class under app/libs/log. I called my file database_logger.php. In order to write log entries, we at least need to have a write function which takes the type and message as variables. This is required when extending the CakeLog class, this is also explained in the book. Here’s how the class looks:
<?php class DatabaseLogger { function __construct() { App::import('Model', 'Log'); $this->Log = new Log; } function write($type, $message) { $log['type'] = ucfirst($type); $log['time'] = date('Y-m-d H:i:s'); $log['message'] = $message; return $this->Log->save($log); } } ?>
Now, the important bit here that is not very well documented in the Cookbook is the class constructor. In here we import our Log model and initiate it for later use in the write function. The write function itself is pretty straight-forward. It takes the required type and message as parameters and with that it builds a data array $log, that we will then save to our Log model. This is done on the last line of the function.
Finally, to start using the DatabaseLogger class instead of using CakeLog’s default log files, we need to adjust our application’s bootstrap. This can be found under app/config/bootstrap.php. You should add the following lines to it:
CakeLog::config('otherFile', array( 'engine' => 'DatabaseLogger' ));
This will load our class into the application. In order to then write a log entry to the database, you can call CakeLog::write from your controller action. In my case I added the following to my UsersController login function:
if(!empty($this->data)) { if ($this->Auth->user()) { CakeLog::write('info', 'User ' . $this->Auth->user('username') . ' successfully logged in. (' . $_SERVER['REMOTE_ADDR'] . ')'); $this->redirect(array('controller' => 'dashboards', 'action' => 'index')); } else { CakeLog::write('info', 'User ' . $this->data['User']['username'] . ' failed to login. (' . $_SERVER['REMOTE_ADDR'] . ')'); } }
Please note that in this case, because we want to execute an action after logging in, it is necessary to add the below line to the controller’s beforeFilter function, to prevent the Auth component from automatically redirecting before saving the log line.
$this->Auth->autoRedirect = false;
Hi Jan,
Thank you for sharing this. Your tutorial is clear and of course, works!
Only 1 thing I wonder is, Can we log more than 1 record in the log table at a time?
I have tried, in the controller,
CakeLog::write('log1','hello world');
CakeLog::write('log2','hello another world');
CakeLog::write('log3','hello the 3rd world');
and when I go the logs table, only the last one (log3) was inserted into the database table.
Multiple records logging works fine when I use file-based logging.
any ideas?
Cheers,
I know this post is a little older but I have the same problem than ArmNo and found this with the google search.
Is there a solution to log multiple messages after one another???
Hope someone reads this
-flosky
Hi guys,
Basically it’s just a regular save operation, so if you want to log multiple rows after each other, just modify the write method to call create() before saving. So something like this (untested) should do the trick: