diff --git a/cake/libs/cake_log.php b/cake/libs/cake_log.php index e956bd699..4d27071c0 100644 --- a/cake/libs/cake_log.php +++ b/cake/libs/cake_log.php @@ -54,16 +54,148 @@ */ class CakeLog { +/** + * An array of connected streams. + * Each stream represents a callable that will be called when write() is called. + * + * @var array + * @access protected + **/ + var $_streams = array(); + +/** + * Get an instance + * + * @return void + * @static + **/ + function &getInstance() { + static $instance = array(); + if (!isset($instance[0])) { + $instance[0] = new CakeLog(); + } + return $instance[0]; + } + +/** + * Configure and add a new logging stream to CakeLog + * You can use add loggers from app/libs use app.loggername, or any plugin/libs using plugin.loggername + * + * @param string $key The keyname for this logger, used to revmoe the logger later. + * @param array $config Array of configuration information for the logger + * @return boolean success of configuration. + * @static + **/ + function config($key, $config) { + if (empty($config['engine'])) { + trigger_error(__('Missing logger classname', true), E_USER_WARNING); + return false; + } + $self = CakeLog::getInstance(); + $className = $self->_getLogger($config['engine']); + if (!$className) { + return false; + } + unset($config['engine']); + $self->_streams[$key] = new $className($config); + return true; + } + +/** + * Attempts to import a logger class from the various paths it could be on. + * Checks that the logger class implements a write method as well. + * + * @return mixed boolean false on any failures, string of classname to use if search was successful.\ + * @access protected + **/ + function _getLogger($loggerName) { + $plugin = null; + if (strpos($loggerName, '.') !== false) { + list($plugin, $loggerName) = explode('.', $loggerName); + } + if ($plugin) { + App::import('Lib', $plugin . '.log/' . $loggerName); + } else { + if (!App::import('Lib', 'log/' . $loggerName)) { + App::import('Core', 'log/' . $loggerName); + } + } + if (!class_exists($loggerName)) { + trigger_error(sprintf(__('Could not load logger class %s', true), $loggerName), E_USER_WARNING); + return false; + } + if (!method_exists($loggerName, 'write')) { + trigger_error( + sprintf(__('logger class %s does not implement a write method.', true), $loggerName), + E_USER_WARNING + ); + return false; + } + return $loggerName; + } + +/** + * Returns the keynames of the currently active streams + * + * @return array + * @static + **/ + function streams() { + $self = CakeLog::getInstance(); + return array_keys($self->_streams); + } + +/** + * Remove a stream from the active streams. Once a stream has been removed + * it will no longer be called. + * + * @param string $keyname Key name of callable to remove. + * @return void + * @static + **/ + function remove($streamName) { + $self = CakeLog::getInstance(); + unset($self->_streams[$streamName]); + } + +/** + * Add a stream the logger. + * Streams represent destinations for log messages. Each stream can connect to + * a different resource /interface and capture/write output to that source. + * + * @param string $key Keyname of config. + * @param array $config Array of config information for the LogStream + * @return boolean success + * @static + **/ + function addStream($key, $config) { + $self = CakeLog::getInstance(); + $self->_streams[$key] = $config; + } + +/** + * Configures the automatic/default stream a FileLog. + * + * @return void + * @access protected + **/ + function _autoConfig() { + if (!class_exists('FileLog')) { + App::import('Core', 'log/FileLog'); + } + $this->_streams['default'] = new FileLog(array('path' => LOGS)); + } + /** * Writes given message to a log file in the logs directory. * * @param string $type Type of log, becomes part of the log's filename - * @param string $msg Message to log + * @param string $message Message to log * @return boolean Success * @access public * @static */ - function write($type, $msg) { + function write($type, $message) { if (!defined('LOG_ERROR')) { define('LOG_ERROR', 2); } @@ -82,19 +214,16 @@ class CakeLog { if (is_int($type) && isset($levels[$type])) { $type = $levels[$type]; } - - if ($type == 'error' || $type == 'warning') { - $filename = LOGS . 'error.log'; - } elseif (in_array($type, $levels)) { - $filename = LOGS . 'debug.log'; - } else { - $filename = LOGS . $type . '.log'; + $self = CakeLog::getInstance(); + if (empty($self->_streams)) { + $self->_autoConfig(); } - $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $msg . "\n"; - $log = new File($filename, true); - if ($log->writable()) { - return $log->append($output); + $keys = array_keys($self->_streams); + foreach ($keys as $key) { + $logger =& $self->_streams[$key]; + $logger->write($type, $message); } + return true; } /** diff --git a/cake/libs/log/file_log.php b/cake/libs/log/file_log.php new file mode 100644 index 000000000..483f1dbb8 --- /dev/null +++ b/cake/libs/log/file_log.php @@ -0,0 +1,76 @@ + LOGS); + $this->_path = $options['path']; + } + +/** + * Implements writing to log files. + * + * @param string $type The type of log you are making. + * @param string $message The message you want to log. + * @return boolean success of write. + **/ + function write($type, $message) { + $debugTypes = array('notice', 'info', 'debug'); + + if ($type == 'error' || $type == 'warning') { + $filename = $this->_path . 'error.log'; + } elseif (in_array($type, $debugTypes)) { + $filename = $this->_path . 'debug.log'; + } else { + $filename = $this->_path . $type . '.log'; + } + $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n"; + $log = new File($filename, true); + if ($log->writable()) { + return $log->append($output); + } + } +} \ No newline at end of file diff --git a/cake/libs/object.php b/cake/libs/object.php index bdf7a81be..86e6c59aa 100644 --- a/cake/libs/object.php +++ b/cake/libs/object.php @@ -1,6 +1,4 @@ _log)) { - $this->_log = new CakeLog(); - } if (!is_string($msg)) { $msg = print_r($msg, true); } - return $this->_log->write($type, $msg); + return CakeLog::write($type, $msg); } /** diff --git a/cake/tests/cases/libs/cake_log.test.php b/cake/tests/cases/libs/cake_log.test.php index e9465881b..a899f7826 100644 --- a/cake/tests/cases/libs/cake_log.test.php +++ b/cake/tests/cases/libs/cake_log.test.php @@ -2,8 +2,6 @@ /** * CakeLogTest file * - * Long description for file - * * PHP versions 4 and 5 * * CakePHP(tm) Tests @@ -20,6 +18,7 @@ * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License */ App::import('Core', 'Log'); +App::import('Core', 'log/FileLog'); /** * CakeLogTest class @@ -29,6 +28,98 @@ App::import('Core', 'Log'); */ class CakeLogTest extends CakeTestCase { +/** + * Start test callback, clears all streams enabled. + * + * @return void + **/ + function startTest() { + $streams = CakeLog::streams(); + foreach ($streams as $stream) { + CakeLog::remove($stream); + } + } + +/** + * test importing loggers from app/libs and plugins. + * + * @return void + **/ + function testImportingLoggers() { + App::build(array( + 'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS), + 'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS) + ), true); + + $result = CakeLog::config('libtest', array( + 'engine' => 'TestAppLog' + )); + $this->assertTrue($result); + $this->assertEqual(CakeLog::streams(), array('libtest')); + + $result = CakeLog::config('plugintest', array( + 'engine' => 'TestPlugin.TestPluginLog' + )); + $this->assertTrue($result); + $this->assertEqual(CakeLog::streams(), array('libtest', 'plugintest')); + + App::build(); + } + +/** + * test all the errors from failed logger imports + * + * @return void + **/ + function testImportingLoggerFailure() { + $this->expectError('Missing logger classname'); + CakeLog::config('fail', array()); + + $this->expectError('Could not load logger class born to fail'); + CakeLog::config('fail', array('engine' => 'born to fail')); + + $this->expectError('logger class stdClass does not implement a write method.'); + CakeLog::config('fail', array('engine' => 'stdClass')); + } + +/** + * Test that CakeLog autoconfigures itself to use a FileLogger with the LOGS dir. + * When no streams are there. + * + * @return void + **/ + function testAutoConfig() { + @unlink(LOGS . 'error.log'); + CakeLog::write(LOG_WARNING, 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'error.log')); + + $result = CakeLog::streams(); + $this->assertEqual($result, array('default')); + unlink(LOGS . 'error.log'); + } + +/** + * test configuring log streams + * + * @return void + **/ + function testConfig() { + CakeLog::config('file', array( + 'engine' => 'FileLog', + 'path' => LOGS + )); + $result = CakeLog::streams(); + $this->assertEqual($result, array('file')); + + @unlink(LOGS . 'error.log'); + CakeLog::write(LOG_WARNING, 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'error.log')); + + $result = file_get_contents(LOGS . 'error.log'); + $this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning/', $result); + unlink(LOGS . 'error.log'); + } + /** * testLogFileWriting method * @@ -37,7 +128,8 @@ class CakeLogTest extends CakeTestCase { */ function testLogFileWriting() { @unlink(LOGS . 'error.log'); - CakeLog::write(LOG_WARNING, 'Test warning'); + $result = CakeLog::write(LOG_WARNING, 'Test warning'); + $this->assertTrue($result); $this->assertTrue(file_exists(LOGS . 'error.log')); unlink(LOGS . 'error.log'); @@ -65,7 +157,7 @@ class CakeLogTest extends CakeTestCase { $result = file(LOGS . 'debug.log'); $this->assertEqual(count($result), 1); $this->assertPattern( - '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Notice: Notice \(8\): Undefined variable: out in \[.+ line \d{2}\]$/', + '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Notice: Notice \(8\): Undefined variable: out in \[.+ line \d+\]$/', $result[0] ); @unlink(LOGS . 'debug.log'); diff --git a/cake/tests/cases/libs/log/file_log.test.php b/cake/tests/cases/libs/log/file_log.test.php new file mode 100644 index 000000000..7f757acac --- /dev/null +++ b/cake/tests/cases/libs/log/file_log.test.php @@ -0,0 +1,82 @@ + + * Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.log + * @since CakePHP(tm) v 1.3 + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +require_once LIBS . 'log' . DS . 'file_log.php'; + +/** + * CakeLogTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class FileLogTest extends CakeTestCase { + +/** + * testLogFileWriting method + * + * @access public + * @return void + */ + function testLogFileWriting() { + @unlink(LOGS . 'error.log'); + $log =& new FileLog(); + $log->write('warning', 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'error.log')); + + $result = file_get_contents(LOGS . 'error.log'); + $this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning/', $result); + unlink(LOGS . 'error.log'); + + @unlink(LOGS . 'debug.log'); + $log->write('debug', 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'debug.log')); + + $result = file_get_contents(LOGS . 'debug.log'); + $this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Debug: Test warning/', $result); + unlink(LOGS . 'debug.log'); + + @unlink(LOGS . 'random.log'); + $log->write('random', 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'random.log')); + + $result = file_get_contents(LOGS . 'random.log'); + $this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Random: Test warning/', $result); + unlink(LOGS . 'random.log'); + } + +/** + * test using the path setting to write logs in other places. + * + * @return void + **/ + function testPathSetting() { + $path = TMP . 'tests' . DS; + @unlink($path . 'error.log'); + + $log =& new FileLog(compact('path')); + $log->write('warning', 'Test warning'); + $this->assertTrue(file_exists($path . 'error.log')); + unlink($path . 'error.log'); + } + +} +?> \ No newline at end of file diff --git a/cake/tests/test_app/libs/library.php b/cake/tests/test_app/libs/library.php index a96886392..48155d61e 100644 --- a/cake/tests/test_app/libs/library.php +++ b/cake/tests/test_app/libs/library.php @@ -1,11 +1,7 @@ @@ -18,10 +14,7 @@ * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests * @package cake * @subpackage cake.tests.cases.libs - * @since CakePHP(tm) v 1.2.0.5432 - * @version $Rev$ - * @modifiedby $LastChangedBy$ - * @lastmodified $Date$ + * @since CakePHP(tm) v 1.3 * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License */ class Library {} diff --git a/cake/tests/test_app/libs/log/test_app_log.php b/cake/tests/test_app/libs/log/test_app_log.php new file mode 100644 index 000000000..3b9331cbb --- /dev/null +++ b/cake/tests/test_app/libs/log/test_app_log.php @@ -0,0 +1,25 @@ + + * Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.3 + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +class TestAppLog { + + function write($type, $message) { + + } +} \ No newline at end of file diff --git a/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php b/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php new file mode 100644 index 000000000..e67fe06d7 --- /dev/null +++ b/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php @@ -0,0 +1,25 @@ + + * Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.3 + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +class TestPluginLog { + + function write($type, $message) { + + } +} \ No newline at end of file