浏览代码

change to match syslog levels & customizable levels

Rachman Chavik 14 年之前
父节点
当前提交
8e8763d69d

+ 1 - 1
app/Config/bootstrap.php

@@ -163,6 +163,6 @@ CakeLog::config('debug', array(
 ));
 CakeLog::config('error', array(
 	'engine' => 'FileLog',
-	'types' => array('error', 'warning'),
+	'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
 	'file' => 'error',
 ));

+ 1 - 1
app/Config/core.php

@@ -133,7 +133,7 @@
  * Defines the default error type when using the log() function. Used for
  * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
  */
-	define('LOG_ERROR', 2);
+	define('LOG_ERROR', LOG_ERR);
 
 /**
  * Session configuration.

+ 1 - 1
lib/Cake/Console/Templates/skel/Config/bootstrap.php

@@ -103,6 +103,6 @@ CakeLog::config('debug', array(
 ));
 CakeLog::config('error', array(
 	'engine' => 'FileLog',
-	'scopes' => array('error', 'warning'),
+	'scopes' => array('warning', 'error', 'critical', 'alert', 'emergency'),
 	'file' => 'error',
 ));

+ 1 - 1
lib/Cake/Console/Templates/skel/Config/core.php

@@ -133,7 +133,7 @@
  * Defines the default error type when using the log() function. Used for
  * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
  */
-	define('LOG_ERROR', 2);
+	define('LOG_ERROR', LOG_ERR);
 
 /**
  * Session configuration.

+ 168 - 32
lib/Cake/Log/CakeLog.php

@@ -24,24 +24,33 @@
  * system.
  *
  */
