I've seen a lot of people asking about the logging facilities in Yii and thought I'd share a nice little class I wrote that provides near real-time logging.
This is really handy while you're developing a site as sometimes, as you probably know, when certain fatal errors occur, Yii pukes and doesn't record the buffered log entries.
My design of this class utilizes the error_log function in PHP to write to the log file. I figured that it must be pretty optimized since it's written in C and is a core PHP function.
So, the class is called CPSLiveLogRoute and is part of my Yii Extension library but has no dependencies on my extensions. You can use it in any project. Change the name if you'd like, I don't care.
To use this class:
- Copy the class to your protected/extensions or protected/components directory
- Import the class if you're not already including the destination in your imports section.
- Add a log route for it in your configuration file:
'log' => array(
'class' => 'CLogRouter',
'routes' => array(
array(
'class' => 'CPSLiveLogRoute',
'levels' => 'error, warning, info, trace',
'maxFileSize' => '10240',
'logFile' => 'my_app_log_file_name',
// Optional excluded category
'excludeCategories' => array(
'system.db.CDbCommand',
),
),
),
),
- Enjoy!
Better Formatting ¶
I change the output format of the logging through this component as well. While the Yii stock logging is adequate, this format is easier on the eyes and let's you pull out what you're looking for more easily.
The new format is:
Mmm dd hh:mm:ss [category fixed 30 chars] : <L> LOG_ENTRY
Where:
Mmm dd hh:mm:ss looks like this: Feb 11 12:34:56
and <L>
is the first letter of the log level (E,W,I, or T).
Here's an example:
Feb 07 15:00:02 [system.CModule ] : <T> Loading "log" application component
Feb 07 15:00:02 [system.CModule ] : <T> Loading "coreMessages" application component
Feb 07 15:00:02 [system.CModule ] : <T> Loading "request" application component
Feb 07 15:00:02 [system.CModule ] : <T> Loading "db" application component
Feb 07 15:00:02 [system.db.CDbConnection ] : <T> Opening DB connection
Excluding Categories ¶
One feature I added was category exclusion. I like to use [Yii::trace()] for informational debug logging. However, when turning on trace level output, you get a lot of crap that, frankly, doesn't really do anything but prolong the time it takes for me to find relevant entries.
Excluding these categories (like system.db.[CDbCommand]) is simple with this log router. You can configure the excluded categories in your configuration file or at runtime.
The class exposes a property called excludeCategories
and is defined as an array. It will take literal strings or regular expressions (regex patterns must be enclosed in slashes (i.e. /^pattern$/) to match. It checks the literal first, then the regex for performance.
CPSLiveLogRoute.php ¶
And, without further ado, here is the class.
<?php
/**
* This file is part of the psYiiExtensions package.
*
* @copyright Copyright © 2009-2011 Pogostick, LLC
* @link http://www.pogostick.com Pogostick, LLC.
* @license http://www.pogostick.com/licensing
* @package psYiiExtensions
* @subpackage logging
* @filesource
* @version $Id$
*/
/**
* CPSLiveLogRoute utilizes PHP's {@link error_log} function to write logs in real time
*
* @author Jerry Ablan <jablan@pogostick.com>
* @since v1.1.0
*/
class CPSLiveLogRoute extends CFileLogRoute
{
//********************************************************************************
//* Private Members
//********************************************************************************
/**
* @property array $excludeCategories An array of categories to exclude from logging. Regex pattern matching is supported via {@link preg_match}
*/
protected $_excludeCategories = array();
public function getExcludeCategories() { return $this->_excludeCategories; }
public function setExcludeCategories( $value ) { $this->_excludeCategories = $value; }
//********************************************************************************
//* Public Methods
//********************************************************************************
/**
* Initialize component
*/
public function init()
{
parent::init();
// Write each line out to disk
Yii::getLogger()->autoFlush = 1;
}
/**
* Retrieves filtered log messages from logger for further processing.
* @param CLogger $logger logger instance
* @param boolean $processLogs whether to process the logs after they are collected from the logger. ALWAYS TRUE NOW!
*/
public function collectLogs( $logger, $processLogs = false /* ignored */ )
{
parent::collectLogs( $logger, true );
}
//********************************************************************************
//* Private Methods
//********************************************************************************
/**
* Writes log messages in files.
* @param array $logs list of log messages
*/
protected function processLogs( $logs = array() )
{
try
{
$_logFile = $this->getLogPath() . DIRECTORY_SEPARATOR . $this->getLogFile();
if ( @filesize( $_logFile ) > $this->getMaxFileSize() * 1024 )
$this->rotateFiles();
// Write out the log entries
foreach ( $logs as $_log )
{
$_exclude = false;
// Check out the exclusions
if ( ! empty( $this->_excludeCategories ) )
{
foreach ( $this->_excludeCategories as $_category )
{
// If found, we skip
if ( trim( strtolower( $_category ) ) == trim( strtolower( $_log[2] ) ) )
{
$_exclude = true;
break;
}
// Check for regex
if ( '/' == $_category[0] && 0 != @preg_match( $_category, $_log[2] ) )
{
$_exclude = true;
break;
}
}
}
/**
* Use {@link error_log} facility to write out log entry
*/
if ( ! $_exclude )
error_log( $this->formatLogMessage( $_log[0], $_log[1], $_log[2], $_log[3] ), 3, $_logFile );
}
// Processed, clear!
$this->logs = null;
}
catch ( Exception $_ex )
{
error_log( __METHOD__ . ': Exception processing application logs: ' . $_ex->getMessage() );
}
}
/**
* Formats a log message given different fields.
* @param string $message message content
* @param integer $level message level
* @param string $category message category
* @param integer $time timestamp
* @return string formatted message
*/
protected function formatLogMessage( $message, $level = 'I', $category = null, $time = null )
{
if ( null === $time )
$time = time();
$level = strtoupper( $level[0] );
return @date( 'M d H:i:s', $time ) . ' [' . sprintf( '%-30s', $category ) . '] ' . ': <' . $level . '> ' . $message . PHP_EOL;
}
}
Thanks
Hi,
Thanks for this extension. I tried to do some sort of real time logging myself but got stuck somehow. So this comes in quite handy . :-)
Btw: works fine in 1.1.12
Thanks
It's simple. Just add rows below in top of your config file:
// realtime logging Yii::getLogger()->autoDump = true; Yii::getLogger()->autoFlush = 1; return array( //...Here config data );
Just what I needed!
Thank you!
This is just what I needed for my shell commands.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.