-if (!defined('LOG_ERROR')) {
-	define('LOG_ERROR', 2);
+if (!defined('LOG_EMERG')) {
+	define('LOG_EMERG', 0);
+}
+if (!defined('LOG_ALERT')) {
+	define('LOG_ALERT', 1);
+}
+if (!defined('LOG_CRIT')) {
+	define('LOG_CRIT', 2);
 }
 if (!defined('LOG_ERR')) {
-	define('LOG_ERR', LOG_ERROR);
+	define('LOG_ERR', 3);
+}
+if (!defined('LOG_ERROR')) {
+	define('LOG_ERROR', LOG_ERR);
 }
 if (!defined('LOG_WARNING')) {
-	define('LOG_WARNING', 3);
+	define('LOG_WARNING', 4);
 }
 if (!defined('LOG_NOTICE')) {
-	define('LOG_NOTICE', 4);
-}
-if (!defined('LOG_DEBUG')) {
-	define('LOG_DEBUG', 5);
+	define('LOG_NOTICE', 5);
 }
 if (!defined('LOG_INFO')) {
 	define('LOG_INFO', 6);
 }
+if (!defined('LOG_DEBUG')) {
+	define('LOG_DEBUG', 7);
+}
 
 App::uses('LogEngineCollection', 'Log');
 
@@ -75,11 +84,37 @@ class CakeLog {
 	protected static $_Collection;
 
 /**
+ * Default log levels as detailed in RFC 5424
+ * http://tools.ietf.org/html/rfc5424
+ */
+	protected static $_defaultLevels = array(
+		LOG_EMERG => 'emergency',
+		LOG_ALERT => 'alert',
+		LOG_CRIT => 'critical',
+		LOG_ERR => 'error',
+		LOG_WARNING => 'warning',
+		LOG_NOTICE => 'notice',
+		LOG_INFO => 'info',
+		LOG_DEBUG => 'debug',
+	);
+
+/**
+ * Active log levels for this instance.
+ */
+	protected static $_levels;
+
+/**
+ * Mapped log levels
+ */
+	protected static $_levelMap;
+
+/**
  * initialize ObjectCollection
  *
  * @return void
  */
 	protected static function _init() {
+		self::$_levels = self::defaultLevels();
 		self::$_Collection = new LogEngineCollection();
 	}
 
@@ -132,6 +167,70 @@ class CakeLog {
 	}
 
 /**
+ * Gets/sets log levels
+ *
+ * Call this method without arguments, eg: `CakeLog::levels()` to obtain current
+ * level configuration.
+ *
+ * To append additional level 'user0' and 'user1' to to default log levels:
+ *
+ *     `CakeLog::levels(array('user0, 'user1'))` or
+ *     `CakeLog::levels(array('user0, 'user1'), true)`
+ *
+ * will result in:
+ *
+ * array(
+ *     0 => 'emergency',
+ *     1 => 'alert',
+ *     ...
+ *     8 => 'user0',
+ *     9 => 'user1',
+ * );
+ *
+ * To set/replace existing configuration, pass an array with the second argument
+ * set to false.
+ *
+ *     `CakeLog::levels(array('user0, 'user1'), false);
+ *
+ * will result in:
+ * array(
+ *      0 => 'user0',
+ *      1 => 'user1',
+ * );
+ *
+ * @param mixed $levels array
+ * @param bool $append true to append, false to replace
+ * @return array active log levels
+ */
+	public static function levels($levels = array(), $append = true) {
+		if (empty(self::$_Collection)) {
+			self::_init();
+		}
+		if (empty($levels)) {
+			return self::$_levels;
+		}
+		$levels = array_values($levels);
+		if ($append) {
+			self::$_levels = array_merge(self::$_levels, $levels);
+		} else {
+			self::$_levels = $levels;
+		}
+		self::$_levelMap = array_flip(self::$_levels);
+		return self::$_levels;
+	}
+
+/**
+ * Reset log levels to the original value
+ *
+ * @return array default log levels
+ */
+	public static function defaultLevels() {
+		self::$_levels = self::$_defaultLevels;
+		self::$_levelMap = array_flip(self::$_levels);
+		return self::$_levels;
+	}
+
+/**
  * Removes a stream from the active streams.  Once a stream has been removed
  * it will no longer have messages sent to it.
  *
@@ -221,7 +320,7 @@ class CakeLog {
 	protected static function _autoConfig() {
 		self::$_Collection->load('error', array(
 			'engine' => 'FileLog',
-			'types' => array('error', 'warning'),
+			'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
 			'path' => LOGS,
 		));
 	}
@@ -233,12 +332,14 @@ class CakeLog {
  *
  * ### Types:
  *
+ * -  LOG_EMERG => 'emergency',
+ * -  LOG_ALERT => 'alert',
+ * -  LOG_CRIT => 'critical',
+ * - `LOG_ERR` => 'error',
  * - `LOG_WARNING` => 'warning',
  * - `LOG_NOTICE` => 'notice',
  * - `LOG_INFO` => 'info',
  * - `LOG_DEBUG` => 'debug',
- * - `LOG_ERR` => 'error',
- * - `LOG_ERROR` => 'error'
  *
  * ### Usage:
  *
@@ -257,19 +358,11 @@ class CakeLog {
 		if (empty(self::$_Collection)) {
 			self::_init();
 		}
-		$levels = array(
-			LOG_ERROR => 'error',
-			LOG_ERR => 'error',
-			LOG_WARNING => 'warning',
-			LOG_NOTICE => 'notice',
-			LOG_DEBUG => 'debug',
-			LOG_INFO => 'info',
-		);
 
-		if (is_int($type) && isset($levels[$type])) {
-			$type = $levels[$type];
+		if (is_int($type) && isset(self::$_levels[$type])) {
+			$type = self::$_levels[$type];
 		}
-		if (is_string($type) && empty($scope) && !in_array($type, $levels)) {
+		if (is_string($type) && empty($scope) && !in_array($type, self::$_levels)) {
 			$scope = $type;
 		}
 		if (!self::$_Collection->attached()) {
@@ -298,48 +391,91 @@ class CakeLog {
 	}
 
 /**
+ * Convenience method to log emergency messages
+ *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
+ * @return boolean Success
+ */
+	public static function emergency($message, $scope = array()) {
+		return self::write(self::$_levelMap['emergency'], $message, $scope);
+	}
+
+/**
+ * Convenience method to log alert messages
+ *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
+ * @return boolean Success
+ */
+	public static function alert($message, $scope = array()) {
+		return self::write(self::$_levelMap['alert'], $message, $scope);
+	}
+
+/**
+ * Convenience method to log critical messages
+ *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
+ * @return boolean Success
+ */
+	public static function critical($message, $scope = array()) {
+		return self::write(self::$_levelMap['critical'], $message, $scope);
+	}
+
+/**
  * Convenience method to log error messages
  *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
  * @return boolean Success
  */
-	public static function error($message) {
-		return self::write(LOG_ERROR, $message);
+	public static function error($message, $scope = array()) {
+		return self::write(self::$_levelMap['error'], $message, $scope);
 	}
 
 /**
  * Convenience method to log warning messages
  *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
  * @return boolean Success
  */
-	public static function warning($message) {
-		return self::write(LOG_WARNING, $message);
+	public static function warning($message, $scope = array()) {
+		return self::write(self::$_levelMap['warning'], $message, $scope);
 	}
 
 /**
  * Convenience method to log notice messages
  *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
  * @return boolean Success
  */
-	public static function notice($message) {
-		return self::write(LOG_NOTICE, $message);
+	public static function notice($message, $scope = array()) {
+		return self::write(self::$_levelMap['notice'], $message, $scope);
 	}
 
 /**
  * Convenience method to log debug messages
  *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
  * @return boolean Success
  */
-	public static function debug($message) {
-		return self::write(LOG_DEBUG, $message);
+	public static function debug($message, $scope = array()) {
+		return self::write(self::$_levelMap['debug'], $message, $scope);
 	}
 
 /**
  * Convenience method to log info messages
  *
+ * @param string $message log message
+ * @param mixed $scope string or array of scopes
  * @return boolean Success
  */
-	public static function info($message) {
-		return self::write(LOG_INFO, $message);
+	public static function info($message, $scope = array()) {
+		return self::write(self::$_levelMap['info'], $message, $scope);
 	}
 
 }

+ 155 - 11
lib/Cake/Test/Case/Log/CakeLogTest.php

@@ -289,7 +289,7 @@ class CakeLogTest extends CakeTestCase {
 		));
 		CakeLog::config('error', array(
 			'engine' => 'FileLog',
-			'types' => array('error', 'warning'),
+			'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
 			'file' => 'error',
 		));
 	}
@@ -431,6 +431,52 @@ class CakeLogTest extends CakeTestCase {
 	}
 
 /**
+ * test scoped logging with convenience methods
+ */
+	public function testConvenienceScopedLogging() {
+		if (file_exists(LOGS . 'shops.log')) {
+			unlink(LOGS . 'shops.log');
+		}
+		if (file_exists(LOGS . 'error.log')) {
+			unlink(LOGS . 'error.log');
+		}
+		if (file_exists(LOGS . 'debug.log')) {
+			unlink(LOGS . 'debug.log');
+		}
+
+		$this->_resetLogConfig();
+		CakeLog::config('shops', array(
+			'engine' => 'FileLog',
+			'types' => array('info', 'notice', 'warning'),
+			'scopes' => array('transactions', 'orders'),
+			'file' => 'shops',
+			));
+
+		CakeLog::info('info message', 'transactions');
+		$this->assertFalse(file_exists(LOGS . 'error.log'));
+		$this->assertTrue(file_exists(LOGS . 'shops.log'));
+		$this->assertTrue(file_exists(LOGS . 'debug.log'));
+
+		$this->_deleteLogs();
+
+		CakeLog::error('error message', 'orders');
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+		$this->assertFalse(file_exists(LOGS . 'debug.log'));
+		$this->assertFalse(file_exists(LOGS . 'shops.log'));
+
+		$this->_deleteLogs();
+
+		CakeLog::warning('warning message', 'orders');
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+		$this->assertTrue(file_exists(LOGS . 'shops.log'));
+		$this->assertFalse(file_exists(LOGS . 'debug.log'));
+
+		$this->_deleteLogs();
+
+		CakeLog::drop('shops');
+	}
+
+/**
  * test convenience methods
  */
 	public function testConvenienceMethods() {
@@ -443,44 +489,142 @@ class CakeLogTest extends CakeTestCase {
 		));
 		CakeLog::config('error', array(
 			'engine' => 'FileLog',
-			'types' => array('error', 'warning'),
+			'types' => array('emergency', 'alert', 'critical', 'error', 'warning'),
 			'file' => 'error',
 		));
 
+		$testMessage = 'emergency message';
+		CakeLog::emergency($testMessage);
+		$contents = file_get_contents(LOGS . 'error.log');
+		$this->assertContains('Emergency: ' . $testMessage, $contents);
+		$this->assertFalse(file_exists(LOGS . 'debug.log'));
+		$this->_deleteLogs();
+
+		$testMessage = 'alert message';
+		CakeLog::alert($testMessage);
+		$contents = file_get_contents(LOGS . 'error.log');
+		$this->assertContains('Alert: ' . $testMessage, $contents);
+		$this->assertFalse(file_exists(LOGS . 'debug.log'));
+		$this->_deleteLogs();
+
+		$testMessage = 'critical message';
+		CakeLog::critical($testMessage);
+		$contents = file_get_contents(LOGS . 'error.log');
+		$this->assertContains('Critical: ' . $testMessage, $contents);
+		$this->assertFalse(file_exists(LOGS . 'debug.log'));
+		$this->_deleteLogs();
+
 		$testMessage = 'error message';
 		CakeLog::error($testMessage);
 		$contents = file_get_contents(LOGS . 'error.log');
-		$this->assertContains($testMessage, $contents);
+		$this->assertContains('Error: ' . $testMessage, $contents);
 		$this->assertFalse(file_exists(LOGS . 'debug.log'));
 		$this->_deleteLogs();
 
 		$testMessage = 'warning message';
 		CakeLog::warning($testMessage);
 		$contents = file_get_contents(LOGS . 'error.log');
-		$this->assertContains($testMessage, $contents);
+		$this->assertContains('Warning: ' . $testMessage, $contents);
 		$this->assertFalse(file_exists(LOGS . 'debug.log'));
 		$this->_deleteLogs();
 
+		$testMessage = 'notice message';
+		CakeLog::notice($testMessage);
+		$contents = file_get_contents(LOGS . 'debug.log');
+		$this->assertContains('Notice: ' . $testMessage, $contents);
+		$this->assertFalse(file_exists(LOGS . 'error.log'));
+		$this->_deleteLogs();
+
 		$testMessage = 'info message';
 		CakeLog::info($testMessage);
 		$contents = file_get_contents(LOGS . 'debug.log');
-		$this->assertContains($testMessage, $contents);
+		$this->assertContains('Info: ' . $testMessage, $contents);
 		$this->assertFalse(file_exists(LOGS . 'error.log'));
 		$this->_deleteLogs();
 
 		$testMessage = 'debug message';
 		CakeLog::debug($testMessage);
 		$contents = file_get_contents(LOGS . 'debug.log');
-		$this->assertContains($testMessage, $contents);
+		$this->assertContains('Debug: ' . $testMessage, $contents);
 		$this->assertFalse(file_exists(LOGS . 'error.log'));
 		$this->_deleteLogs();
+	}
 
-		$testMessage = 'notice message';
-		CakeLog::notice($testMessage);
-		$contents = file_get_contents(LOGS . 'debug.log');
-		$this->assertContains($testMessage, $contents);
-		$this->assertFalse(file_exists(LOGS . 'error.log'));
+/**
+ * test levels customization
+ */
+	public function testLevelCustomization() {
+		$this->skipIf(DIRECTORY_SEPARATOR === '\\', 'Log level tests not supported on Windows.');
+
+		$levels = CakeLog::defaultLevels();
+		$this->assertNotEmpty($levels);
+		$result = array_keys($levels);
+		$this->assertEquals(array(0, 1, 2, 3, 4, 5, 6, 7), $result);
+
+		$levels = CakeLog::levels(array('foo', 'bar'));
+		CakeLog::defaultLevels();
+		$this->assertEquals('foo', $levels[8]);
+		$this->assertEquals('bar', $levels[9]);
+
+		$levels = CakeLog::levels(array(11 => 'spam', 'bar' => 'eggs'));
+		CakeLog::defaultLevels();
+		$this->assertEquals('spam', $levels[8]);
+		$this->assertEquals('eggs', $levels[9]);
+
+		$levels = CakeLog::levels(array(11 => 'spam', 'bar' => 'eggs'), false);
+		CakeLog::defaultLevels();
+		$this->assertEquals(array('spam', 'eggs'), $levels);
+
+		$levels = CakeLog::levels(array('ham', 9 => 'spam', '12' => 'fam'), false);
+		CakeLog::defaultLevels();
+		$this->assertEquals(array('ham', 'spam', 'fam'), $levels);
+	}
+
+/**
+ * Test writing log files with custom levels
+ */
+	public function testCustomLevelWrites() {
 		$this->_deleteLogs();
+		$this->_resetLogConfig();
+
+		$levels = CakeLog::levels(array('spam', 'eggs'));
+
+		$testMessage = 'error message';
+		CakeLog::write('error', $testMessage);
+		CakeLog::defaultLevels();
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+		$contents = file_get_contents(LOGS . 'error.log');
+		$this->assertContains('Error: ' . $testMessage, $contents);
+
+		CakeLog::config('spam', array(
+			'engine' => 'FileLog',
+			'file' => 'spam.log',
+			'types' => 'spam',
+			));
+		CakeLog::config('eggs', array(
+			'engine' => 'FileLog',
+			'file' => 'eggs.log',
+			'types' => array('spam', 'eggs'),
+			));
+
+		$testMessage = 'spam message';
+		CakeLog::write('spam', $testMessage);
+		CakeLog::defaultLevels();
+		$this->assertTrue(file_exists(LOGS . 'spam.log'));
+		$this->assertTrue(file_exists(LOGS . 'eggs.log'));
+		$contents = file_get_contents(LOGS . 'spam.log');
+		$this->assertContains('Spam: ' . $testMessage, $contents);
+
+		$testMessage = 'egg message';
+		CakeLog::write('eggs', $testMessage);
+		CakeLog::defaultLevels();
+		$contents = file_get_contents(LOGS . 'spam.log');
+		$this->assertNotContains('Eggs: ' . $testMessage, $contents);
+		$contents = file_get_contents(LOGS . 'eggs.log');
+		$this->assertContains('Eggs: ' . $testMessage, $contents);
+
+		CakeLog::drop('spam');
+		CakeLog::drop('eggs');
 	}
 
 }